ul-api-utils 9.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- example/__init__.py +0 -0
- example/conf.py +35 -0
- example/main.py +24 -0
- example/models/__init__.py +0 -0
- example/permissions.py +6 -0
- example/pure_flask_example.py +65 -0
- example/rate_limit_load.py +10 -0
- example/redis_repository.py +22 -0
- example/routes/__init__.py +0 -0
- example/routes/api_some.py +335 -0
- example/sockets/__init__.py +0 -0
- example/sockets/on_connect.py +16 -0
- example/sockets/on_disconnect.py +14 -0
- example/sockets/on_json.py +10 -0
- example/sockets/on_message.py +13 -0
- example/sockets/on_open.py +16 -0
- example/workers/__init__.py +0 -0
- example/workers/worker.py +28 -0
- ul_api_utils/__init__.py +0 -0
- ul_api_utils/access/__init__.py +122 -0
- ul_api_utils/api_resource/__init__.py +0 -0
- ul_api_utils/api_resource/api_request.py +105 -0
- ul_api_utils/api_resource/api_resource.py +414 -0
- ul_api_utils/api_resource/api_resource_config.py +20 -0
- ul_api_utils/api_resource/api_resource_error_handling.py +21 -0
- ul_api_utils/api_resource/api_resource_fn_typing.py +356 -0
- ul_api_utils/api_resource/api_resource_type.py +16 -0
- ul_api_utils/api_resource/api_response.py +300 -0
- ul_api_utils/api_resource/api_response_db.py +26 -0
- ul_api_utils/api_resource/api_response_payload_alias.py +25 -0
- ul_api_utils/api_resource/db_types.py +9 -0
- ul_api_utils/api_resource/signature_check.py +41 -0
- ul_api_utils/commands/__init__.py +0 -0
- ul_api_utils/commands/cmd_enc_keys.py +172 -0
- ul_api_utils/commands/cmd_gen_api_user_token.py +77 -0
- ul_api_utils/commands/cmd_gen_new_api_user.py +106 -0
- ul_api_utils/commands/cmd_generate_api_docs.py +181 -0
- ul_api_utils/commands/cmd_start.py +110 -0
- ul_api_utils/commands/cmd_worker_start.py +76 -0
- ul_api_utils/commands/start/__init__.py +0 -0
- ul_api_utils/commands/start/gunicorn.conf.local.py +0 -0
- ul_api_utils/commands/start/gunicorn.conf.py +26 -0
- ul_api_utils/commands/start/wsgi.py +22 -0
- ul_api_utils/conf/ul-debugger-main.js +1 -0
- ul_api_utils/conf/ul-debugger-ui.js +1 -0
- ul_api_utils/conf.py +70 -0
- ul_api_utils/const.py +78 -0
- ul_api_utils/debug/__init__.py +0 -0
- ul_api_utils/debug/debugger.py +119 -0
- ul_api_utils/debug/malloc.py +93 -0
- ul_api_utils/debug/stat.py +444 -0
- ul_api_utils/encrypt/__init__.py +0 -0
- ul_api_utils/encrypt/encrypt_decrypt_abstract.py +15 -0
- ul_api_utils/encrypt/encrypt_decrypt_aes_xtea.py +59 -0
- ul_api_utils/errors.py +200 -0
- ul_api_utils/internal_api/__init__.py +0 -0
- ul_api_utils/internal_api/__tests__/__init__.py +0 -0
- ul_api_utils/internal_api/__tests__/internal_api.py +29 -0
- ul_api_utils/internal_api/__tests__/internal_api_content_type.py +22 -0
- ul_api_utils/internal_api/internal_api.py +369 -0
- ul_api_utils/internal_api/internal_api_check_context.py +42 -0
- ul_api_utils/internal_api/internal_api_error.py +17 -0
- ul_api_utils/internal_api/internal_api_response.py +296 -0
- ul_api_utils/main.py +29 -0
- ul_api_utils/modules/__init__.py +0 -0
- ul_api_utils/modules/__tests__/__init__.py +0 -0
- ul_api_utils/modules/__tests__/test_api_sdk_jwt.py +195 -0
- ul_api_utils/modules/api_sdk.py +555 -0
- ul_api_utils/modules/api_sdk_config.py +63 -0
- ul_api_utils/modules/api_sdk_jwt.py +377 -0
- ul_api_utils/modules/intermediate_state.py +34 -0
- ul_api_utils/modules/worker_context.py +35 -0
- ul_api_utils/modules/worker_sdk.py +109 -0
- ul_api_utils/modules/worker_sdk_config.py +13 -0
- ul_api_utils/py.typed +0 -0
- ul_api_utils/resources/__init__.py +0 -0
- ul_api_utils/resources/caching.py +196 -0
- ul_api_utils/resources/debugger_scripts.py +97 -0
- ul_api_utils/resources/health_check/__init__.py +0 -0
- ul_api_utils/resources/health_check/const.py +2 -0
- ul_api_utils/resources/health_check/health_check.py +439 -0
- ul_api_utils/resources/health_check/health_check_template.py +64 -0
- ul_api_utils/resources/health_check/resource.py +97 -0
- ul_api_utils/resources/not_implemented.py +25 -0
- ul_api_utils/resources/permissions.py +29 -0
- ul_api_utils/resources/rate_limitter.py +84 -0
- ul_api_utils/resources/socketio.py +55 -0
- ul_api_utils/resources/swagger.py +119 -0
- ul_api_utils/resources/web_forms/__init__.py +0 -0
- ul_api_utils/resources/web_forms/custom_fields/__init__.py +0 -0
- ul_api_utils/resources/web_forms/custom_fields/custom_checkbox_select.py +5 -0
- ul_api_utils/resources/web_forms/custom_widgets/__init__.py +0 -0
- ul_api_utils/resources/web_forms/custom_widgets/custom_select_widget.py +86 -0
- ul_api_utils/resources/web_forms/custom_widgets/custom_text_input_widget.py +42 -0
- ul_api_utils/resources/web_forms/uni_form.py +75 -0
- ul_api_utils/sentry.py +52 -0
- ul_api_utils/utils/__init__.py +0 -0
- ul_api_utils/utils/__tests__/__init__.py +0 -0
- ul_api_utils/utils/__tests__/api_path_version.py +16 -0
- ul_api_utils/utils/__tests__/unwrap_typing.py +67 -0
- ul_api_utils/utils/api_encoding.py +51 -0
- ul_api_utils/utils/api_format.py +61 -0
- ul_api_utils/utils/api_method.py +55 -0
- ul_api_utils/utils/api_pagination.py +58 -0
- ul_api_utils/utils/api_path_version.py +60 -0
- ul_api_utils/utils/api_request_info.py +6 -0
- ul_api_utils/utils/avro.py +131 -0
- ul_api_utils/utils/broker_topics_message_count.py +47 -0
- ul_api_utils/utils/cached_per_request.py +23 -0
- ul_api_utils/utils/colors.py +31 -0
- ul_api_utils/utils/constants.py +7 -0
- ul_api_utils/utils/decode_base64.py +9 -0
- ul_api_utils/utils/deprecated.py +19 -0
- ul_api_utils/utils/flags.py +29 -0
- ul_api_utils/utils/flask_swagger_generator/__init__.py +0 -0
- ul_api_utils/utils/flask_swagger_generator/conf.py +4 -0
- ul_api_utils/utils/flask_swagger_generator/exceptions.py +7 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/__init__.py +0 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_models.py +57 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_specifier.py +48 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_three_specifier.py +777 -0
- ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_version.py +40 -0
- ul_api_utils/utils/flask_swagger_generator/utils/__init__.py +0 -0
- ul_api_utils/utils/flask_swagger_generator/utils/input_type.py +77 -0
- ul_api_utils/utils/flask_swagger_generator/utils/parameter_type.py +51 -0
- ul_api_utils/utils/flask_swagger_generator/utils/replace_in_dict.py +18 -0
- ul_api_utils/utils/flask_swagger_generator/utils/request_type.py +52 -0
- ul_api_utils/utils/flask_swagger_generator/utils/schema_type.py +15 -0
- ul_api_utils/utils/flask_swagger_generator/utils/security_type.py +39 -0
- ul_api_utils/utils/imports.py +16 -0
- ul_api_utils/utils/instance_checks.py +16 -0
- ul_api_utils/utils/jinja/__init__.py +0 -0
- ul_api_utils/utils/jinja/t_url_for.py +19 -0
- ul_api_utils/utils/jinja/to_pretty_json.py +11 -0
- ul_api_utils/utils/json_encoder.py +126 -0
- ul_api_utils/utils/load_modules.py +15 -0
- ul_api_utils/utils/memory_db/__init__.py +0 -0
- ul_api_utils/utils/memory_db/__tests__/__init__.py +0 -0
- ul_api_utils/utils/memory_db/errors.py +8 -0
- ul_api_utils/utils/memory_db/repository.py +102 -0
- ul_api_utils/utils/token_check.py +14 -0
- ul_api_utils/utils/token_check_through_request.py +16 -0
- ul_api_utils/utils/unwrap_typing.py +117 -0
- ul_api_utils/utils/uuid_converter.py +22 -0
- ul_api_utils/validators/__init__.py +0 -0
- ul_api_utils/validators/__tests__/__init__.py +0 -0
- ul_api_utils/validators/__tests__/test_custom_fields.py +32 -0
- ul_api_utils/validators/custom_fields.py +66 -0
- ul_api_utils/validators/validate_empty_object.py +10 -0
- ul_api_utils/validators/validate_uuid.py +11 -0
- ul_api_utils-9.3.0.dist-info/LICENSE +21 -0
- ul_api_utils-9.3.0.dist-info/METADATA +279 -0
- ul_api_utils-9.3.0.dist-info/RECORD +156 -0
- ul_api_utils-9.3.0.dist-info/WHEEL +5 -0
- ul_api_utils-9.3.0.dist-info/entry_points.txt +2 -0
- ul_api_utils-9.3.0.dist-info/top_level.txt +2 -0
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from ul_api_utils.utils.flask_swagger_generator.exceptions import SwaggerGeneratorError
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SwaggerVersion(Enum):
|
|
6
|
+
"""
|
|
7
|
+
Class SwaggerVersion: Enum for types of swagger version
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
VERSION_THREE = 'VERSION_THREE'
|
|
11
|
+
VERSION_TWO = 'VERSION_TWO'
|
|
12
|
+
|
|
13
|
+
def __repr__(self) -> str:
|
|
14
|
+
return f'{type(self).__name__}.{self.name}'
|
|
15
|
+
|
|
16
|
+
@staticmethod
|
|
17
|
+
def from_string(value: str) -> 'SwaggerVersion':
|
|
18
|
+
if isinstance(value, str):
|
|
19
|
+
if value.lower() in ['three', 'version_three']:
|
|
20
|
+
return SwaggerVersion.VERSION_THREE
|
|
21
|
+
elif value.lower() in ['two', 'version_two']:
|
|
22
|
+
return SwaggerVersion.VERSION_TWO
|
|
23
|
+
else:
|
|
24
|
+
raise SwaggerGeneratorError('Could not convert value {} to a swagger version'.format(value))
|
|
25
|
+
else:
|
|
26
|
+
raise SwaggerGeneratorError("Could not convert non string value to a swagger version")
|
|
27
|
+
|
|
28
|
+
def equals(self, other): # type: ignore
|
|
29
|
+
|
|
30
|
+
if isinstance(other, Enum):
|
|
31
|
+
return self.value == other.value
|
|
32
|
+
else:
|
|
33
|
+
|
|
34
|
+
try:
|
|
35
|
+
data_base_type = SwaggerVersion.from_string(other)
|
|
36
|
+
return data_base_type == self
|
|
37
|
+
except SwaggerGeneratorError:
|
|
38
|
+
pass
|
|
39
|
+
|
|
40
|
+
return other == self.value
|
|
File without changes
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from typing import Optional
|
|
3
|
+
|
|
4
|
+
from ul_api_utils.utils.flask_swagger_generator.exceptions import SwaggerGeneratorError
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class InputType(Enum):
|
|
8
|
+
"""
|
|
9
|
+
Class SwaggerVersion: Enum for types of swagger version
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
INTEGER = 'integer'
|
|
13
|
+
NUMBER = 'number'
|
|
14
|
+
BOOLEAN = 'boolean'
|
|
15
|
+
STRING = 'string'
|
|
16
|
+
ARRAY = 'array'
|
|
17
|
+
OBJECT = 'object'
|
|
18
|
+
NESTED = 'nested'
|
|
19
|
+
DATE_TIME = 'datetime'
|
|
20
|
+
UUID = 'uuid'
|
|
21
|
+
|
|
22
|
+
def __repr__(self) -> str:
|
|
23
|
+
return f'{type(self).__name__}.{self.name}'
|
|
24
|
+
|
|
25
|
+
@staticmethod
|
|
26
|
+
def from_string(value: str) -> 'InputType':
|
|
27
|
+
type_map = {
|
|
28
|
+
"integer": InputType.INTEGER, "int": InputType.INTEGER,
|
|
29
|
+
"number": InputType.NUMBER, "num": InputType.NUMBER,
|
|
30
|
+
"boolean": InputType.BOOLEAN, "bool": InputType.BOOLEAN,
|
|
31
|
+
"string": InputType.STRING, "str": InputType.STRING,
|
|
32
|
+
"array": InputType.ARRAY, "object": InputType.OBJECT,
|
|
33
|
+
"nested": InputType.NESTED, "datetime": InputType.STRING,
|
|
34
|
+
"uuid": InputType.UUID,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if isinstance(value, str):
|
|
38
|
+
route_with_arguments_parenthesis = '('
|
|
39
|
+
if route_with_arguments_parenthesis in value:
|
|
40
|
+
value = value.lower().split(route_with_arguments_parenthesis)[0]
|
|
41
|
+
try:
|
|
42
|
+
return type_map[value.lower()]
|
|
43
|
+
except KeyError:
|
|
44
|
+
raise SwaggerGeneratorError(f'Could not convert {value=} to a input type')
|
|
45
|
+
else:
|
|
46
|
+
raise SwaggerGeneratorError("Could not convert non string value to a parameter type")
|
|
47
|
+
|
|
48
|
+
def equals(self, other): # type: ignore
|
|
49
|
+
|
|
50
|
+
if isinstance(other, Enum):
|
|
51
|
+
return self.value == other.value
|
|
52
|
+
else:
|
|
53
|
+
|
|
54
|
+
try:
|
|
55
|
+
data_base_type = InputType.from_string(other)
|
|
56
|
+
return data_base_type == self
|
|
57
|
+
except SwaggerGeneratorError:
|
|
58
|
+
pass
|
|
59
|
+
|
|
60
|
+
return other == self.value
|
|
61
|
+
|
|
62
|
+
def get_flask_input_type_value(self) -> Optional[str]:
|
|
63
|
+
if self.value.lower() == 'integer':
|
|
64
|
+
return 'int'
|
|
65
|
+
elif self.value.lower() in 'number':
|
|
66
|
+
return 'num'
|
|
67
|
+
elif self.value.lower() in 'boolean':
|
|
68
|
+
return 'bool'
|
|
69
|
+
elif self.value.lower() in 'string':
|
|
70
|
+
return 'string'
|
|
71
|
+
elif self.value.lower() == 'array':
|
|
72
|
+
return 'array'
|
|
73
|
+
elif self.value.lower() == 'object':
|
|
74
|
+
return 'object'
|
|
75
|
+
elif self.value.lower() == 'uuid':
|
|
76
|
+
return 'uuid'
|
|
77
|
+
return None
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from ul_api_utils.utils.flask_swagger_generator.exceptions import SwaggerGeneratorError
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class ParameterType(Enum):
|
|
6
|
+
"""
|
|
7
|
+
Class SwaggerVersion: Enum for types of swagger version
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
PATH = 'PATH'
|
|
11
|
+
QUERY = 'QUERY'
|
|
12
|
+
HEADER = 'HEADER'
|
|
13
|
+
FORMDATA = 'FORMDATA'
|
|
14
|
+
BODY = 'BODY'
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f'{type(self).__name__}.{self.name}'
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def from_string(value: str) -> 'ParameterType':
|
|
21
|
+
|
|
22
|
+
if isinstance(value, str):
|
|
23
|
+
|
|
24
|
+
if value.lower() == 'path':
|
|
25
|
+
return ParameterType.PATH
|
|
26
|
+
elif value.lower() == 'query':
|
|
27
|
+
return ParameterType.QUERY
|
|
28
|
+
elif value.lower() == 'header':
|
|
29
|
+
return ParameterType.HEADER
|
|
30
|
+
elif value.lower() == 'formdata':
|
|
31
|
+
return ParameterType.FORMDATA
|
|
32
|
+
elif value.lower() == 'body':
|
|
33
|
+
return ParameterType.BODY
|
|
34
|
+
else:
|
|
35
|
+
raise SwaggerGeneratorError('Could not convert value {} to a parameter type'.format(value))
|
|
36
|
+
else:
|
|
37
|
+
raise SwaggerGeneratorError("Could not convert non string value to a parameter type")
|
|
38
|
+
|
|
39
|
+
def equals(self, other): # type: ignore
|
|
40
|
+
|
|
41
|
+
if isinstance(other, Enum):
|
|
42
|
+
return self.value == other.value
|
|
43
|
+
else:
|
|
44
|
+
|
|
45
|
+
try:
|
|
46
|
+
data_base_type = ParameterType.from_string(other)
|
|
47
|
+
return data_base_type == self
|
|
48
|
+
except SwaggerGeneratorError:
|
|
49
|
+
pass
|
|
50
|
+
|
|
51
|
+
return other == self.value
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
from typing import Union, List, Dict
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def replace_value_in_dict(item: Union[List, Dict], original_schema): # type: ignore
|
|
5
|
+
if isinstance(item, list):
|
|
6
|
+
return [replace_value_in_dict(i, original_schema) for i in item]
|
|
7
|
+
elif isinstance(item, dict):
|
|
8
|
+
if '$ref' in list(item.keys()):
|
|
9
|
+
definitions = item['$ref'][2:].split('/')
|
|
10
|
+
res = original_schema.copy()
|
|
11
|
+
for definition in definitions:
|
|
12
|
+
if 'enum' in res[definition]:
|
|
13
|
+
res[definition]['type'] = 'string'
|
|
14
|
+
res = res[definition]
|
|
15
|
+
return res
|
|
16
|
+
else:
|
|
17
|
+
return {key: replace_value_in_dict(i, original_schema) for key, i in item.items()}
|
|
18
|
+
return item # type: ignore
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from ul_api_utils.utils.flask_swagger_generator.exceptions import SwaggerGeneratorError
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class RequestType(Enum):
|
|
6
|
+
"""
|
|
7
|
+
Class RequestType: Enum for types of requests
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
POST = 'post'
|
|
11
|
+
GET = 'get'
|
|
12
|
+
DELETE = 'delete'
|
|
13
|
+
PUT = 'put'
|
|
14
|
+
PATCH = 'patch'
|
|
15
|
+
|
|
16
|
+
def __repr__(self) -> str:
|
|
17
|
+
return f'{type(self).__name__}.{self.name}'
|
|
18
|
+
|
|
19
|
+
@staticmethod
|
|
20
|
+
def from_string(value: str) -> 'RequestType':
|
|
21
|
+
|
|
22
|
+
if isinstance(value, str):
|
|
23
|
+
|
|
24
|
+
if value.lower() == 'post':
|
|
25
|
+
return RequestType.POST
|
|
26
|
+
elif value.lower() == 'get':
|
|
27
|
+
return RequestType.GET
|
|
28
|
+
elif value.lower() == 'delete':
|
|
29
|
+
return RequestType.DELETE
|
|
30
|
+
elif value.lower() == 'put':
|
|
31
|
+
return RequestType.PUT
|
|
32
|
+
elif value.lower() == 'patch':
|
|
33
|
+
return RequestType.PATCH
|
|
34
|
+
else:
|
|
35
|
+
raise SwaggerGeneratorError('Could not convert value {} to a request type'.format(value))
|
|
36
|
+
|
|
37
|
+
else:
|
|
38
|
+
raise SwaggerGeneratorError("Could not convert non string value to a request type")
|
|
39
|
+
|
|
40
|
+
def equals(self, other): # type: ignore
|
|
41
|
+
|
|
42
|
+
if isinstance(other, Enum):
|
|
43
|
+
return self.value == other.value
|
|
44
|
+
else:
|
|
45
|
+
|
|
46
|
+
try:
|
|
47
|
+
data_base_type = RequestType.from_string(other)
|
|
48
|
+
return data_base_type == self
|
|
49
|
+
except SwaggerGeneratorError:
|
|
50
|
+
pass
|
|
51
|
+
|
|
52
|
+
return other == self.value
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class SchemaType(Enum):
|
|
5
|
+
"""
|
|
6
|
+
Class SchemaType: Enum for types of schema types
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
LIST = 'LIST'
|
|
10
|
+
MARSH_MALLOW = 'MARSH_MALLOW'
|
|
11
|
+
DICT = 'DICT'
|
|
12
|
+
STRING = 'STRING'
|
|
13
|
+
|
|
14
|
+
def __repr__(self) -> str:
|
|
15
|
+
return f'{type(self).__name__}.{self.name}'
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
from enum import Enum
|
|
2
|
+
from ul_api_utils.utils.flask_swagger_generator.exceptions import SwaggerGeneratorError
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class SecurityType(Enum):
|
|
6
|
+
"""
|
|
7
|
+
Class SecurityType: Enum for types of swagger security types
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
BEARER_AUTH = 'BEARER_AUTH'
|
|
11
|
+
|
|
12
|
+
def __repr__(self) -> str:
|
|
13
|
+
return f'{type(self).__name__}.{self.name}'
|
|
14
|
+
|
|
15
|
+
@staticmethod
|
|
16
|
+
def from_string(value: str) -> 'SecurityType':
|
|
17
|
+
|
|
18
|
+
if isinstance(value, str):
|
|
19
|
+
|
|
20
|
+
if value.lower() == 'bearer_auth':
|
|
21
|
+
return SecurityType.BEARER_AUTH
|
|
22
|
+
else:
|
|
23
|
+
raise SwaggerGeneratorError('Could not convert value {} to a security type'.format(value))
|
|
24
|
+
else:
|
|
25
|
+
raise SwaggerGeneratorError("Could not convert non string value to a security type")
|
|
26
|
+
|
|
27
|
+
def equals(self, other): # type: ignore
|
|
28
|
+
|
|
29
|
+
if isinstance(other, Enum):
|
|
30
|
+
return self.value == other.value
|
|
31
|
+
else:
|
|
32
|
+
|
|
33
|
+
try:
|
|
34
|
+
data_base_type = SecurityType.from_string(other)
|
|
35
|
+
return data_base_type == self
|
|
36
|
+
except SwaggerGeneratorError:
|
|
37
|
+
pass
|
|
38
|
+
|
|
39
|
+
return other == self.value
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import sys
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def has_already_imported(module_name: str) -> bool:
|
|
5
|
+
return module_name in sys.modules
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
_has_already_imported_db = False
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def has_already_imported_db() -> bool:
|
|
12
|
+
global _has_already_imported_db
|
|
13
|
+
if _has_already_imported_db:
|
|
14
|
+
return True
|
|
15
|
+
_has_already_imported_db = has_already_imported('flask_sqlalchemy')
|
|
16
|
+
return _has_already_imported_db
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Any
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
def isinstance_namedtuple(obj: Any) -> bool:
|
|
5
|
+
namedtuple_unique_attributes = ('_asdict', '_fields')
|
|
6
|
+
is_tuple = isinstance(obj, tuple)
|
|
7
|
+
has_namedtuple_attributes = hasattr(obj, namedtuple_unique_attributes[0]) and hasattr(obj, namedtuple_unique_attributes[1])
|
|
8
|
+
return has_namedtuple_attributes and is_tuple
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def is_iterable(obj: Any) -> bool:
|
|
12
|
+
try:
|
|
13
|
+
iter(obj)
|
|
14
|
+
except TypeError:
|
|
15
|
+
return False
|
|
16
|
+
return True
|
|
File without changes
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
from typing import Any, List, Dict
|
|
2
|
+
|
|
3
|
+
from flask import url_for
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
# template url_for
|
|
7
|
+
def t_url_for(*args: Any, **kwargs: Any) -> str:
|
|
8
|
+
res_args: List[Any] = []
|
|
9
|
+
res_kwargs: Dict[str, Any] = {}
|
|
10
|
+
|
|
11
|
+
for arg in args:
|
|
12
|
+
if isinstance(arg, dict):
|
|
13
|
+
res_kwargs.update(arg)
|
|
14
|
+
elif isinstance(arg, (list, tuple)):
|
|
15
|
+
res_args = [*res_args, *arg]
|
|
16
|
+
else:
|
|
17
|
+
res_args.append(arg)
|
|
18
|
+
res_kwargs.update(kwargs)
|
|
19
|
+
return url_for(*res_args, **res_kwargs)
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
def to_pretty_json(value: Any) -> str:
|
|
6
|
+
if isinstance(value, str):
|
|
7
|
+
try:
|
|
8
|
+
value = json.loads(value)
|
|
9
|
+
except Exception: # noqa: B902
|
|
10
|
+
return value
|
|
11
|
+
return json.dumps(value, sort_keys=True, indent=4, separators=(',', ': '))
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import dataclasses
|
|
2
|
+
import decimal
|
|
3
|
+
import json
|
|
4
|
+
from base64 import b64encode
|
|
5
|
+
from datetime import date, datetime, time
|
|
6
|
+
from enum import Enum
|
|
7
|
+
from json import JSONEncoder
|
|
8
|
+
from typing import Dict, Any, Union, List, Optional, TYPE_CHECKING
|
|
9
|
+
from uuid import UUID
|
|
10
|
+
|
|
11
|
+
from flask.json.provider import DefaultJSONProvider
|
|
12
|
+
from frozendict import frozendict
|
|
13
|
+
from pydantic import BaseModel
|
|
14
|
+
|
|
15
|
+
from flask_sqlalchemy.query import Query
|
|
16
|
+
from flask_sqlalchemy.model import Model, DefaultMeta
|
|
17
|
+
|
|
18
|
+
from sqlalchemy.orm import Query, registry
|
|
19
|
+
|
|
20
|
+
from ul_db_utils.modules.postgres_modules.db import DbModel
|
|
21
|
+
from ul_db_utils.model.base_model import BaseModel as DbBaseModel
|
|
22
|
+
from ul_api_utils.utils.imports import has_already_imported_db
|
|
23
|
+
|
|
24
|
+
if TYPE_CHECKING:
|
|
25
|
+
from ul_api_utils.api_resource.api_response_db import TDictable
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def to_dict(obj: 'TDictable') -> Optional[Dict[str, Any]]:
|
|
29
|
+
if isinstance(obj, dict):
|
|
30
|
+
return obj
|
|
31
|
+
if isinstance(obj, tuple) and hasattr(obj, '_asdict'): # NamedTuple
|
|
32
|
+
return obj._asdict()
|
|
33
|
+
if dataclasses.is_dataclass(obj):
|
|
34
|
+
return dataclasses.asdict(obj)
|
|
35
|
+
if isinstance(obj, BaseModel):
|
|
36
|
+
return obj.model_dump()
|
|
37
|
+
if has_already_imported_db():
|
|
38
|
+
if isinstance(obj, DbBaseModel) or isinstance(obj, DbModel):
|
|
39
|
+
return obj.to_dict()
|
|
40
|
+
if isinstance(obj, Model):
|
|
41
|
+
fields = {}
|
|
42
|
+
for field in (x for x in dir(obj) if not x.startswith('_') and x != 'metadata'):
|
|
43
|
+
val = obj.__getattribute__(field)
|
|
44
|
+
# is this field method defination, or an SQLalchemy object
|
|
45
|
+
if not hasattr(val, "__call__") and not isinstance(val, Query): # noqa: B004
|
|
46
|
+
if isinstance(val, datetime):
|
|
47
|
+
val = str(val.isoformat())
|
|
48
|
+
if isinstance(val, UUID):
|
|
49
|
+
val = str(val)
|
|
50
|
+
if isinstance(val, bytes):
|
|
51
|
+
val = b64encode(val).decode()
|
|
52
|
+
if isinstance(val, registry):
|
|
53
|
+
continue
|
|
54
|
+
fields[field] = val
|
|
55
|
+
return fields
|
|
56
|
+
return None
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
class CustomJSONEncoder(JSONEncoder):
|
|
60
|
+
def default(self, obj: object) -> Union[str, Dict[str, Any], List[Any], None]:
|
|
61
|
+
if isinstance(obj, (decimal.Decimal)):
|
|
62
|
+
return str(obj)
|
|
63
|
+
if isinstance(obj, BaseModel):
|
|
64
|
+
return obj.model_dump()
|
|
65
|
+
if isinstance(obj, datetime):
|
|
66
|
+
return str(obj.isoformat())
|
|
67
|
+
if isinstance(obj, date):
|
|
68
|
+
return str(obj.isoformat())
|
|
69
|
+
if isinstance(obj, time):
|
|
70
|
+
return str(obj.isoformat())
|
|
71
|
+
if isinstance(obj, UUID):
|
|
72
|
+
return str(obj)
|
|
73
|
+
if isinstance(obj, Enum):
|
|
74
|
+
return str(obj.value)
|
|
75
|
+
if isinstance(obj, set):
|
|
76
|
+
return list(obj)
|
|
77
|
+
if isinstance(obj, frozendict):
|
|
78
|
+
return dict(obj)
|
|
79
|
+
if dataclasses.is_dataclass(obj):
|
|
80
|
+
return dataclasses.asdict(obj) # type: ignore
|
|
81
|
+
if hasattr(obj, "__html__"): # it needs for Flask ?
|
|
82
|
+
return str(obj.__html__())
|
|
83
|
+
|
|
84
|
+
if has_already_imported_db():
|
|
85
|
+
if isinstance(obj, DbBaseModel):
|
|
86
|
+
return obj.to_dict()
|
|
87
|
+
if isinstance(obj, Query) or isinstance(obj, DefaultMeta):
|
|
88
|
+
return None
|
|
89
|
+
if isinstance(obj, Model):
|
|
90
|
+
fields = {}
|
|
91
|
+
for field in [x for x in dir(obj) if not x.startswith('_') and x != 'metadata']:
|
|
92
|
+
val = obj.__getattribute__(field)
|
|
93
|
+
# is this field method defination, or an SQLalchemy object
|
|
94
|
+
if not hasattr(val, "__call__") and not isinstance(val, Query): # noqa: B004
|
|
95
|
+
if isinstance(val, datetime):
|
|
96
|
+
val = str(val.isoformat())
|
|
97
|
+
if isinstance(val, UUID):
|
|
98
|
+
val = str(val)
|
|
99
|
+
if isinstance(val, bytes):
|
|
100
|
+
val = b64encode(val).decode()
|
|
101
|
+
if isinstance(val, registry):
|
|
102
|
+
continue
|
|
103
|
+
fields[field] = val
|
|
104
|
+
return fields
|
|
105
|
+
return super().default(obj)
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
class SocketIOJsonWrapper:
|
|
109
|
+
@staticmethod
|
|
110
|
+
def dumps(*args: Any, **kwargs: Any) -> str:
|
|
111
|
+
if 'cls' not in kwargs:
|
|
112
|
+
kwargs['cls'] = CustomJSONEncoder
|
|
113
|
+
return json.dumps(*args, **kwargs)
|
|
114
|
+
|
|
115
|
+
@staticmethod
|
|
116
|
+
def loads(*args: Any, **kwargs: Any) -> Any:
|
|
117
|
+
return json.loads(*args, **kwargs)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class CustomJSONProvider(DefaultJSONProvider):
|
|
121
|
+
def __init__(self, app):
|
|
122
|
+
super().__init__(app)
|
|
123
|
+
self.encoder = CustomJSONEncoder()
|
|
124
|
+
|
|
125
|
+
def default(self, obj) -> Union[str, Dict[str, Any], List[Any], None]:
|
|
126
|
+
return self.encoder.default(obj)
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import importlib
|
|
2
|
+
import os
|
|
3
|
+
from typing import List, Optional, Tuple
|
|
4
|
+
|
|
5
|
+
from ul_py_tool.utils.arg_files_glob import arg_files_glob, arg_file_glob_compile_files
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def load_modules_by_template(include: List[str], exclude: Optional[List[str]] = None) -> Tuple[List[str], List[str]]:
|
|
9
|
+
find = arg_files_glob(ignore_absent=True)
|
|
10
|
+
files, ignored = arg_file_glob_compile_files(include=[find(tpl) for tpl in include], exclude=[find(tpl) for tpl in (exclude or [])])
|
|
11
|
+
for file in files:
|
|
12
|
+
file_rel = os.path.relpath(file, os.getcwd())
|
|
13
|
+
mdl = file_rel[:-len('.py')].replace('\\', '/').strip('/').replace('/', '.')
|
|
14
|
+
importlib.import_module(mdl)
|
|
15
|
+
return files, ignored
|
|
File without changes
|
|
File without changes
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import decimal
|
|
2
|
+
from datetime import timedelta
|
|
3
|
+
|
|
4
|
+
import redis
|
|
5
|
+
import ormsgpack
|
|
6
|
+
import collections
|
|
7
|
+
|
|
8
|
+
from pydantic import ValidationError, BaseModel, TypeAdapter
|
|
9
|
+
from typing import Any, Type, overload, Iterator, KeysView, cast
|
|
10
|
+
|
|
11
|
+
from ul_api_utils.utils.instance_checks import isinstance_namedtuple
|
|
12
|
+
from ul_api_utils.utils.memory_db.errors import CompositeKeyError, UnsupportedParsingType
|
|
13
|
+
|
|
14
|
+
CompositeKeyT = tuple[str, Type[BaseModel]]
|
|
15
|
+
AnyKeyT = str | CompositeKeyT
|
|
16
|
+
AnyT = Any
|
|
17
|
+
RedisClientT = redis.StrictRedis | redis.Redis # type: ignore
|
|
18
|
+
ExpiryT = int | timedelta | None
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class BaseMemoryDbRepository(collections.abc.MutableMapping[str, AnyT]):
|
|
22
|
+
def __init__(self, redis_client: RedisClientT) -> None:
|
|
23
|
+
self._db = redis_client
|
|
24
|
+
self._composite_key_max_length = 2
|
|
25
|
+
|
|
26
|
+
@property
|
|
27
|
+
def db(self) -> RedisClientT:
|
|
28
|
+
return self._db
|
|
29
|
+
|
|
30
|
+
def get(self, __key: str, *, parse_as_type: Type[BaseModel] | None = None, default: Any | None = None) -> AnyT | None: # type: ignore
|
|
31
|
+
try:
|
|
32
|
+
if parse_as_type is None:
|
|
33
|
+
return self[__key]
|
|
34
|
+
return self[__key, parse_as_type]
|
|
35
|
+
except KeyError:
|
|
36
|
+
return default
|
|
37
|
+
|
|
38
|
+
def set(self, __key: str, value: AnyT, *, expires: ExpiryT = None) -> None:
|
|
39
|
+
packed_value = ormsgpack.packb(value, option=ormsgpack.OPT_SERIALIZE_PYDANTIC, default=self._default_serializer)
|
|
40
|
+
self._db.set(__key, packed_value, ex=expires)
|
|
41
|
+
|
|
42
|
+
@overload
|
|
43
|
+
def __getitem__(self, key: str) -> AnyT:
|
|
44
|
+
...
|
|
45
|
+
|
|
46
|
+
@overload
|
|
47
|
+
def __getitem__(self, key: CompositeKeyT) -> AnyT:
|
|
48
|
+
...
|
|
49
|
+
|
|
50
|
+
def __getitem__(self, key: AnyKeyT) -> AnyT:
|
|
51
|
+
key_complex = isinstance(key, tuple)
|
|
52
|
+
|
|
53
|
+
if not key_complex:
|
|
54
|
+
single_key = cast(str, key)
|
|
55
|
+
return ormsgpack.unpackb(self._db[single_key])
|
|
56
|
+
|
|
57
|
+
composite_key = cast(CompositeKeyT, key)
|
|
58
|
+
if len(composite_key) > self._composite_key_max_length:
|
|
59
|
+
raise CompositeKeyError(f"Can't retrieve an item with {key=}. Composite key should have only two arguments.")
|
|
60
|
+
|
|
61
|
+
composite_key_name, _parse_as_type = composite_key
|
|
62
|
+
parsing_type_supported = issubclass(_parse_as_type, BaseModel) and _parse_as_type is not BaseModel
|
|
63
|
+
if not parsing_type_supported:
|
|
64
|
+
raise UnsupportedParsingType(f"Unsupported parsing type {_parse_as_type}.")
|
|
65
|
+
value = ormsgpack.unpackb(self._db[composite_key_name])
|
|
66
|
+
try:
|
|
67
|
+
if isinstance(value, list):
|
|
68
|
+
return TypeAdapter(list[_parse_as_type]).validate_python(value) # type: ignore
|
|
69
|
+
return TypeAdapter(_parse_as_type).validate_python(value)
|
|
70
|
+
except ValidationError:
|
|
71
|
+
raise UnsupportedParsingType(f"Could not parse the value of key '{composite_key_name}' with type {_parse_as_type}") from None
|
|
72
|
+
|
|
73
|
+
def __setitem__(self, key: str, value: AnyT) -> None:
|
|
74
|
+
self._db[key] = ormsgpack.packb(value, option=ormsgpack.OPT_SERIALIZE_PYDANTIC, default=self._default_serializer)
|
|
75
|
+
|
|
76
|
+
def __delitem__(self, key: str) -> None:
|
|
77
|
+
del self._db[key]
|
|
78
|
+
|
|
79
|
+
def __iter__(self) -> Iterator[str]:
|
|
80
|
+
return iter(self.keys())
|
|
81
|
+
|
|
82
|
+
def __len__(self) -> int:
|
|
83
|
+
return len(self._db.keys())
|
|
84
|
+
|
|
85
|
+
def keys(self) -> KeysView[str]:
|
|
86
|
+
available_keys = [key.decode() for key in self._db.keys()]
|
|
87
|
+
return cast(KeysView[str], available_keys)
|
|
88
|
+
|
|
89
|
+
def clear(self) -> None:
|
|
90
|
+
self._db.flushdb()
|
|
91
|
+
|
|
92
|
+
@staticmethod
|
|
93
|
+
def _default_serializer(obj: Any) -> Any:
|
|
94
|
+
if isinstance_namedtuple(obj):
|
|
95
|
+
return obj._asdict()
|
|
96
|
+
if isinstance(obj, decimal.Decimal):
|
|
97
|
+
return str(obj)
|
|
98
|
+
if isinstance(obj, set):
|
|
99
|
+
return list(obj)
|
|
100
|
+
if isinstance(obj, frozenset):
|
|
101
|
+
return list(obj)
|
|
102
|
+
raise TypeError
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from typing import Callable, Type, TYPE_CHECKING
|
|
2
|
+
|
|
3
|
+
from ul_api_utils.modules.api_sdk_jwt import ApiSdkJwt
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
if TYPE_CHECKING:
|
|
7
|
+
from ul_db_utils.model.base_model import BaseModel
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def api_auth_token_check(api_auth_model: Type['BaseModel']) -> Callable[[ApiSdkJwt], bool]:
|
|
11
|
+
def token_exist_check(token: ApiSdkJwt) -> bool:
|
|
12
|
+
token_query = api_auth_model.query.filter_by(id=token.user_id, is_alive=True)
|
|
13
|
+
return token_query.first() is not None
|
|
14
|
+
return token_exist_check
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
from typing import Callable
|
|
2
|
+
|
|
3
|
+
from ul_api_utils.errors import Client4XXInternalApiError, NotFinishedRequestInternalApiError
|
|
4
|
+
from ul_api_utils.internal_api.internal_api import InternalApi
|
|
5
|
+
from ul_api_utils.modules.api_sdk_jwt import ApiSdkJwt
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def api_auth_token_check_through_request(auth_internal_api: InternalApi) -> Callable[[ApiSdkJwt], bool]:
|
|
9
|
+
def token_exist_check(token: ApiSdkJwt) -> bool:
|
|
10
|
+
try:
|
|
11
|
+
auth_internal_api.request_get(f"tokens/{token.user_id}").check()
|
|
12
|
+
except (Client4XXInternalApiError, NotFinishedRequestInternalApiError):
|
|
13
|
+
return False
|
|
14
|
+
else:
|
|
15
|
+
return True
|
|
16
|
+
return token_exist_check
|