ul-api-utils 7.6.0__tar.gz → 7.7.0__tar.gz
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.
Potentially problematic release.
This version of ul-api-utils might be problematic. Click here for more details.
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/PKG-INFO +1 -1
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/conf.py +2 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/routes/api_some.py +13 -7
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/setup.py +5 -3
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/const.py +1 -1
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/api_sdk.py +45 -4
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/api_sdk_config.py +3 -0
- ul-api-utils-7.7.0/ul_api_utils/resources/caching.py +187 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/api_method.py +3 -1
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils.egg-info/PKG-INFO +1 -1
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils.egg-info/SOURCES.txt +1 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils.egg-info/requires.txt +2 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/LICENSE +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/README.md +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/main.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/models/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/permissions.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/pure_flask_example.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/rate_limit_load.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/routes/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/workers/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/example/workers/worker.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/setup.cfg +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/access/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_request.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_resource.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_resource_config.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_resource_fn_typing.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_resource_type.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_response.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_response_db.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_response_payload_alias.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/db_types.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/signature_check.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/cmd_enc_keys.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/cmd_gen_api_user_token.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/cmd_gen_new_api_user.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/cmd_start.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/cmd_worker_start.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/start/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/start/gunicorn.conf.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/commands/start/wsgi.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/conf/ul-debugger-main.js +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/conf/ul-debugger-ui.js +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/conf.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/debug/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/debug/debugger.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/debug/malloc.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/debug/stat.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/encrypt/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/encrypt/encrypt_decrypt_abstract.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/encrypt/encrypt_decrypt_aes_xtea.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/errors.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/__tests__/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/__tests__/internal_api.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/__tests__/internal_api_content_type.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/internal_api.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/internal_api_check_context.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/internal_api_error.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/internal_api_response.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/main.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/__tests__/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/__tests__/test_api_sdk_jwt.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/api_sdk_jwt.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/intermediate_state.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/worker_context.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/worker_sdk.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/worker_sdk_config.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/py.typed +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/debugger_scripts.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/health_check/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/health_check/const.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/health_check/health_check.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/health_check/health_check_template.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/health_check/resource.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/not_implemented.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/permissions.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/rate_limitter.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/swagger.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/sentry.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/__tests__/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/__tests__/api_path_version.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/__tests__/unwrap_typing.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/api_encoding.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/api_format.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/api_pagination.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/api_path_version.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/api_request_info.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/avro.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/broker_topics_message_count.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/cached_per_request.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/colors.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/constants.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/decode_base64.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/deprecated.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flags.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/conf.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/exceptions.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/specifiers/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_models.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_specifier.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_three_specifier.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/specifiers/swagger_version.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/utils/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/utils/input_type.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/utils/parameter_type.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/utils/replace_in_dict.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/utils/request_type.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/utils/schema_type.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/utils/security_type.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/imports.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/jinja/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/jinja/t_url_for.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/jinja/to_pretty_json.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/json_encoder.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/load_modules.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/token_check.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/token_check_through_request.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/unwrap_typing.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/uuid_converter.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/validators/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/validators/__tests__/__init__.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/validators/__tests__/test_custom_fields.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/validators/custom_fields.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/validators/validate_empty_object.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/validators/validate_uuid.py +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils.egg-info/dependency_links.txt +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils.egg-info/entry_points.txt +0 -0
- {ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils.egg-info/top_level.txt +0 -0
|
@@ -9,6 +9,8 @@ from example.permissions import permissions
|
|
|
9
9
|
sdk = ApiSdk(ApiSdkConfig(
|
|
10
10
|
service_name='example_service',
|
|
11
11
|
permissions=permissions,
|
|
12
|
+
cache_storage_uri='redis://localhost:16379',
|
|
13
|
+
cache_default_ttl=60,
|
|
12
14
|
rate_limit_storage_uri='redis://localhost:16379',
|
|
13
15
|
rate_limit_identify=ApiSdkIdentifyTypeEnum.JWT_USER_ID,
|
|
14
16
|
flask_debugging_plugins=ApiSdkFlaskDebuggingPluginsEnabled(
|
|
@@ -1,12 +1,14 @@
|
|
|
1
|
-
from datetime import datetime, timedelta
|
|
2
1
|
from time import sleep
|
|
2
|
+
from datetime import datetime, timedelta
|
|
3
3
|
from typing import List, Optional, Tuple
|
|
4
4
|
|
|
5
|
-
from ul_db_utils.modules.db import db
|
|
6
5
|
from flask import jsonify
|
|
7
6
|
from pydantic import BaseModel
|
|
8
|
-
|
|
7
|
+
from ul_db_utils.modules.db import db
|
|
9
8
|
from werkzeug import Response as BaseResponse
|
|
9
|
+
|
|
10
|
+
from example.conf import sdk
|
|
11
|
+
from example.permissions import SOME_PERMISSION, SOME_PERMISSION2
|
|
10
12
|
from ul_api_utils.api_resource.api_request import ApiRequestQuery
|
|
11
13
|
from ul_api_utils.api_resource.api_resource import ApiResource
|
|
12
14
|
from ul_api_utils.api_resource.api_resource_config import ApiResourceConfig
|
|
@@ -16,8 +18,6 @@ from ul_api_utils.internal_api.internal_api import InternalApi
|
|
|
16
18
|
from ul_api_utils.utils.api_encoding import ApiEncoding
|
|
17
19
|
from ul_api_utils.resources.health_check.health_check import HealthCheckContext
|
|
18
20
|
from ul_api_utils.validators.custom_fields import QueryParamsSeparatedList
|
|
19
|
-
from example.conf import sdk
|
|
20
|
-
from example.permissions import SOME_PERMISSION, SOME_PERMISSION2
|
|
21
21
|
|
|
22
22
|
internal_api = InternalApi(
|
|
23
23
|
entry_point='http://localhost:5000',
|
|
@@ -47,10 +47,11 @@ class SomeBody(BaseModel):
|
|
|
47
47
|
|
|
48
48
|
class Some2Query(ApiRequestQuery):
|
|
49
49
|
sleep: float = 0.4
|
|
50
|
-
some: QueryParamsSeparatedList[str]
|
|
50
|
+
some: QueryParamsSeparatedList[str] = '' # type: ignore
|
|
51
51
|
|
|
52
52
|
|
|
53
53
|
@sdk.rest_api('POST', '/example-resource-simple', access=sdk.ACCESS_PUBLIC)
|
|
54
|
+
@sdk.cache_api('REFRESH', 'example')
|
|
54
55
|
def some5(api_resource: ApiResource, body: SomeBody) -> JsonApiResponse[List[RespObject]]:
|
|
55
56
|
return api_resource.response_ok([
|
|
56
57
|
RespObject(now=datetime.now() + timedelta(seconds=body.seconds)),
|
|
@@ -62,6 +63,7 @@ class Some7Query(ApiRequestQuery):
|
|
|
62
63
|
need_redirect: int
|
|
63
64
|
|
|
64
65
|
|
|
66
|
+
@sdk.cache_api('READ', 'example')
|
|
65
67
|
@sdk.rest_api('POST', '/example-resource-simple-any', access=sdk.ACCESS_PUBLIC)
|
|
66
68
|
def some9(api_resource: ApiResource) -> AnyJsonApiResponse:
|
|
67
69
|
return api_resource.response_ok([RespObject(now=datetime.now(), notes="some9")], 1)
|
|
@@ -91,7 +93,7 @@ def some7(api_resource: ApiResource, query: Some7Query) -> JsonApiResponse[List[
|
|
|
91
93
|
|
|
92
94
|
@sdk.rest_api('GET', '/example-resource-simple-proxy', access=sdk.ACCESS_PUBLIC)
|
|
93
95
|
def some6(api_resource: ApiResource) -> ProxyJsonApiResponse[List[RespObject]]: # type: ignore
|
|
94
|
-
res = internal_api.request_post('/example-resource-simple', json={'seconds': 123}).typed(List[RespObject]).check()
|
|
96
|
+
res = internal_api.request_post('/example-resource-simple-any', json={'seconds': 123}).typed(List[RespObject]).check()
|
|
95
97
|
assert res.payload[0].now is not None
|
|
96
98
|
|
|
97
99
|
return api_resource.response_proxy(res)
|
|
@@ -151,6 +153,7 @@ def health_check(context: HealthCheckContext) -> None:
|
|
|
151
153
|
|
|
152
154
|
|
|
153
155
|
@sdk.rest_api('GET', '/example-resource', access=sdk.ACCESS_PUBLIC)
|
|
156
|
+
@sdk.cache_api('READ', ('example', 'resource', 'simple'))
|
|
154
157
|
def some3(api_resource: ApiResource) -> JsonApiResponse[RespObject]:
|
|
155
158
|
api_resource.logger.info('some 1')
|
|
156
159
|
sleep(0.1)
|
|
@@ -182,6 +185,7 @@ def some2html(api_resource: ApiResource, query: Some2Query) -> HtmlApiResponse:
|
|
|
182
185
|
|
|
183
186
|
|
|
184
187
|
@sdk.rest_api('GET', '/example-resource-for-loong-sleep', access=sdk.ACCESS_PUBLIC)
|
|
188
|
+
@sdk.cache_api('READ', ('example', 'resource', 'simple'))
|
|
185
189
|
def some2(api_resource: ApiResource, query: Some2Query) -> JsonApiResponse[RespObject]:
|
|
186
190
|
sess = db.session()
|
|
187
191
|
sess.execute('SELECT * FROM information_schema.tables LIMIT 3;')
|
|
@@ -191,6 +195,7 @@ def some2(api_resource: ApiResource, query: Some2Query) -> JsonApiResponse[RespO
|
|
|
191
195
|
|
|
192
196
|
|
|
193
197
|
@sdk.rest_api('GET', '/example-resource-empty', access=sdk.ACCESS_PUBLIC)
|
|
198
|
+
@sdk.cache_api('READ', ('example', 'resource'))
|
|
194
199
|
def some1(api_resource: ApiResource) -> JsonApiResponse[RespObject]:
|
|
195
200
|
return api_resource.response_ok(RespObject(now=datetime.now()))
|
|
196
201
|
|
|
@@ -213,6 +218,7 @@ def some12(api_resource: ApiResource, body: List[SomeBody]) -> JsonApiResponse[R
|
|
|
213
218
|
|
|
214
219
|
|
|
215
220
|
@sdk.html_view(('GET', 'POST'), '/', access=sdk.ACCESS_PUBLIC)
|
|
221
|
+
@sdk.cache_api('READ', 'some')
|
|
216
222
|
def view_home(api_resource: ApiResource, body: Optional[List[SomeBody]]) -> HtmlApiResponse:
|
|
217
223
|
sleep(0.02)
|
|
218
224
|
sess = db.session()
|
|
@@ -9,7 +9,7 @@ with open(path.join(HERE, 'README.md'), encoding='utf-8') as f:
|
|
|
9
9
|
|
|
10
10
|
setup(
|
|
11
11
|
name='ul-api-utils',
|
|
12
|
-
version='7.
|
|
12
|
+
version='7.7.0',
|
|
13
13
|
description='Python api utils',
|
|
14
14
|
author='Unic-lab',
|
|
15
15
|
long_description=long_description,
|
|
@@ -55,6 +55,7 @@ setup(
|
|
|
55
55
|
"flask==2.1.3",
|
|
56
56
|
"flask-wtf==1.0.1",
|
|
57
57
|
"flask-limiter==2.5.1",
|
|
58
|
+
"flask-caching==2.1.0",
|
|
58
59
|
"flask-swagger-ui==4.11.1",
|
|
59
60
|
"flask-monitoringdashboard==3.1.2",
|
|
60
61
|
"pycryptodome==3.15.0",
|
|
@@ -65,6 +66,7 @@ setup(
|
|
|
65
66
|
"cryptography==38.0.1",
|
|
66
67
|
"colored==1.4.3",
|
|
67
68
|
|
|
69
|
+
"ormsgpack==1.4.1",
|
|
68
70
|
"msgpack==1.0.4",
|
|
69
71
|
"msgpack-types==0.2.0",
|
|
70
72
|
"fastavro==1.7.0",
|
|
@@ -82,7 +84,7 @@ setup(
|
|
|
82
84
|
# "opentelemetry-instrumentation-requests==0.27b0",
|
|
83
85
|
# "opentelemetry-exporter-jaeger==1.8.0",
|
|
84
86
|
# "opentelemetry-instrumentation-sqlalchemy==0.27b0",
|
|
85
|
-
# "ul-db-utils==
|
|
86
|
-
# "ul-py-tool==1.15.
|
|
87
|
+
# "ul-db-utils==3.1.0", # ACTUALIZE, BUT DO NOT UNCOMMENT PLEASE
|
|
88
|
+
# "ul-py-tool==1.15.42", # ACTUALIZE, BUT DO NOT UNCOMMENT PLEASE
|
|
87
89
|
],
|
|
88
90
|
)
|
|
@@ -41,10 +41,10 @@ MIME__MSGPCK = 'application/x-msgpack'
|
|
|
41
41
|
|
|
42
42
|
ENCODING_MIME__GZIP = 'gzip'
|
|
43
43
|
|
|
44
|
-
|
|
45
44
|
REQUEST_METHOD__PUT = 'PUT'
|
|
46
45
|
REQUEST_METHOD__GET = 'GET'
|
|
47
46
|
REQUEST_METHOD__POST = 'POST'
|
|
47
|
+
REQUEST_METHOD__QUERY = 'QUERY'
|
|
48
48
|
REQUEST_METHOD__PATCH = 'PATCH'
|
|
49
49
|
REQUEST_METHOD__DELETE = 'DELETE'
|
|
50
50
|
REQUEST_METHOD__OPTIONS = 'OPTIONS'
|
|
@@ -11,6 +11,7 @@ from typing import List, Union, Callable, Tuple, Any, Optional, TypeVar, cast, S
|
|
|
11
11
|
|
|
12
12
|
from flask import Response, request, Flask, url_for as flask_url_for, _app_ctx_stack
|
|
13
13
|
from pydantic import BaseModel
|
|
14
|
+
from redis.connection import parse_url
|
|
14
15
|
from ul_py_tool.utils.arg_files_glob import arg_files_print
|
|
15
16
|
from werkzeug import Response as BaseResponse
|
|
16
17
|
|
|
@@ -29,6 +30,7 @@ from ul_api_utils.errors import UserAbstractApiError, ResourceRuntimeApiError, R
|
|
|
29
30
|
from ul_api_utils.internal_api.internal_api_check_context import internal_api_check_context
|
|
30
31
|
from ul_api_utils.modules.api_sdk_config import ApiSdkConfig
|
|
31
32
|
from ul_api_utils.modules.intermediate_state import try_init, try_configure
|
|
33
|
+
from ul_api_utils.resources.caching import ULCache, TCacheMode, ULCacheMode, ULCacheConfig
|
|
32
34
|
from ul_api_utils.resources.debugger_scripts import load_debugger_static_scripts
|
|
33
35
|
from ul_api_utils.resources.health_check.health_check import HealthCheckContext
|
|
34
36
|
from ul_api_utils.resources.health_check.resource import init_health_check_resource
|
|
@@ -50,7 +52,7 @@ from ul_api_utils.utils.uuid_converter import UUID4Converter
|
|
|
50
52
|
|
|
51
53
|
if TYPE_CHECKING:
|
|
52
54
|
import flask_sqlalchemy
|
|
53
|
-
from flask_pymongo import PyMongo
|
|
55
|
+
from flask_pymongo import PyMongo # type: ignore # lib without mypy stubs
|
|
54
56
|
from ul_db_utils.modules.db import DbConfig
|
|
55
57
|
from ul_db_utils.modules.mongo_db_modules.db import MongoDbConfig
|
|
56
58
|
|
|
@@ -105,6 +107,7 @@ class ApiSdk:
|
|
|
105
107
|
'_fn_registry',
|
|
106
108
|
'_flask_app_cache',
|
|
107
109
|
'_limiter_enabled',
|
|
110
|
+
'_cache',
|
|
108
111
|
'_db',
|
|
109
112
|
)
|
|
110
113
|
|
|
@@ -117,6 +120,7 @@ class ApiSdk:
|
|
|
117
120
|
self._initialized_flask_name: Optional[str] = None
|
|
118
121
|
self._flask_app_cache: Optional[Flask] = None
|
|
119
122
|
self._limiter_enabled = False
|
|
123
|
+
self._cache = None
|
|
120
124
|
|
|
121
125
|
self._templates_dir = os.path.join(APPLICATION_DIR, 'templates')
|
|
122
126
|
|
|
@@ -139,7 +143,7 @@ class ApiSdk:
|
|
|
139
143
|
|
|
140
144
|
if db_config is not None and type(db_config).__name__ == 'DbConfig':
|
|
141
145
|
from ul_db_utils.utils.waiting_for_postgres import waiting_for_postgres
|
|
142
|
-
from ul_db_utils.modules.db import db
|
|
146
|
+
from ul_db_utils.modules.db import db # type: ignore # yes, db already defined, but can not use two conigs
|
|
143
147
|
db_config._init_from_sdk_with_flask(self)
|
|
144
148
|
waiting_for_postgres(db_config.uri)
|
|
145
149
|
self._db = db
|
|
@@ -152,6 +156,26 @@ class ApiSdk:
|
|
|
152
156
|
rate_limit=self._config.rate_limit,
|
|
153
157
|
storage_uri=self._config.rate_limit_storage_uri,
|
|
154
158
|
)
|
|
159
|
+
if self._config.cache_storage_uri:
|
|
160
|
+
cache_config: ULCacheConfig = {
|
|
161
|
+
'CACHE_TYPE': "RedisCache",
|
|
162
|
+
'CACHE_REDIS_HOST': '',
|
|
163
|
+
'CACHE_REDIS_PORT': '',
|
|
164
|
+
'CACHE_REDIS_PASSWORD': '',
|
|
165
|
+
'CACHE_DEFAULT_TIMEOUT': self._config.cache_default_ttl,
|
|
166
|
+
'CACHE_KEY_PREFIX': f'CACHE__{self._config.service_name}',
|
|
167
|
+
'CACHE_SOURCE_CHECK': True,
|
|
168
|
+
}
|
|
169
|
+
try:
|
|
170
|
+
redis_url = parse_url(self._config.cache_storage_uri)
|
|
171
|
+
except ValueError as e:
|
|
172
|
+
logger.error(f'broken redis uri :: {e}')
|
|
173
|
+
else:
|
|
174
|
+
assert all(('host' in redis_url, 'port' in redis_url)), 'missing part of redis uri'
|
|
175
|
+
cache_config['CACHE_REDIS_HOST'] = redis_url['host']
|
|
176
|
+
cache_config['CACHE_REDIS_PORT'] = redis_url['port']
|
|
177
|
+
cache_config['CACHE_REDIS_PASSWORD'] = redis_url.get('password', '')
|
|
178
|
+
self._cache = ULCache(self._flask_app, config=cache_config) # type: ignore
|
|
155
179
|
|
|
156
180
|
route_files, ignored_route_files = load_modules_by_template([
|
|
157
181
|
os.path.join(APPLICATION_DIR, 'routes', 'api_*.py'),
|
|
@@ -215,7 +239,7 @@ class ApiSdk:
|
|
|
215
239
|
|
|
216
240
|
flask_app.before_request(self._before_request)
|
|
217
241
|
flask_app.after_request(self._after_request)
|
|
218
|
-
flask_app.teardown_request(self.
|
|
242
|
+
flask_app.teardown_request(self._teardown_request)
|
|
219
243
|
|
|
220
244
|
flask_app.errorhandler(404)(functools.partial(
|
|
221
245
|
not_implemented_handler,
|
|
@@ -225,6 +249,10 @@ class ApiSdk:
|
|
|
225
249
|
|
|
226
250
|
return flask_app
|
|
227
251
|
|
|
252
|
+
@property
|
|
253
|
+
def ul_cache_factory(self) -> Optional['ULCache']:
|
|
254
|
+
return self._cache
|
|
255
|
+
|
|
228
256
|
@property
|
|
229
257
|
def db(self) -> Optional['flask_sqlalchemy.SQLAlchemy'] | Optional['PyMongo']:
|
|
230
258
|
return self._db
|
|
@@ -247,7 +275,7 @@ class ApiSdk:
|
|
|
247
275
|
def _debugger_enabled_with_pin(self) -> bool:
|
|
248
276
|
return request.headers.get(REQUEST_HEADER__DEBUGGER) == APPLICATION_DEBUGGER_PIN or request.cookies.get(REQUEST_HEADER__DEBUGGER, "") == APPLICATION_DEBUGGER_PIN
|
|
249
277
|
|
|
250
|
-
def
|
|
278
|
+
def _teardown_request(self, err: Optional[BaseException]) -> None:
|
|
251
279
|
clean_files()
|
|
252
280
|
|
|
253
281
|
def _debug_internal_request(self) -> bool:
|
|
@@ -285,6 +313,18 @@ class ApiSdk:
|
|
|
285
313
|
def health_check(self) -> Callable[[Callable[[HealthCheckContext], None]], None]:
|
|
286
314
|
return functools.partial(init_health_check_resource, api_sdk=self)
|
|
287
315
|
|
|
316
|
+
def cache_api(
|
|
317
|
+
self,
|
|
318
|
+
mode: TCacheMode,
|
|
319
|
+
tags: Tuple[str, ...] | str,
|
|
320
|
+
timeout: Optional[int] = None,
|
|
321
|
+
source_check: Optional[bool] = True,
|
|
322
|
+
) -> Callable[[TFn], TFn]:
|
|
323
|
+
assert self.ul_cache_factory is not None, 'cache not configured !'
|
|
324
|
+
if ULCacheMode.compile_mode(mode) == ULCacheMode.READ.value:
|
|
325
|
+
return self.ul_cache_factory.cache_read_wrap(tags, timeout, source_check)
|
|
326
|
+
return self.ul_cache_factory.cache_refresh_wrap(tags)
|
|
327
|
+
|
|
288
328
|
def rest_api(
|
|
289
329
|
self,
|
|
290
330
|
method: TMethodShort,
|
|
@@ -436,6 +476,7 @@ class ApiSdk:
|
|
|
436
476
|
res = api_resource_config.override_flask_response(res)
|
|
437
477
|
|
|
438
478
|
ri = api_resource.request_info
|
|
479
|
+
# TODO: add cache hit flag
|
|
439
480
|
logger.info('AUDIT ' + json.dumps({
|
|
440
481
|
'user_id': str(scope_user_id) if scope_user_id else None,
|
|
441
482
|
'token_id': str(scope_token_id) if scope_token_id else None,
|
|
@@ -48,6 +48,9 @@ class ApiSdkConfig(BaseModel):
|
|
|
48
48
|
rate_limit_storage_uri: str = '' # supports url of redis, memcached, mongodb
|
|
49
49
|
rate_limit_identify: Union[ApiSdkIdentifyTypeEnum, Callable[[], str]] = ApiSdkIdentifyTypeEnum.DISABLED # must be None if disabled
|
|
50
50
|
|
|
51
|
+
cache_storage_uri: str = '' # supports only redis
|
|
52
|
+
cache_default_ttl: int = 60 # seconds
|
|
53
|
+
|
|
51
54
|
flask_debugging_plugins: Optional[ApiSdkFlaskDebuggingPluginsEnabled] = None
|
|
52
55
|
|
|
53
56
|
api_route_path_prefix: str = '/api'
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import functools
|
|
2
|
+
import hashlib
|
|
3
|
+
import inspect
|
|
4
|
+
import itertools
|
|
5
|
+
import logging
|
|
6
|
+
from _hashlib import HASH # type: ignore
|
|
7
|
+
from collections import defaultdict
|
|
8
|
+
from enum import Enum
|
|
9
|
+
from typing import Optional, Callable, Dict, Tuple, List, Union, Literal, cast, TypeVar, Any, Set, TypedDict
|
|
10
|
+
|
|
11
|
+
import ormsgpack
|
|
12
|
+
from pydantic import ValidationError
|
|
13
|
+
from werkzeug import Response as BaseResponse
|
|
14
|
+
|
|
15
|
+
from flask import request
|
|
16
|
+
from flask_caching import Cache, CachedResponse
|
|
17
|
+
|
|
18
|
+
from ul_api_utils.api_resource.api_response import ApiResponse, JsonApiResponse
|
|
19
|
+
from ul_api_utils.conf import APPLICATION_DEBUG
|
|
20
|
+
from ul_api_utils.utils.constants import TKwargs
|
|
21
|
+
from ul_api_utils.utils.json_encoder import CustomJSONEncoder
|
|
22
|
+
|
|
23
|
+
TCacheModeStr = Union[Literal['READ'], Literal['REFRESH']]
|
|
24
|
+
TFn = TypeVar("TFn", bound=Callable[..., ApiResponse])
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
class ULCacheMode(Enum):
|
|
28
|
+
READ = 'READ'
|
|
29
|
+
REFRESH = 'REFRESH'
|
|
30
|
+
|
|
31
|
+
@staticmethod
|
|
32
|
+
def compile_mode(mode: Union[TCacheModeStr, 'ULCacheMode']) -> str:
|
|
33
|
+
if isinstance(mode, ULCacheMode):
|
|
34
|
+
return mode.value
|
|
35
|
+
assert isinstance(mode, str)
|
|
36
|
+
m = mode.strip().upper()
|
|
37
|
+
return ULCacheMode(m).value
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
TCacheMode = Union[TCacheModeStr, 'ULCacheMode']
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class ULCacheConfig(TypedDict): # https://flask-caching.readthedocs.io/en/latest/#configuring-flask-caching
|
|
44
|
+
CACHE_TYPE: str
|
|
45
|
+
CACHE_REDIS_HOST: str
|
|
46
|
+
CACHE_REDIS_PORT: str
|
|
47
|
+
CACHE_REDIS_PASSWORD: str
|
|
48
|
+
CACHE_KEY_PREFIX: str
|
|
49
|
+
CACHE_DEFAULT_TIMEOUT: int
|
|
50
|
+
CACHE_SOURCE_CHECK: bool
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class ULCache(Cache):
|
|
54
|
+
|
|
55
|
+
@property
|
|
56
|
+
def tags_map(self) -> Dict[Tuple[str, ...], Set[str]]:
|
|
57
|
+
if self.cache.has(':tags_map'):
|
|
58
|
+
return self.cache.get(':tags_map')
|
|
59
|
+
else:
|
|
60
|
+
return defaultdict(set)
|
|
61
|
+
|
|
62
|
+
@tags_map.setter
|
|
63
|
+
def tags_map(self, value: Dict[Tuple[str], Set[str]]) -> None:
|
|
64
|
+
self.cache.set(':tags_map', value, -1)
|
|
65
|
+
|
|
66
|
+
@staticmethod
|
|
67
|
+
def _has_common_elements(seq: Set[Tuple[str, ...]]) -> bool:
|
|
68
|
+
return bool(functools.reduce(set.intersection, map(set, seq))) # type: ignore
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _format_cache_key(parts: List[Any]) -> str:
|
|
72
|
+
return ':' + ':'.join([p for p in parts if p])
|
|
73
|
+
|
|
74
|
+
def make_cache_key(
|
|
75
|
+
self,
|
|
76
|
+
fn: TFn,
|
|
77
|
+
source_check: bool,
|
|
78
|
+
hash_method: Callable[[bytes], HASH],
|
|
79
|
+
) -> str:
|
|
80
|
+
cache_key_parts = [request.path]
|
|
81
|
+
cache_hash = None
|
|
82
|
+
if request.args:
|
|
83
|
+
args_as_sorted_tuple = tuple(sorted(pair for pair in request.args.items(multi=True)))
|
|
84
|
+
|
|
85
|
+
args_as_bytes = str(args_as_sorted_tuple).encode()
|
|
86
|
+
cache_hash = hash_method(args_as_bytes)
|
|
87
|
+
|
|
88
|
+
# Use the source code if source_check is True and update the
|
|
89
|
+
# cache_hash before generating the hashing and using it in cache_key
|
|
90
|
+
if source_check and callable(fn):
|
|
91
|
+
func_source_code = inspect.getsource(fn)
|
|
92
|
+
if cache_hash is not None:
|
|
93
|
+
cache_hash.update(func_source_code.encode("utf-8"))
|
|
94
|
+
else:
|
|
95
|
+
cache_hash = hash_method(func_source_code.encode("utf-8"))
|
|
96
|
+
cache_hash = str(cache_hash.hexdigest()) if cache_hash is not None else ''
|
|
97
|
+
cache_key_parts.append(cache_hash)
|
|
98
|
+
|
|
99
|
+
return self._format_cache_key(cache_key_parts)
|
|
100
|
+
|
|
101
|
+
def cache_refresh_wrap(
|
|
102
|
+
self,
|
|
103
|
+
tags: Tuple[str, ...] | str,
|
|
104
|
+
) -> Callable[[TFn], TFn]:
|
|
105
|
+
def wrap(fn: TFn) -> TFn:
|
|
106
|
+
assert fn.__module__, 'empty __module__ of function'
|
|
107
|
+
func_tags = tags if isinstance(tags, tuple) else (tags,)
|
|
108
|
+
|
|
109
|
+
@functools.wraps(fn)
|
|
110
|
+
def wrapper(*args: Any, **kwargs: TKwargs) -> Tuple[BaseResponse, int]:
|
|
111
|
+
dependent_keys = []
|
|
112
|
+
tags_map = self.tags_map.copy()
|
|
113
|
+
|
|
114
|
+
for t in tags_map.keys():
|
|
115
|
+
stored_tags = t if isinstance(t, tuple) else (t,)
|
|
116
|
+
if self._has_common_elements({stored_tags, func_tags}):
|
|
117
|
+
dependent_keys.append(tags_map[stored_tags].copy())
|
|
118
|
+
tags_map[stored_tags] = set()
|
|
119
|
+
cache_list_to_reset: List[str] = list(itertools.chain.from_iterable(dependent_keys))
|
|
120
|
+
self.delete_many(*cache_list_to_reset)
|
|
121
|
+
tags_map[func_tags] = set()
|
|
122
|
+
self.tags_map = tags_map
|
|
123
|
+
return self._call_fn(fn, *args, **kwargs)
|
|
124
|
+
|
|
125
|
+
return cast(TFn, wrapper)
|
|
126
|
+
return wrap
|
|
127
|
+
|
|
128
|
+
def cache_read_wrap(
|
|
129
|
+
self,
|
|
130
|
+
tags: Tuple[str, ...] | str,
|
|
131
|
+
timeout: Optional[int] = None,
|
|
132
|
+
source_check: Optional[bool] = None,
|
|
133
|
+
hash_method: Callable[[bytes], HASH] = hashlib.md5,
|
|
134
|
+
) -> Callable[[TFn], TFn]:
|
|
135
|
+
def cache_wrap(fn: TFn) -> TFn:
|
|
136
|
+
assert fn.__module__, 'empty __module__ of function'
|
|
137
|
+
|
|
138
|
+
nonlocal source_check
|
|
139
|
+
if source_check is None:
|
|
140
|
+
source_check = self.source_check or False
|
|
141
|
+
|
|
142
|
+
func_tags = tags if isinstance(tags, tuple) else (tags,)
|
|
143
|
+
if self.tags_map.get(func_tags) is None:
|
|
144
|
+
self.tags_map[func_tags] = set()
|
|
145
|
+
|
|
146
|
+
logger = logging.getLogger(fn.__module__)
|
|
147
|
+
|
|
148
|
+
@functools.wraps(fn)
|
|
149
|
+
def cache_wrapper(*args: Any, **kwargs: TKwargs) -> Tuple[BaseResponse | CachedResponse, int]:
|
|
150
|
+
try:
|
|
151
|
+
assert source_check is not None # just for mypy
|
|
152
|
+
cache_key = self.make_cache_key(fn, source_check, hash_method)
|
|
153
|
+
if resp := self.cache.get(cache_key):
|
|
154
|
+
try:
|
|
155
|
+
resp = JsonApiResponse(**ormsgpack.unpackb(resp))
|
|
156
|
+
except ValidationError:
|
|
157
|
+
logger.error(f'cache read error of {fn.__name__} :: response not type of {JsonApiResponse.__name__}')
|
|
158
|
+
raise
|
|
159
|
+
|
|
160
|
+
found = True
|
|
161
|
+
# If the value returned by cache.get() is None
|
|
162
|
+
# it might be because the key is not found in the cache
|
|
163
|
+
# or because the cached value is actually None
|
|
164
|
+
if resp is None:
|
|
165
|
+
found = self.cache.has(cache_key)
|
|
166
|
+
except Exception as e: # noqa: B902 # cuz lot of variations of cache factory impementaions
|
|
167
|
+
logger.error(f'error due cache check :: {e}')
|
|
168
|
+
return self._call_fn(fn, *args, **kwargs)
|
|
169
|
+
if not found:
|
|
170
|
+
tags_map = self.tags_map.copy()
|
|
171
|
+
tags_map[func_tags].add(cache_key)
|
|
172
|
+
resp = self._call_fn(fn, *args, **kwargs)
|
|
173
|
+
try:
|
|
174
|
+
self.cache.set(
|
|
175
|
+
key=cache_key,
|
|
176
|
+
value=ormsgpack.packb(resp, default=CustomJSONEncoder().default),
|
|
177
|
+
timeout=timeout,
|
|
178
|
+
)
|
|
179
|
+
except Exception as e: # noqa: B902 # cuz lot of variations of cache factory impementaions
|
|
180
|
+
if APPLICATION_DEBUG:
|
|
181
|
+
raise
|
|
182
|
+
logger.error(f'exception possibly due to cache response :: {e}')
|
|
183
|
+
self.tags_map = self.tags_map | tags_map
|
|
184
|
+
return resp
|
|
185
|
+
|
|
186
|
+
return cast(TFn, cache_wrapper)
|
|
187
|
+
return cache_wrap
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
from enum import Enum, unique
|
|
2
2
|
from typing import Union, List, Tuple, Any, Literal
|
|
3
3
|
|
|
4
|
-
from ul_api_utils.const import REQUEST_METHOD__PUT, REQUEST_METHOD__GET, REQUEST_METHOD__POST,
|
|
4
|
+
from ul_api_utils.const import REQUEST_METHOD__PUT, REQUEST_METHOD__GET, REQUEST_METHOD__POST, \
|
|
5
|
+
REQUEST_METHOD__PATCH, REQUEST_METHOD__DELETE, REQUEST_METHOD__OPTIONS, REQUEST_METHOD__QUERY
|
|
5
6
|
|
|
6
7
|
TMethodStr = Union[Literal['GET'], Literal['POST'], Literal['PUT'], Literal['PATCH'], Literal['DELETE'], Literal['OPTIONS']]
|
|
7
8
|
TMethod = Union[TMethodStr, 'ApiMethod', List[TMethodStr], Tuple[TMethodStr, ...], List['ApiMethod'], Tuple['ApiMethod', ...]]
|
|
@@ -13,6 +14,7 @@ class ApiMethod(Enum):
|
|
|
13
14
|
PUT = REQUEST_METHOD__PUT
|
|
14
15
|
GET = REQUEST_METHOD__GET
|
|
15
16
|
POST = REQUEST_METHOD__POST
|
|
17
|
+
QUERY = REQUEST_METHOD__QUERY
|
|
16
18
|
PATCH = REQUEST_METHOD__PATCH
|
|
17
19
|
DELETE = REQUEST_METHOD__DELETE
|
|
18
20
|
OPTIONS = REQUEST_METHOD__OPTIONS
|
|
@@ -74,6 +74,7 @@ ul_api_utils/modules/worker_sdk_config.py
|
|
|
74
74
|
ul_api_utils/modules/__tests__/__init__.py
|
|
75
75
|
ul_api_utils/modules/__tests__/test_api_sdk_jwt.py
|
|
76
76
|
ul_api_utils/resources/__init__.py
|
|
77
|
+
ul_api_utils/resources/caching.py
|
|
77
78
|
ul_api_utils/resources/debugger_scripts.py
|
|
78
79
|
ul_api_utils/resources/not_implemented.py
|
|
79
80
|
ul_api_utils/resources/permissions.py
|
|
@@ -5,6 +5,7 @@ jinja2==3.1.2
|
|
|
5
5
|
flask==2.1.3
|
|
6
6
|
flask-wtf==1.0.1
|
|
7
7
|
flask-limiter==2.5.1
|
|
8
|
+
flask-caching==2.1.0
|
|
8
9
|
flask-swagger-ui==4.11.1
|
|
9
10
|
flask-monitoringdashboard==3.1.2
|
|
10
11
|
pycryptodome==3.15.0
|
|
@@ -14,6 +15,7 @@ pyyaml==6.0
|
|
|
14
15
|
requests==2.28.1
|
|
15
16
|
cryptography==38.0.1
|
|
16
17
|
colored==1.4.3
|
|
18
|
+
ormsgpack==1.4.1
|
|
17
19
|
msgpack==1.0.4
|
|
18
20
|
msgpack-types==0.2.0
|
|
19
21
|
fastavro==1.7.0
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_resource_fn_typing.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/api_resource/api_response_payload_alias.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/__tests__/internal_api.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/internal_api_check_context.py
RENAMED
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/internal_api/internal_api_response.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/modules/__tests__/test_api_sdk_jwt.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/resources/health_check/health_check.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/__init__.py
RENAMED
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/conf.py
RENAMED
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/utils/flask_swagger_generator/exceptions.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{ul-api-utils-7.6.0 → ul-api-utils-7.7.0}/ul_api_utils/validators/__tests__/test_custom_fields.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|