Skip to content

Commit

Permalink
deploy hotfixes
Browse files Browse the repository at this point in the history
  • Loading branch information
trisongz committed Apr 24, 2024
1 parent 8e52bed commit 20b7b48
Show file tree
Hide file tree
Showing 10 changed files with 146 additions and 23 deletions.
6 changes: 4 additions & 2 deletions lazyops/libs/abcs/cli/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ def error(self, message):
def parse_one(
name: str,
field: FieldInfo,
) -> Tuple[Set, Dict]:
) -> Tuple[Set, Dict]: # sourcery skip: low-code-quality
"""
Parse a single field into the args and kwargs for the add_argument method
"""
Expand All @@ -80,7 +80,9 @@ def parse_one(
if field.is_required():
args.add(name)
else:
args.add(f'--{name}')
if not field.json_schema_extra or not field.json_schema_extra.get('exclude_name', False):
args.add(f'--{name}')

if field.json_schema_extra and field.json_schema_extra.get('alt'):
alt = field.json_schema_extra['alt']
if isinstance(alt, str): alt = [alt]
Expand Down
17 changes: 14 additions & 3 deletions lazyops/libs/abcs/clients/http.py
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,8 @@ class BaseAPIClient(BaseGlobalClient):
name: Optional[str] = 'api'
verbose_errors: Optional[bool] = True
http_timeout: Optional[float] = None
http_errors_verbose: Optional[bool] = True
http_errors_truncate_length: Optional[int] = 1000

_endpoint: Optional[str] = None
_headers: Optional[Dict[str, Any]] = None
Expand Down Expand Up @@ -509,12 +511,21 @@ def handle_response(
else:
raise ValueError(f'Invalid return type: {return_type}')
except aiohttpx.HTTPStatusError as e:
if self.settings.is_production_env:
if not self.http_errors_verbose or self.settings.is_production_env:
self.logger.warning(f'[{response.status_code} - {e.request.url}]')
else:
self.logger.error(f'[{response.status_code} - {e.request.url}] {response.text}')
response_text = response.text
if self.http_errors_truncate_length:
response_text = response_text[:self.http_errors_truncate_length]
self.logger.error(f'[{response.status_code} - {e.request.url}] {response_text}')
except Exception as e:
self.logger.trace(f'Error in response: {response.text}', e)
if not self.http_errors_verbose:
self.logger.error(f'[{type(e)}] {str(e)}')
else:
response_text = response.text
if self.http_errors_truncate_length:
response_text = response_text[:self.http_errors_truncate_length]
self.logger.trace(f'Error in response: {response_text}', e)
raise e


Expand Down
42 changes: 40 additions & 2 deletions lazyops/libs/abcs/utils/helpers.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
from __future__ import annotations

import collections.abc
from typing import Dict, Optional
from typing import Dict, Optional, Any


def update_dict(d: Dict, u: Dict, exclude_none: Optional[bool] = False) -> Dict:
Expand Down Expand Up @@ -53,4 +53,42 @@ def flatten_dict_value(
items.extend(flatten_dict_value(v, new_key).items())
else:
items.append((new_key, v))
return dict(items)
return dict(items)


# https://stackoverflow.com/questions/27265939/comparing-python-dictionaries-and-nested-dictionaries



def diff_dict(d1: Dict[str, Any], d2: Dict[str, Any]) -> Dict[str, Any]:
"""
Returns the difference between two dictionaries
d1 should be the original dict
d2 should be the new/updated dict
Returns a dict with the differences between d1 and d2
"""
d1_keys = set(d1.keys())
d2_keys = set(d2.keys())
shared_keys = d1_keys.intersection(d2_keys)
shared_deltas = {o: (d1[o], d2[o]) for o in shared_keys if d1[o] != d2[o]}
added_keys = d2_keys - d1_keys
added_deltas = {o: (None, d2[o]) for o in added_keys}
deltas = {**shared_deltas, **added_deltas}
return parse_deltas(deltas)


def parse_deltas(deltas: dict) -> dict:
"""
Parses the deltas
"""
res = {}
for k, v in deltas.items():
if isinstance(v[0], dict):
tmp = diff_dict(v[0], v[1])
if tmp:
res[k] = tmp
else:
res[k] = v[1]
return res
9 changes: 6 additions & 3 deletions lazyops/libs/authzero/clients/oauth.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
from ..utils.helpers import get_hashed_key, create_code_challenge, parse_scopes, encode_params_to_url


from typing import Optional, List, Dict, Any, Union, Type
from typing import Optional, List, Dict, Any, Union, Type, Tuple

if lazyload.TYPE_CHECKING:
import niquests
Expand Down Expand Up @@ -239,7 +239,7 @@ def create_docs_index(self, schema: Dict[str, Any]):
self.docs_schema_index[doc_name] = schema['paths'][path][method]['operationId']


def create_openapi_source_spec(self, spec: Dict[str, Any], spec_map: Optional[Dict[str, Any]] = None):
def create_openapi_source_spec(self, spec: Dict[str, Any], spec_map: Optional[Dict[str, Union[str, Tuple[str, int]]]] = None,):
"""
Creates the Source Spec
Expand All @@ -251,7 +251,10 @@ def create_openapi_source_spec(self, spec: Dict[str, Any], spec_map: Optional[Di
if not spec_map: return
_spec = json.dumps(spec)
for key, value in spec_map.items():
_spec = _spec.replace(key, value)
if isinstance(value, tuple):
_spec = _spec.replace(key, value[0], value[1])
else:
_spec = _spec.replace(key, value)
self.source_openapi_schema = json.loads(_spec)


Expand Down
8 changes: 4 additions & 4 deletions lazyops/libs/authzero/types/current_user.py
Original file line number Diff line number Diff line change
Expand Up @@ -509,9 +509,9 @@ async def validate_authorization(
if self.role is None: self.role = UserRole.SERVICE
self.user_type = 'service'
if self.role is None:
if self.user_data and self.user_data.user_metadata.get('role'):
if self.user_data and self.user_data.user_metadata and self.user_data.user_metadata.get('role'):
self.role = UserRole.parse_role(self.user_data.user_metadata['role'])
elif self.user_data and self.user_data.app_metadata.get('role'):
elif self.user_data and self.user_data.app_metadata and self.user_data.app_metadata.get('role'):
self.role = UserRole.parse_role(self.user_data.app_metadata['role'])
else:
self.role = UserRole.USER
Expand Down Expand Up @@ -656,9 +656,9 @@ async def validate_user_api_key(self, request: Optional[Request], scopes: Option
self.validate_permissions() if scopes is None else self.validate_scopes(scopes)
if self.validation_method is None: self.validation_method = 'api_key'
if self.role is None:
if self.user_data and self.user_data.user_metadata.get('role'):
if self.user_data and self.user_data.user_metadata and self.user_data.user_metadata.get('role'):
self.role = UserRole.parse_role(self.user_data.user_metadata['role'])
elif self.user_data and self.user_data.app_metadata.get('role'):
elif self.user_data and self.user_data.app_metadata and self.user_data.app_metadata.get('role'):
self.role = UserRole.parse_role(self.user_data.app_metadata['role'])
elif '@clients' in self.claims.sub:
self.role = UserRole.SERVICE
Expand Down
37 changes: 36 additions & 1 deletion lazyops/libs/fastapi_utils/openapi/spec.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,14 +86,36 @@ def patch_openapi_schema(
replace_key_start: Optional[str] = KEY_START,
replace_key_end: Optional[str] = KEY_END,
replace_sep_char: Optional[str] = KEY_SEP,
openapi_spec_map: Optional[Dict[str, Union[str, Tuple[str, int]]]] = None,

# Extra Schemas
enable_description_path_patch: Optional[bool] = None,
**kwargs
) -> Dict[str, Any]:
) -> Dict[str, Any]: # sourcery skip: low-code-quality
"""
Patch the openapi schema
Operations:
1. If `openapi_spec_map` is provided, it will be used to replace the values in the openapi schema
This operation uses JSON to repalce the string values by dumping/loading
2. If `schemas_patches` is provided, it will be used to patch the openapi schema
3. If `replace_patches` is provided, it will be used to replace the values in the openapi schema
4. If `enable_description_path_patch` is provided, it will be used to patch the openapi schema
This operation replaces any `CURRENT_OPENAPI_PATH` with the current path
"""
global _openapi_schemas
if module_name not in _openapi_schemas or overwrite:
if verbose: logger.info(f"[|g|{module_name}|e|] Patching OpenAPI Schema", colored = True)
if openapi_spec_map:
_spec = json.dumps(openapi_schema)
for key, value in openapi_spec_map.items():
if isinstance(value, tuple):
_spec = _spec.replace(key, value[0], value[1])
else:
_spec = _spec.replace(key, value)
openapi_schema = json.loads(_spec)

for path, patch in schemas_patches.items():
with contextlib.suppress(Exception):
patch_source = openapi_schema['components']['schemas'].pop(patch['source'], None)
Expand All @@ -105,6 +127,15 @@ def patch_openapi_schema(
# Handle Paths
openapi_schema['paths'][path]['post']['requestBody']['content']['multipart/form-data']['schema']['$ref'] = f'#/components/schemas/{patch["schema"]}'

if enable_description_path_patch:
with contextlib.suppress(Exception):
for path in openapi_schema['paths']:
for method in openapi_schema['paths'][path]:
if 'description' in openapi_schema['paths'][path][method] and 'CURRENT_OPENAPI_PATH' in openapi_schema['paths'][path][method]['description']:
# logger.info(f"Patching Description Path: {path}", prefix = '|g|OpenAPI|e|', colored = True)
openapi_schema['paths'][path][method]['description'] = openapi_schema['paths'][path][method]['description'].replace('CURRENT_OPENAPI_PATH', path)


if not excluded_schemas: excluded_schemas = []
for exclude in excluded_schemas:
_ = openapi_schema['components']['schemas'].pop(exclude, None)
Expand Down Expand Up @@ -139,6 +170,8 @@ def create_openapi_schema_patch(
replace_key_end: Optional[str] = KEY_END,
replace_sep_char: Optional[str] = KEY_SEP,
replace_domain_key: Optional[str] = DOMAIN_KEY,
openapi_spec_map: Optional[Dict[str, Union[str, Tuple[str, int]]]] = None,
enable_description_path_patch: Optional[bool] = None,

) -> Callable[[Dict[str, Any]], Dict[str, Any]]:
"""
Expand All @@ -164,6 +197,8 @@ def patch_openapi_schema_wrapper(
replace_key_start = replace_key_start,
replace_key_end = replace_key_end,
replace_sep_char = replace_sep_char,
openapi_spec_map = openapi_spec_map,
enable_description_path_patch = enable_description_path_patch,
)
return patch_openapi_schema_wrapper

Expand Down
9 changes: 5 additions & 4 deletions lazyops/libs/persistence/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -419,19 +419,20 @@ async def avalues(self, **kwargs) -> Iterable[VT]:
return await self.base.avalues(**kwargs)

@overload
async def aitems(self, iterable: Optional[bool] = True, **kwargs) -> Iterable[Tuple[KT, VT]]:
async def aitems(self, iterable: Optional[bool] = False, **kwargs) -> Dict[KT, VT]:
"""
Returns the Items
"""
...



@overload
async def aitems(self, iterable: Optional[bool] = False, **kwargs) -> Dict[KT, VT]:
async def aitems(self, **kwargs) -> Iterable[Tuple[KT, VT]]:
"""
Returns the Items
"""
...


async def aitems(self, iterable: Optional[bool] = True, **kwargs) -> Dict[KT, VT]:
"""
Expand Down
23 changes: 21 additions & 2 deletions lazyops/libs/slack/types.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
from __future__ import annotations

import datetime
from pydantic import model_validator
from lazyops.types import BaseModel, Field
from typing import Optional, Dict, Any, Union, List
Expand All @@ -23,6 +24,14 @@ def lookup_id(self, name: str) -> Optional[str]:
Lookup ID
"""
return self.uids.get(name, self.users.get(name, self.channels.get(name)))


def create_slack_timestamp() -> int:
"""
Creates a Slack Timestamp
"""
return int(datetime.datetime.now().timestamp())


class SlackPayload(BaseModel):
token: Optional[str] = None
Expand All @@ -42,6 +51,8 @@ class SlackPayload(BaseModel):

ccommand: Optional[str] = None # A mutable copy of the command
ctext: Optional[str] = None # A mutable copy of the text

timestamp: Optional[int] = Field(default_factory = create_slack_timestamp)

@model_validator(mode = 'after')
def set_mutable_context(self):
Expand All @@ -51,5 +62,13 @@ def set_mutable_context(self):
if self.text is not None: self.ctext = self.text
if self.command is not None: self.ccommand = self.command
return self



def from_repeat(self, payload: SlackPayload) -> 'SlackPayload':
"""
Sets the existing payload from a previous payload
"""
self.text = payload.text
self.command = payload.command
self.ctext = payload.ctext
self.ccommand = payload.ccommand
return self
16 changes: 15 additions & 1 deletion lazyops/utils/times.py
Original file line number Diff line number Diff line change
Expand Up @@ -283,7 +283,7 @@ def total_average_s(self, count: int) -> str:
"""
Returns the average duration of the timer as a string
"""
return self.pformat(self.total_average(count))
return self.pformat(self.total_average(count or 1))

def total_average_iter(self, count: int) -> float:
"""
Expand Down Expand Up @@ -397,6 +397,20 @@ def __getitem__(self, index: int) -> float:
Returns the duration at the given index
"""
return self.durations[index] if self.durations else self.elapsed

def __iadd__(self, other: Union[float, int]) -> 'Timer':
"""
Adds the other to the values
"""
self.durations.append(other)
return self

def __isub__(self, other: Union[float, int]) -> 'Timer':
"""
Subtracts the other from the values
"""
self.durations.append(-other)
return self

@property
def data_dict(self) -> Dict[str, Any]:
Expand Down
2 changes: 1 addition & 1 deletion lazyops/version.py
Original file line number Diff line number Diff line change
@@ -1 +1 @@
VERSION = '0.2.81'
VERSION = '0.2.82'

0 comments on commit 20b7b48

Please sign in to comment.