amsdal 0.4.13__cp312-cp312-manylinux2014_x86_64.manylinux_2_17_x86_64.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.
- amsdal/Third-Party Materials - AMSDAL Dependencies - License Notices.md +1334 -0
- amsdal/__about__.py +4 -0
- amsdal/__about__.pyi +1 -0
- amsdal/__init__.py +23 -0
- amsdal/__init__.pyi +9 -0
- amsdal/__migrations__/0000_initial.py +217 -0
- amsdal/__migrations__/0001_datetime_type.py +18 -0
- amsdal/__migrations__/0002_fixture_order.py +44 -0
- amsdal/__migrations__/0003_schema_type_in_class_meta.py +44 -0
- amsdal/cloud/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/__init__.pyi +0 -0
- amsdal/cloud/client.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/client.pyi +57 -0
- amsdal/cloud/constants.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/constants.pyi +13 -0
- amsdal/cloud/enums.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/enums.pyi +68 -0
- amsdal/cloud/models/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/models/__init__.pyi +0 -0
- amsdal/cloud/models/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/models/base.pyi +247 -0
- amsdal/cloud/services/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/__init__.pyi +0 -0
- amsdal/cloud/services/actions/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/__init__.pyi +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.pyi +19 -0
- amsdal/cloud/services/actions/add_basic_auth.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_basic_auth.pyi +21 -0
- amsdal/cloud/services/actions/add_dependency.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_dependency.pyi +19 -0
- amsdal/cloud/services/actions/add_secret.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/add_secret.pyi +20 -0
- amsdal/cloud/services/actions/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/base.pyi +122 -0
- amsdal/cloud/services/actions/create_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_deploy.pyi +41 -0
- amsdal/cloud/services/actions/create_env.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_env.pyi +19 -0
- amsdal/cloud/services/actions/create_session.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/create_session.pyi +17 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.pyi +19 -0
- amsdal/cloud/services/actions/delete_basic_auth.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_basic_auth.pyi +20 -0
- amsdal/cloud/services/actions/delete_dependency.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_dependency.pyi +21 -0
- amsdal/cloud/services/actions/delete_env.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_env.pyi +21 -0
- amsdal/cloud/services/actions/delete_secret.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/delete_secret.pyi +21 -0
- amsdal/cloud/services/actions/destroy_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/destroy_deploy.pyi +18 -0
- amsdal/cloud/services/actions/expose_db.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/expose_db.pyi +22 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.pyi +21 -0
- amsdal/cloud/services/actions/get_monitoring_info.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/get_monitoring_info.pyi +21 -0
- amsdal/cloud/services/actions/list_dependencies.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_dependencies.pyi +21 -0
- amsdal/cloud/services/actions/list_deploys.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_deploys.pyi +19 -0
- amsdal/cloud/services/actions/list_envs.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_envs.pyi +20 -0
- amsdal/cloud/services/actions/list_secrets.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/list_secrets.pyi +22 -0
- amsdal/cloud/services/actions/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/manager.pyi +278 -0
- amsdal/cloud/services/actions/signup_action.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/signup_action.pyi +20 -0
- amsdal/cloud/services/actions/update_deploy.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/actions/update_deploy.pyi +19 -0
- amsdal/cloud/services/auth/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/__init__.pyi +0 -0
- amsdal/cloud/services/auth/base.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/base.pyi +6 -0
- amsdal/cloud/services/auth/credentials.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/credentials.pyi +30 -0
- amsdal/cloud/services/auth/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/manager.pyi +26 -0
- amsdal/cloud/services/auth/signup_service.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/signup_service.pyi +32 -0
- amsdal/cloud/services/auth/token.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/cloud/services/auth/token.pyi +27 -0
- amsdal/configs/__init__.py +0 -0
- amsdal/configs/__init__.pyi +0 -0
- amsdal/configs/constants.py +33 -0
- amsdal/configs/constants.pyi +22 -0
- amsdal/configs/main.py +258 -0
- amsdal/configs/main.pyi +173 -0
- amsdal/context/__init__.py +0 -0
- amsdal/context/__init__.pyi +0 -0
- amsdal/context/manager.py +69 -0
- amsdal/context/manager.pyi +50 -0
- amsdal/contrib/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/contrib/__init__.pyi +0 -0
- amsdal/contrib/app_config.py +7 -0
- amsdal/contrib/app_config.pyi +6 -0
- amsdal/contrib/auth/__init__.py +0 -0
- amsdal/contrib/auth/__init__.pyi +0 -0
- amsdal/contrib/auth/app.py +27 -0
- amsdal/contrib/auth/app.pyi +15 -0
- amsdal/contrib/auth/decorators/__init__.py +35 -0
- amsdal/contrib/auth/decorators/__init__.pyi +6 -0
- amsdal/contrib/auth/errors.py +7 -0
- amsdal/contrib/auth/errors.pyi +4 -0
- amsdal/contrib/auth/fixtures/basic_permissions.json +64 -0
- amsdal/contrib/auth/lifecycle/__init__.py +0 -0
- amsdal/contrib/auth/lifecycle/__init__.pyi +0 -0
- amsdal/contrib/auth/lifecycle/consumer.py +394 -0
- amsdal/contrib/auth/lifecycle/consumer.pyi +108 -0
- amsdal/contrib/auth/migrations/0000_initial.py +84 -0
- amsdal/contrib/auth/models/__init__.py +0 -0
- amsdal/contrib/auth/models/__init__.pyi +0 -0
- amsdal/contrib/auth/models/login_session.py +118 -0
- amsdal/contrib/auth/models/login_session.pyi +37 -0
- amsdal/contrib/auth/models/permission.py +23 -0
- amsdal/contrib/auth/models/permission.pyi +18 -0
- amsdal/contrib/auth/models/user.py +106 -0
- amsdal/contrib/auth/models/user.pyi +46 -0
- amsdal/contrib/auth/settings.py +36 -0
- amsdal/contrib/auth/settings.pyi +26 -0
- amsdal/contrib/frontend_configs/__init__.py +0 -0
- amsdal/contrib/frontend_configs/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/app.py +24 -0
- amsdal/contrib/frontend_configs/app.pyi +19 -0
- amsdal/contrib/frontend_configs/constants.py +1 -0
- amsdal/contrib/frontend_configs/constants.pyi +1 -0
- amsdal/contrib/frontend_configs/conversion/__init__.py +5 -0
- amsdal/contrib/frontend_configs/conversion/__init__.pyi +3 -0
- amsdal/contrib/frontend_configs/conversion/convert.py +286 -0
- amsdal/contrib/frontend_configs/conversion/convert.pyi +22 -0
- amsdal/contrib/frontend_configs/lifecycle/__init__.py +0 -0
- amsdal/contrib/frontend_configs/lifecycle/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/lifecycle/consumer.py +306 -0
- amsdal/contrib/frontend_configs/lifecycle/consumer.pyi +98 -0
- amsdal/contrib/frontend_configs/migrations/0000_initial.py +256 -0
- amsdal/contrib/frontend_configs/models/__init__.py +0 -0
- amsdal/contrib/frontend_configs/models/__init__.pyi +0 -0
- amsdal/contrib/frontend_configs/models/frontend_activator_config.py +22 -0
- amsdal/contrib/frontend_configs/models/frontend_activator_config.pyi +12 -0
- amsdal/contrib/frontend_configs/models/frontend_config_async_validator.py +11 -0
- amsdal/contrib/frontend_configs/models/frontend_config_async_validator.pyi +7 -0
- amsdal/contrib/frontend_configs/models/frontend_config_control_action.py +54 -0
- amsdal/contrib/frontend_configs/models/frontend_config_control_action.pyi +32 -0
- amsdal/contrib/frontend_configs/models/frontend_config_group_validator.py +21 -0
- amsdal/contrib/frontend_configs/models/frontend_config_group_validator.pyi +11 -0
- amsdal/contrib/frontend_configs/models/frontend_config_option.py +12 -0
- amsdal/contrib/frontend_configs/models/frontend_config_option.pyi +8 -0
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.py +17 -0
- amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.pyi +8 -0
- amsdal/contrib/frontend_configs/models/frontend_config_slider_option.py +13 -0
- amsdal/contrib/frontend_configs/models/frontend_config_slider_option.pyi +9 -0
- amsdal/contrib/frontend_configs/models/frontend_config_text_mask.py +14 -0
- amsdal/contrib/frontend_configs/models/frontend_config_text_mask.pyi +10 -0
- amsdal/contrib/frontend_configs/models/frontend_config_validator.py +28 -0
- amsdal/contrib/frontend_configs/models/frontend_config_validator.pyi +15 -0
- amsdal/contrib/frontend_configs/models/frontend_control_config.py +87 -0
- amsdal/contrib/frontend_configs/models/frontend_control_config.pyi +35 -0
- amsdal/contrib/frontend_configs/models/frontend_model_config.py +14 -0
- amsdal/contrib/frontend_configs/models/frontend_model_config.pyi +9 -0
- amsdal/contrib/frontend_configs/utils.py +29 -0
- amsdal/contrib/frontend_configs/utils.pyi +17 -0
- amsdal/errors.py +31 -0
- amsdal/errors.pyi +12 -0
- amsdal/fixtures/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/__init__.pyi +0 -0
- amsdal/fixtures/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/manager.pyi +170 -0
- amsdal/fixtures/utils.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/fixtures/utils.pyi +9 -0
- amsdal/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/manager.pyi +265 -0
- amsdal/mixins/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/mixins/__init__.pyi +0 -0
- amsdal/mixins/class_versions_mixin.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/mixins/class_versions_mixin.pyi +12 -0
- amsdal/models/__init__.py +19 -0
- amsdal/models/__init__.pyi +9 -0
- amsdal/models/core/__init__.py +0 -0
- amsdal/models/core/__init__.pyi +0 -0
- amsdal/models/core/class_object.py +37 -0
- amsdal/models/core/class_object.pyi +24 -0
- amsdal/models/core/class_object_meta.py +26 -0
- amsdal/models/core/class_object_meta.pyi +15 -0
- amsdal/models/core/class_property.py +20 -0
- amsdal/models/core/class_property.pyi +11 -0
- amsdal/models/core/class_property_meta.py +15 -0
- amsdal/models/core/class_property_meta.pyi +10 -0
- amsdal/models/core/file.py +156 -0
- amsdal/models/core/file.pyi +104 -0
- amsdal/models/core/fixture.py +25 -0
- amsdal/models/core/fixture.pyi +14 -0
- amsdal/models/core/option.py +11 -0
- amsdal/models/core/option.pyi +8 -0
- amsdal/models/core/validator.py +12 -0
- amsdal/models/core/validator.pyi +8 -0
- amsdal/models/types/__init__.py +0 -0
- amsdal/models/types/__init__.pyi +0 -0
- amsdal/models/types/object.py +26 -0
- amsdal/models/types/object.pyi +16 -0
- amsdal/py.typed +0 -0
- amsdal/queryset/__init__.py +21 -0
- amsdal/queryset/__init__.pyi +6 -0
- amsdal/schemas/__init__.py +0 -0
- amsdal/schemas/__init__.pyi +0 -0
- amsdal/schemas/core/class_object/model.json +31 -0
- amsdal/schemas/core/class_object/properties/display_name.py +9 -0
- amsdal/schemas/core/class_object_meta/model.json +59 -0
- amsdal/schemas/core/class_property/model.json +22 -0
- amsdal/schemas/core/class_property_meta/model.json +23 -0
- amsdal/schemas/core/file/hooks/pre_create.py +24 -0
- amsdal/schemas/core/file/hooks/pre_update.py +24 -0
- amsdal/schemas/core/file/model.json +23 -0
- amsdal/schemas/core/file/properties/from_file.py +34 -0
- amsdal/schemas/core/file/properties/mimetype.py +13 -0
- amsdal/schemas/core/file/properties/str.py +6 -0
- amsdal/schemas/core/file/properties/to_file.py +24 -0
- amsdal/schemas/core/file/properties/validate_data.py +32 -0
- amsdal/schemas/core/fixture/model.json +35 -0
- amsdal/schemas/core/option/model.json +19 -0
- amsdal/schemas/core/validator/model.json +19 -0
- amsdal/schemas/interfaces.py +25 -0
- amsdal/schemas/interfaces.pyi +20 -0
- amsdal/schemas/manager.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/schemas/manager.py +0 -0
- amsdal/schemas/manager.pyi +0 -0
- amsdal/schemas/mixins/__init__.py +0 -0
- amsdal/schemas/mixins/__init__.pyi +0 -0
- amsdal/schemas/mixins/check_dependencies_mixin.py +125 -0
- amsdal/schemas/mixins/check_dependencies_mixin.pyi +45 -0
- amsdal/schemas/mixins/verify_schemas_mixin.py +96 -0
- amsdal/schemas/mixins/verify_schemas_mixin.pyi +33 -0
- amsdal/schemas/repository.py +84 -0
- amsdal/schemas/repository.pyi +22 -0
- amsdal/schemas/types/anything/model.json +7 -0
- amsdal/schemas/types/array/model.json +7 -0
- amsdal/schemas/types/binary/model.json +7 -0
- amsdal/schemas/types/boolean/model.json +17 -0
- amsdal/schemas/types/date/model.json +7 -0
- amsdal/schemas/types/datetime/model.json +7 -0
- amsdal/schemas/types/dictionary/model.json +8 -0
- amsdal/schemas/types/number/model.json +8 -0
- amsdal/schemas/types/object/model.json +53 -0
- amsdal/schemas/types/string/model.json +8 -0
- amsdal/schemas/utils.py +16 -0
- amsdal/schemas/utils.pyi +10 -0
- amsdal/services/__init__.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/services/__init__.pyi +0 -0
- amsdal/services/transaction_execution.cpython-312-x86_64-linux-gnu.so +0 -0
- amsdal/services/transaction_execution.pyi +93 -0
- amsdal/transactions/__init__.py +13 -0
- amsdal/transactions/__init__.pyi +4 -0
- amsdal/utils/__init__.py +0 -0
- amsdal/utils/__init__.pyi +0 -0
- amsdal/utils/contrib_paths.py +23 -0
- amsdal/utils/contrib_paths.pyi +14 -0
- amsdal/utils/rollback/__init__.py +440 -0
- amsdal/utils/rollback/__init__.pyi +38 -0
- amsdal/utils/tests/__init__.py +0 -0
- amsdal/utils/tests/enums.py +18 -0
- amsdal/utils/tests/factories.py +49 -0
- amsdal/utils/tests/helpers.py +499 -0
- amsdal-0.4.13.dist-info/METADATA +369 -0
- amsdal-0.4.13.dist-info/RECORD +269 -0
- amsdal-0.4.13.dist-info/WHEEL +6 -0
- amsdal-0.4.13.dist-info/licenses/LICENSE.txt +107 -0
- amsdal-0.4.13.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
|
|
1
|
+
ON_RESPONSE_EVENT: str
|
@@ -0,0 +1,286 @@
|
|
1
|
+
from contextlib import suppress
|
2
|
+
from datetime import date
|
3
|
+
from datetime import datetime
|
4
|
+
from enum import Enum
|
5
|
+
from inspect import _empty
|
6
|
+
from inspect import signature
|
7
|
+
from types import FunctionType
|
8
|
+
from types import UnionType
|
9
|
+
from typing import Any
|
10
|
+
from typing import ClassVar
|
11
|
+
from typing import ForwardRef
|
12
|
+
from typing import Union
|
13
|
+
from typing import get_args
|
14
|
+
from typing import get_origin
|
15
|
+
|
16
|
+
from amsdal_models.classes.class_manager import ClassManager
|
17
|
+
from amsdal_models.classes.model import LegacyModel
|
18
|
+
from amsdal_models.classes.model import Model
|
19
|
+
from amsdal_models.classes.model import TypeModel
|
20
|
+
from amsdal_models.classes.relationships.constants import MANY_TO_MANY_FIELDS
|
21
|
+
from amsdal_models.schemas.object_schema import model_to_object_schema
|
22
|
+
from amsdal_utils.models.data_models.reference import Reference
|
23
|
+
from pydantic import BaseModel
|
24
|
+
from pydantic_core import PydanticUndefined
|
25
|
+
|
26
|
+
default_types_map = {
|
27
|
+
int: 'number',
|
28
|
+
float: 'number',
|
29
|
+
bool: 'checkbox',
|
30
|
+
str: 'text',
|
31
|
+
bytes: 'Bytes',
|
32
|
+
date: 'date',
|
33
|
+
datetime: 'datetime',
|
34
|
+
}
|
35
|
+
|
36
|
+
|
37
|
+
def _process_union(value: UnionType, *, is_transaction: bool = False) -> dict[str, Any]:
|
38
|
+
arg_type = {'required': True}
|
39
|
+
|
40
|
+
for arg in get_args(value):
|
41
|
+
if arg is type(None):
|
42
|
+
arg_type['required'] = False
|
43
|
+
continue
|
44
|
+
|
45
|
+
if not is_transaction:
|
46
|
+
with suppress(TypeError):
|
47
|
+
if issubclass(arg, Model):
|
48
|
+
arg_type['type'] = 'object_latest' # type: ignore[assignment]
|
49
|
+
arg_type['entityType'] = arg.__name__
|
50
|
+
continue
|
51
|
+
|
52
|
+
control = convert_to_frontend_config(arg, is_transaction=is_transaction)
|
53
|
+
if control:
|
54
|
+
arg_type.update(control)
|
55
|
+
|
56
|
+
return arg_type
|
57
|
+
|
58
|
+
|
59
|
+
def convert_to_frontend_config(value: Any, *, is_transaction: bool = False) -> dict[str, Any]:
|
60
|
+
"""
|
61
|
+
Converts a given value to a frontend configuration dictionary.
|
62
|
+
|
63
|
+
This function takes a value and converts it into a dictionary that represents
|
64
|
+
the configuration for a frontend form control. It handles various types such as
|
65
|
+
Union, list, dict, BaseModel, and custom types.
|
66
|
+
|
67
|
+
Args:
|
68
|
+
value (Any): The value to be converted to frontend configuration.
|
69
|
+
is_transaction (bool, optional): Indicates if the conversion is for a transaction. Defaults to False.
|
70
|
+
|
71
|
+
Returns:
|
72
|
+
dict[str, Any]: A dictionary representing the frontend configuration for the given value.
|
73
|
+
"""
|
74
|
+
schema = None
|
75
|
+
origin_class = get_origin(value)
|
76
|
+
|
77
|
+
if origin_class:
|
78
|
+
if origin_class in [ClassVar]:
|
79
|
+
return {}
|
80
|
+
|
81
|
+
if origin_class is Union:
|
82
|
+
_union = _process_union(value, is_transaction=is_transaction)
|
83
|
+
|
84
|
+
if 'entityType' in _union and _union['entityType'] == 'File':
|
85
|
+
_union['type'] = 'file'
|
86
|
+
del _union['entityType']
|
87
|
+
|
88
|
+
return _union
|
89
|
+
|
90
|
+
if origin_class is list:
|
91
|
+
return {
|
92
|
+
'type': 'array',
|
93
|
+
'name': 'array_items',
|
94
|
+
'label': 'array_items',
|
95
|
+
'control': {
|
96
|
+
'name': 'array_items_values',
|
97
|
+
'label': 'array_items_values',
|
98
|
+
**convert_to_frontend_config(value.__args__[0], is_transaction=is_transaction),
|
99
|
+
},
|
100
|
+
}
|
101
|
+
|
102
|
+
if origin_class is dict:
|
103
|
+
return {
|
104
|
+
'type': 'dict',
|
105
|
+
'name': 'dict_items',
|
106
|
+
'label': 'dict_items',
|
107
|
+
'control': {
|
108
|
+
'name': 'dict_items_values',
|
109
|
+
'label': 'dict_items_values',
|
110
|
+
**convert_to_frontend_config(value.__args__[1], is_transaction=is_transaction),
|
111
|
+
},
|
112
|
+
}
|
113
|
+
|
114
|
+
if isinstance(value, ForwardRef):
|
115
|
+
class_name = value.__forward_arg__
|
116
|
+
_class = ClassManager().import_class(class_name)
|
117
|
+
|
118
|
+
if issubclass(_class, Model):
|
119
|
+
return {
|
120
|
+
'entityType': value.__forward_arg__,
|
121
|
+
}
|
122
|
+
|
123
|
+
value = _class
|
124
|
+
|
125
|
+
if isinstance(value, UnionType):
|
126
|
+
_union = _process_union(value, is_transaction=is_transaction)
|
127
|
+
|
128
|
+
if 'entityType' in _union and _union['entityType'] == 'File':
|
129
|
+
_union['type'] = 'file'
|
130
|
+
del _union['entityType']
|
131
|
+
|
132
|
+
return _union
|
133
|
+
|
134
|
+
if value in default_types_map:
|
135
|
+
return {
|
136
|
+
'type': default_types_map[value],
|
137
|
+
}
|
138
|
+
|
139
|
+
if value is Any:
|
140
|
+
return {
|
141
|
+
'type': 'text',
|
142
|
+
}
|
143
|
+
|
144
|
+
if isinstance(value, FunctionType):
|
145
|
+
function_controls = []
|
146
|
+
|
147
|
+
while hasattr(value, '__wrapped__'):
|
148
|
+
value = value.__wrapped__
|
149
|
+
|
150
|
+
_signature = signature(value)
|
151
|
+
_parameters = _signature.parameters
|
152
|
+
|
153
|
+
for arg_name in _parameters:
|
154
|
+
if arg_name in value.__annotations__:
|
155
|
+
arg_type = value.__annotations__[arg_name]
|
156
|
+
control = convert_to_frontend_config(
|
157
|
+
arg_type,
|
158
|
+
is_transaction=True,
|
159
|
+
)
|
160
|
+
|
161
|
+
if not control:
|
162
|
+
continue
|
163
|
+
|
164
|
+
control['name'] = arg_name
|
165
|
+
control['label'] = arg_name
|
166
|
+
else:
|
167
|
+
control = {
|
168
|
+
'type': 'text',
|
169
|
+
'name': arg_name,
|
170
|
+
'label': arg_name,
|
171
|
+
}
|
172
|
+
|
173
|
+
control.setdefault('required', True)
|
174
|
+
_param = _parameters[arg_name]
|
175
|
+
if _param.default is not _empty:
|
176
|
+
control['value'] = _param.default
|
177
|
+
control['required'] = False
|
178
|
+
|
179
|
+
if not control['name'].startswith('_'):
|
180
|
+
function_controls.append(control)
|
181
|
+
|
182
|
+
return {
|
183
|
+
'type': 'group',
|
184
|
+
'name': value.__name__,
|
185
|
+
'label': value.__name__,
|
186
|
+
'controls': function_controls,
|
187
|
+
}
|
188
|
+
|
189
|
+
try:
|
190
|
+
if issubclass(value, Reference):
|
191
|
+
return {
|
192
|
+
'type': 'object_latest',
|
193
|
+
}
|
194
|
+
except TypeError:
|
195
|
+
return {}
|
196
|
+
|
197
|
+
if is_transaction and issubclass(value, Model):
|
198
|
+
if value.__name__ == 'File':
|
199
|
+
return {
|
200
|
+
'type': 'file',
|
201
|
+
}
|
202
|
+
return {
|
203
|
+
'type': 'object_latest',
|
204
|
+
'entityType': value.__name__,
|
205
|
+
}
|
206
|
+
|
207
|
+
if issubclass(value, LegacyModel):
|
208
|
+
return {}
|
209
|
+
|
210
|
+
if issubclass(value, BaseModel):
|
211
|
+
model_controls = []
|
212
|
+
|
213
|
+
try:
|
214
|
+
if issubclass(value, Model | TypeModel):
|
215
|
+
schema = model_to_object_schema(value)
|
216
|
+
except FileNotFoundError:
|
217
|
+
schema = None
|
218
|
+
|
219
|
+
if value.__name__ == 'File':
|
220
|
+
return {
|
221
|
+
'type': 'file',
|
222
|
+
}
|
223
|
+
|
224
|
+
for field_name, field in value.model_fields.items():
|
225
|
+
control = convert_to_frontend_config(field.annotation, is_transaction=is_transaction)
|
226
|
+
|
227
|
+
if not control:
|
228
|
+
continue
|
229
|
+
|
230
|
+
control.setdefault('required', True)
|
231
|
+
|
232
|
+
if field.default is not PydanticUndefined:
|
233
|
+
control['value'] = field.default
|
234
|
+
|
235
|
+
control['name'] = field_name
|
236
|
+
control['label'] = field_name
|
237
|
+
|
238
|
+
if schema and schema.properties and field_name in schema.properties:
|
239
|
+
schema_property = schema.properties[field_name]
|
240
|
+
|
241
|
+
if schema_property.options:
|
242
|
+
control['options'] = [
|
243
|
+
{
|
244
|
+
'label': option.key,
|
245
|
+
'value': option.value,
|
246
|
+
}
|
247
|
+
for option in schema_property.options
|
248
|
+
]
|
249
|
+
if control.get('type') == 'text':
|
250
|
+
control['type'] = 'select'
|
251
|
+
|
252
|
+
if schema_property.title:
|
253
|
+
control['label'] = schema_property.title
|
254
|
+
|
255
|
+
if not control['name'].startswith('_'):
|
256
|
+
model_controls.append(control)
|
257
|
+
|
258
|
+
for m2m, (m2m_ref, _, _, field_info) in (getattr(value, MANY_TO_MANY_FIELDS, None) or {}).items():
|
259
|
+
pass
|
260
|
+
control = convert_to_frontend_config(list[Reference | m2m_ref], is_transaction=is_transaction) # type: ignore[valid-type]
|
261
|
+
|
262
|
+
if getattr(field_info, 'default', PydanticUndefined) is not PydanticUndefined:
|
263
|
+
control['value'] = field_info.default
|
264
|
+
|
265
|
+
control['name'] = m2m
|
266
|
+
control['label'] = m2m
|
267
|
+
control['required'] = False
|
268
|
+
model_controls.append(control)
|
269
|
+
|
270
|
+
return {
|
271
|
+
'type': 'group',
|
272
|
+
'name': value.__name__,
|
273
|
+
'label': value.__name__,
|
274
|
+
'controls': model_controls,
|
275
|
+
}
|
276
|
+
|
277
|
+
try:
|
278
|
+
if issubclass(value, Enum):
|
279
|
+
return {
|
280
|
+
'type': 'select',
|
281
|
+
'options': [{'label': option.name, 'value': option.value} for option in value],
|
282
|
+
}
|
283
|
+
except TypeError:
|
284
|
+
pass
|
285
|
+
|
286
|
+
return {}
|
@@ -0,0 +1,22 @@
|
|
1
|
+
from _typeshed import Incomplete
|
2
|
+
from types import UnionType
|
3
|
+
from typing import Any
|
4
|
+
|
5
|
+
default_types_map: Incomplete
|
6
|
+
|
7
|
+
def _process_union(value: UnionType, *, is_transaction: bool = False) -> dict[str, Any]: ...
|
8
|
+
def convert_to_frontend_config(value: Any, *, is_transaction: bool = False) -> dict[str, Any]:
|
9
|
+
"""
|
10
|
+
Converts a given value to a frontend configuration dictionary.
|
11
|
+
|
12
|
+
This function takes a value and converts it into a dictionary that represents
|
13
|
+
the configuration for a frontend form control. It handles various types such as
|
14
|
+
Union, list, dict, BaseModel, and custom types.
|
15
|
+
|
16
|
+
Args:
|
17
|
+
value (Any): The value to be converted to frontend configuration.
|
18
|
+
is_transaction (bool, optional): Indicates if the conversion is for a transaction. Defaults to False.
|
19
|
+
|
20
|
+
Returns:
|
21
|
+
dict[str, Any]: A dictionary representing the frontend configuration for the given value.
|
22
|
+
"""
|
File without changes
|
File without changes
|
@@ -0,0 +1,306 @@
|
|
1
|
+
# mypy: disable-error-code="arg-type"
|
2
|
+
import contextlib
|
3
|
+
import logging
|
4
|
+
from typing import Any
|
5
|
+
|
6
|
+
from amsdal_models.classes.errors import AmsdalClassNotFoundError
|
7
|
+
from amsdal_utils.lifecycle.consumer import LifecycleConsumer
|
8
|
+
from amsdal_utils.models.data_models.address import Address
|
9
|
+
from amsdal_utils.models.data_models.core import LegacyDictSchema
|
10
|
+
from amsdal_utils.models.data_models.enums import CoreTypes
|
11
|
+
from amsdal_utils.models.enums import Versions
|
12
|
+
from amsdal_utils.schemas.schema import PropertyData
|
13
|
+
|
14
|
+
logger = logging.getLogger(__name__)
|
15
|
+
|
16
|
+
core_to_frontend_types = {
|
17
|
+
CoreTypes.NUMBER.value: 'number',
|
18
|
+
CoreTypes.INTEGER.value: 'integer',
|
19
|
+
CoreTypes.BOOLEAN.value: 'checkbox',
|
20
|
+
CoreTypes.STRING.value: 'text',
|
21
|
+
CoreTypes.ANYTHING.value: 'text',
|
22
|
+
CoreTypes.BINARY.value: 'text',
|
23
|
+
CoreTypes.DATE.value: 'date',
|
24
|
+
CoreTypes.DATETIME.value: 'datetime',
|
25
|
+
}
|
26
|
+
|
27
|
+
|
28
|
+
def process_property(field_name: str, property_data: PropertyData) -> dict[str, Any]:
|
29
|
+
"""
|
30
|
+
Processes a property and converts it to a frontend configuration dictionary.
|
31
|
+
|
32
|
+
This function takes a field name and property data, and converts them into a dictionary
|
33
|
+
that represents the configuration for a frontend form control. It handles various types
|
34
|
+
such as core types, arrays, dictionaries, and files.
|
35
|
+
|
36
|
+
Args:
|
37
|
+
field_name (str): The name of the field to be processed.
|
38
|
+
property_data (PropertyData): The property data to be processed.
|
39
|
+
|
40
|
+
Returns:
|
41
|
+
dict[str, Any]: A dictionary representing the frontend configuration for the given property.
|
42
|
+
"""
|
43
|
+
type_definition: dict[str, Any]
|
44
|
+
if property_data.type in core_to_frontend_types:
|
45
|
+
type_definition = {
|
46
|
+
'type': core_to_frontend_types[property_data.type],
|
47
|
+
}
|
48
|
+
elif property_data.type == CoreTypes.ARRAY.value:
|
49
|
+
type_definition = {
|
50
|
+
'type': 'array',
|
51
|
+
'control': process_property(f'{field_name}_items', property_data.items), # type: ignore[arg-type]
|
52
|
+
}
|
53
|
+
elif property_data.type == CoreTypes.DICTIONARY.value:
|
54
|
+
if isinstance(property_data.items, LegacyDictSchema):
|
55
|
+
type_definition = {
|
56
|
+
'type': 'dict',
|
57
|
+
'control': process_property(
|
58
|
+
f'{field_name}_items',
|
59
|
+
PropertyData(
|
60
|
+
type=property_data.items.key_type,
|
61
|
+
items=None,
|
62
|
+
title=None,
|
63
|
+
read_only=False,
|
64
|
+
options=None,
|
65
|
+
default=None,
|
66
|
+
field_name=field_name,
|
67
|
+
field_id=None,
|
68
|
+
is_deleted=False,
|
69
|
+
),
|
70
|
+
),
|
71
|
+
}
|
72
|
+
else:
|
73
|
+
type_definition = {
|
74
|
+
'type': 'dict',
|
75
|
+
'control': process_property(
|
76
|
+
f'{field_name}_items',
|
77
|
+
property_data.items.key, # type: ignore[union-attr, arg-type]
|
78
|
+
),
|
79
|
+
}
|
80
|
+
elif property_data.type == 'File':
|
81
|
+
type_definition = {
|
82
|
+
'type': 'file',
|
83
|
+
}
|
84
|
+
else:
|
85
|
+
type_definition = {
|
86
|
+
'type': 'object_latest',
|
87
|
+
'entityType': property_data.type,
|
88
|
+
}
|
89
|
+
|
90
|
+
if getattr(property_data, 'default', None) is not None:
|
91
|
+
type_definition['value'] = property_data.default
|
92
|
+
|
93
|
+
if getattr(property_data, 'options', None) is not None:
|
94
|
+
type_definition['options'] = [
|
95
|
+
{
|
96
|
+
'label': option.key,
|
97
|
+
'value': option.value,
|
98
|
+
}
|
99
|
+
for option in property_data.options # type: ignore[union-attr]
|
100
|
+
]
|
101
|
+
|
102
|
+
return {
|
103
|
+
'name': field_name,
|
104
|
+
'label': property_data.title if hasattr(property_data, 'title') and property_data.title else field_name,
|
105
|
+
**type_definition,
|
106
|
+
}
|
107
|
+
|
108
|
+
|
109
|
+
def populate_frontend_config_with_values(config: dict[str, Any], values: dict[str, Any]) -> dict[str, Any]:
|
110
|
+
"""
|
111
|
+
Populates a frontend configuration dictionary with values.
|
112
|
+
|
113
|
+
This function takes a frontend configuration dictionary and a dictionary of values,
|
114
|
+
and populates the configuration with the corresponding values. It recursively processes
|
115
|
+
nested controls to ensure all values are populated.
|
116
|
+
|
117
|
+
Args:
|
118
|
+
config (dict[str, Any]): The frontend configuration dictionary to be populated.
|
119
|
+
values (dict[str, Any]): The dictionary of values to populate the configuration with.
|
120
|
+
|
121
|
+
Returns:
|
122
|
+
dict[str, Any]: The populated frontend configuration dictionary.
|
123
|
+
"""
|
124
|
+
if config.get('controls') and isinstance(config['controls'], list):
|
125
|
+
for control in config['controls']:
|
126
|
+
populate_frontend_config_with_values(control, values)
|
127
|
+
|
128
|
+
if config.get('name') in values:
|
129
|
+
config['value'] = values[config['name']]
|
130
|
+
|
131
|
+
return config
|
132
|
+
|
133
|
+
|
134
|
+
def get_values_from_response(response: dict[str, Any] | list[dict[str, Any]]) -> dict[str, Any]:
|
135
|
+
"""
|
136
|
+
Extracts values from a response dictionary or list of dictionaries.
|
137
|
+
|
138
|
+
This function processes a response to extract the relevant values. It checks if the response
|
139
|
+
is a dictionary containing a 'rows' key and processes the rows to find the appropriate values.
|
140
|
+
If the response is not in the expected format, it returns an empty dictionary.
|
141
|
+
|
142
|
+
Args:
|
143
|
+
response (dict[str, Any] | list[dict[str, Any]]): The response to extract values from.
|
144
|
+
|
145
|
+
Returns:
|
146
|
+
dict[str, Any]: A dictionary containing the extracted values.
|
147
|
+
"""
|
148
|
+
if not isinstance(response, dict) or 'rows' not in response or not response['rows']:
|
149
|
+
return {}
|
150
|
+
|
151
|
+
for row in response['rows']:
|
152
|
+
if '_metadata' in row and row['_metadata'].get('next_version') is None:
|
153
|
+
return row
|
154
|
+
|
155
|
+
return response['rows'][0]
|
156
|
+
|
157
|
+
|
158
|
+
def get_default_control(class_name: str) -> dict[str, Any]:
|
159
|
+
"""
|
160
|
+
Retrieves the default frontend control configuration for a given class name.
|
161
|
+
|
162
|
+
This function attempts to import a class by its name from various schema types.
|
163
|
+
If the class is found, it converts it to a frontend configuration dictionary
|
164
|
+
and returns it. If the class is not found, it returns an empty dictionary.
|
165
|
+
|
166
|
+
Args:
|
167
|
+
class_name (str): The name of the class to retrieve the default control for.
|
168
|
+
|
169
|
+
Returns:
|
170
|
+
dict[str, Any]: A dictionary representing the frontend control configuration for the given class.
|
171
|
+
"""
|
172
|
+
from amsdal_models.classes.class_manager import ClassManager
|
173
|
+
|
174
|
+
from amsdal.contrib.frontend_configs.conversion import convert_to_frontend_config
|
175
|
+
from amsdal.contrib.frontend_configs.models.frontend_control_config import (
|
176
|
+
FrontendControlConfig, # type: ignore[import-not-found]
|
177
|
+
)
|
178
|
+
from amsdal.models.core.file import File
|
179
|
+
|
180
|
+
target_class = None
|
181
|
+
with contextlib.suppress(AmsdalClassNotFoundError):
|
182
|
+
target_class = ClassManager().import_class(class_name)
|
183
|
+
|
184
|
+
if not target_class:
|
185
|
+
return {}
|
186
|
+
|
187
|
+
if target_class is File:
|
188
|
+
config = {
|
189
|
+
'type': 'group',
|
190
|
+
'name': 'File',
|
191
|
+
'label': 'File',
|
192
|
+
'controls': [
|
193
|
+
{'label': 'Filename', 'name': 'filename', 'type': 'text', 'required': True},
|
194
|
+
{'label': 'Data', 'name': 'data', 'type': 'Bytes', 'required': True},
|
195
|
+
{'label': 'Size', 'name': 'size', 'type': 'number', 'required': False},
|
196
|
+
],
|
197
|
+
}
|
198
|
+
else:
|
199
|
+
config = convert_to_frontend_config(target_class, is_transaction=False)
|
200
|
+
|
201
|
+
return FrontendControlConfig(**config).model_dump(
|
202
|
+
exclude_none=True,
|
203
|
+
)
|
204
|
+
|
205
|
+
|
206
|
+
class ProcessResponseConsumer(LifecycleConsumer):
|
207
|
+
"""
|
208
|
+
Consumer class for processing responses and populating frontend configurations.
|
209
|
+
|
210
|
+
This class extends the LifecycleConsumer and processes responses to populate
|
211
|
+
frontend configurations based on the class name and values extracted from the response.
|
212
|
+
"""
|
213
|
+
|
214
|
+
def on_event(
|
215
|
+
self,
|
216
|
+
request: Any,
|
217
|
+
response: dict[str, Any],
|
218
|
+
) -> None:
|
219
|
+
"""
|
220
|
+
Handles the event by extracting the class name and values from the request and response,
|
221
|
+
and populates the frontend configuration accordingly.
|
222
|
+
|
223
|
+
Args:
|
224
|
+
request (Any): The request object containing query and path parameters.
|
225
|
+
response (dict[str, Any]): The response dictionary to be processed.
|
226
|
+
|
227
|
+
Returns:
|
228
|
+
None
|
229
|
+
"""
|
230
|
+
from amsdal.contrib.frontend_configs.models.frontend_model_config import (
|
231
|
+
FrontendModelConfig, # type: ignore[import-not-found]
|
232
|
+
)
|
233
|
+
|
234
|
+
class_name = None
|
235
|
+
values = {}
|
236
|
+
if hasattr(request, 'query_params') and 'class_name' in request.query_params:
|
237
|
+
class_name = request.query_params['class_name']
|
238
|
+
|
239
|
+
if hasattr(request, 'path_params') and 'address' in request.path_params:
|
240
|
+
class_name = Address.from_string(request.path_params['address']).class_name
|
241
|
+
values = get_values_from_response(response)
|
242
|
+
|
243
|
+
if class_name and isinstance(response, dict):
|
244
|
+
config = (
|
245
|
+
FrontendModelConfig.objects.all()
|
246
|
+
.first(
|
247
|
+
class_name=class_name,
|
248
|
+
_metadata__is_deleted=False,
|
249
|
+
_address__object_version=Versions.LATEST,
|
250
|
+
)
|
251
|
+
.execute()
|
252
|
+
)
|
253
|
+
|
254
|
+
if config and config.control:
|
255
|
+
response['control'] = populate_frontend_config_with_values(
|
256
|
+
config.control.model_dump(exclude_none=True), values
|
257
|
+
)
|
258
|
+
else:
|
259
|
+
response['control'] = populate_frontend_config_with_values(get_default_control(class_name), values)
|
260
|
+
|
261
|
+
async def on_event_async(
|
262
|
+
self,
|
263
|
+
request: Any,
|
264
|
+
response: dict[str, Any],
|
265
|
+
) -> None:
|
266
|
+
"""
|
267
|
+
Handles the event by extracting the class name and values from the request and response,
|
268
|
+
and populates the frontend configuration accordingly.
|
269
|
+
|
270
|
+
Args:
|
271
|
+
request (Any): The request object containing query and path parameters.
|
272
|
+
response (dict[str, Any]): The response dictionary to be processed.
|
273
|
+
|
274
|
+
Returns:
|
275
|
+
None
|
276
|
+
"""
|
277
|
+
from amsdal.contrib.frontend_configs.models.frontend_model_config import (
|
278
|
+
FrontendModelConfig, # type: ignore[import-not-found]
|
279
|
+
)
|
280
|
+
|
281
|
+
class_name = None
|
282
|
+
values = {}
|
283
|
+
if hasattr(request, 'query_params') and 'class_name' in request.query_params:
|
284
|
+
class_name = request.query_params['class_name']
|
285
|
+
|
286
|
+
if hasattr(request, 'path_params') and 'address' in request.path_params:
|
287
|
+
class_name = Address.from_string(request.path_params['address']).class_name
|
288
|
+
values = get_values_from_response(response)
|
289
|
+
|
290
|
+
if class_name and isinstance(response, dict):
|
291
|
+
config = (
|
292
|
+
await FrontendModelConfig.objects.all()
|
293
|
+
.first(
|
294
|
+
class_name=class_name,
|
295
|
+
_metadata__is_deleted=False,
|
296
|
+
_address__object_version=Versions.LATEST,
|
297
|
+
)
|
298
|
+
.aexecute()
|
299
|
+
)
|
300
|
+
|
301
|
+
if config and config.control:
|
302
|
+
response['control'] = populate_frontend_config_with_values(
|
303
|
+
config.control.model_dump(exclude_none=True), values
|
304
|
+
)
|
305
|
+
else:
|
306
|
+
response['control'] = populate_frontend_config_with_values(get_default_control(class_name), values)
|