amsdal 0.4.13__cp311-cp311-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.
Files changed (269) hide show
  1. amsdal/Third-Party Materials - AMSDAL Dependencies - License Notices.md +1334 -0
  2. amsdal/__about__.py +4 -0
  3. amsdal/__about__.pyi +1 -0
  4. amsdal/__init__.py +23 -0
  5. amsdal/__init__.pyi +9 -0
  6. amsdal/__migrations__/0000_initial.py +217 -0
  7. amsdal/__migrations__/0001_datetime_type.py +18 -0
  8. amsdal/__migrations__/0002_fixture_order.py +44 -0
  9. amsdal/__migrations__/0003_schema_type_in_class_meta.py +44 -0
  10. amsdal/cloud/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  11. amsdal/cloud/__init__.pyi +0 -0
  12. amsdal/cloud/client.cpython-311-x86_64-linux-gnu.so +0 -0
  13. amsdal/cloud/client.pyi +57 -0
  14. amsdal/cloud/constants.cpython-311-x86_64-linux-gnu.so +0 -0
  15. amsdal/cloud/constants.pyi +13 -0
  16. amsdal/cloud/enums.cpython-311-x86_64-linux-gnu.so +0 -0
  17. amsdal/cloud/enums.pyi +68 -0
  18. amsdal/cloud/models/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  19. amsdal/cloud/models/__init__.pyi +0 -0
  20. amsdal/cloud/models/base.cpython-311-x86_64-linux-gnu.so +0 -0
  21. amsdal/cloud/models/base.pyi +247 -0
  22. amsdal/cloud/services/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  23. amsdal/cloud/services/__init__.pyi +0 -0
  24. amsdal/cloud/services/actions/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  25. amsdal/cloud/services/actions/__init__.pyi +0 -0
  26. amsdal/cloud/services/actions/add_allowlist_ip.cpython-311-x86_64-linux-gnu.so +0 -0
  27. amsdal/cloud/services/actions/add_allowlist_ip.pyi +19 -0
  28. amsdal/cloud/services/actions/add_basic_auth.cpython-311-x86_64-linux-gnu.so +0 -0
  29. amsdal/cloud/services/actions/add_basic_auth.pyi +21 -0
  30. amsdal/cloud/services/actions/add_dependency.cpython-311-x86_64-linux-gnu.so +0 -0
  31. amsdal/cloud/services/actions/add_dependency.pyi +19 -0
  32. amsdal/cloud/services/actions/add_secret.cpython-311-x86_64-linux-gnu.so +0 -0
  33. amsdal/cloud/services/actions/add_secret.pyi +20 -0
  34. amsdal/cloud/services/actions/base.cpython-311-x86_64-linux-gnu.so +0 -0
  35. amsdal/cloud/services/actions/base.pyi +122 -0
  36. amsdal/cloud/services/actions/create_deploy.cpython-311-x86_64-linux-gnu.so +0 -0
  37. amsdal/cloud/services/actions/create_deploy.pyi +41 -0
  38. amsdal/cloud/services/actions/create_env.cpython-311-x86_64-linux-gnu.so +0 -0
  39. amsdal/cloud/services/actions/create_env.pyi +19 -0
  40. amsdal/cloud/services/actions/create_session.cpython-311-x86_64-linux-gnu.so +0 -0
  41. amsdal/cloud/services/actions/create_session.pyi +17 -0
  42. amsdal/cloud/services/actions/delete_allowlist_ip.cpython-311-x86_64-linux-gnu.so +0 -0
  43. amsdal/cloud/services/actions/delete_allowlist_ip.pyi +19 -0
  44. amsdal/cloud/services/actions/delete_basic_auth.cpython-311-x86_64-linux-gnu.so +0 -0
  45. amsdal/cloud/services/actions/delete_basic_auth.pyi +20 -0
  46. amsdal/cloud/services/actions/delete_dependency.cpython-311-x86_64-linux-gnu.so +0 -0
  47. amsdal/cloud/services/actions/delete_dependency.pyi +21 -0
  48. amsdal/cloud/services/actions/delete_env.cpython-311-x86_64-linux-gnu.so +0 -0
  49. amsdal/cloud/services/actions/delete_env.pyi +21 -0
  50. amsdal/cloud/services/actions/delete_secret.cpython-311-x86_64-linux-gnu.so +0 -0
  51. amsdal/cloud/services/actions/delete_secret.pyi +21 -0
  52. amsdal/cloud/services/actions/destroy_deploy.cpython-311-x86_64-linux-gnu.so +0 -0
  53. amsdal/cloud/services/actions/destroy_deploy.pyi +18 -0
  54. amsdal/cloud/services/actions/expose_db.cpython-311-x86_64-linux-gnu.so +0 -0
  55. amsdal/cloud/services/actions/expose_db.pyi +22 -0
  56. amsdal/cloud/services/actions/get_basic_auth_credentials.cpython-311-x86_64-linux-gnu.so +0 -0
  57. amsdal/cloud/services/actions/get_basic_auth_credentials.pyi +21 -0
  58. amsdal/cloud/services/actions/get_monitoring_info.cpython-311-x86_64-linux-gnu.so +0 -0
  59. amsdal/cloud/services/actions/get_monitoring_info.pyi +21 -0
  60. amsdal/cloud/services/actions/list_dependencies.cpython-311-x86_64-linux-gnu.so +0 -0
  61. amsdal/cloud/services/actions/list_dependencies.pyi +21 -0
  62. amsdal/cloud/services/actions/list_deploys.cpython-311-x86_64-linux-gnu.so +0 -0
  63. amsdal/cloud/services/actions/list_deploys.pyi +19 -0
  64. amsdal/cloud/services/actions/list_envs.cpython-311-x86_64-linux-gnu.so +0 -0
  65. amsdal/cloud/services/actions/list_envs.pyi +20 -0
  66. amsdal/cloud/services/actions/list_secrets.cpython-311-x86_64-linux-gnu.so +0 -0
  67. amsdal/cloud/services/actions/list_secrets.pyi +22 -0
  68. amsdal/cloud/services/actions/manager.cpython-311-x86_64-linux-gnu.so +0 -0
  69. amsdal/cloud/services/actions/manager.pyi +278 -0
  70. amsdal/cloud/services/actions/signup_action.cpython-311-x86_64-linux-gnu.so +0 -0
  71. amsdal/cloud/services/actions/signup_action.pyi +20 -0
  72. amsdal/cloud/services/actions/update_deploy.cpython-311-x86_64-linux-gnu.so +0 -0
  73. amsdal/cloud/services/actions/update_deploy.pyi +19 -0
  74. amsdal/cloud/services/auth/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  75. amsdal/cloud/services/auth/__init__.pyi +0 -0
  76. amsdal/cloud/services/auth/base.cpython-311-x86_64-linux-gnu.so +0 -0
  77. amsdal/cloud/services/auth/base.pyi +6 -0
  78. amsdal/cloud/services/auth/credentials.cpython-311-x86_64-linux-gnu.so +0 -0
  79. amsdal/cloud/services/auth/credentials.pyi +30 -0
  80. amsdal/cloud/services/auth/manager.cpython-311-x86_64-linux-gnu.so +0 -0
  81. amsdal/cloud/services/auth/manager.pyi +26 -0
  82. amsdal/cloud/services/auth/signup_service.cpython-311-x86_64-linux-gnu.so +0 -0
  83. amsdal/cloud/services/auth/signup_service.pyi +32 -0
  84. amsdal/cloud/services/auth/token.cpython-311-x86_64-linux-gnu.so +0 -0
  85. amsdal/cloud/services/auth/token.pyi +27 -0
  86. amsdal/configs/__init__.py +0 -0
  87. amsdal/configs/__init__.pyi +0 -0
  88. amsdal/configs/constants.py +33 -0
  89. amsdal/configs/constants.pyi +22 -0
  90. amsdal/configs/main.py +258 -0
  91. amsdal/configs/main.pyi +173 -0
  92. amsdal/context/__init__.py +0 -0
  93. amsdal/context/__init__.pyi +0 -0
  94. amsdal/context/manager.py +69 -0
  95. amsdal/context/manager.pyi +50 -0
  96. amsdal/contrib/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  97. amsdal/contrib/__init__.pyi +0 -0
  98. amsdal/contrib/app_config.py +7 -0
  99. amsdal/contrib/app_config.pyi +6 -0
  100. amsdal/contrib/auth/__init__.py +0 -0
  101. amsdal/contrib/auth/__init__.pyi +0 -0
  102. amsdal/contrib/auth/app.py +27 -0
  103. amsdal/contrib/auth/app.pyi +15 -0
  104. amsdal/contrib/auth/decorators/__init__.py +35 -0
  105. amsdal/contrib/auth/decorators/__init__.pyi +6 -0
  106. amsdal/contrib/auth/errors.py +7 -0
  107. amsdal/contrib/auth/errors.pyi +4 -0
  108. amsdal/contrib/auth/fixtures/basic_permissions.json +64 -0
  109. amsdal/contrib/auth/lifecycle/__init__.py +0 -0
  110. amsdal/contrib/auth/lifecycle/__init__.pyi +0 -0
  111. amsdal/contrib/auth/lifecycle/consumer.py +394 -0
  112. amsdal/contrib/auth/lifecycle/consumer.pyi +108 -0
  113. amsdal/contrib/auth/migrations/0000_initial.py +84 -0
  114. amsdal/contrib/auth/models/__init__.py +0 -0
  115. amsdal/contrib/auth/models/__init__.pyi +0 -0
  116. amsdal/contrib/auth/models/login_session.py +118 -0
  117. amsdal/contrib/auth/models/login_session.pyi +37 -0
  118. amsdal/contrib/auth/models/permission.py +23 -0
  119. amsdal/contrib/auth/models/permission.pyi +18 -0
  120. amsdal/contrib/auth/models/user.py +106 -0
  121. amsdal/contrib/auth/models/user.pyi +46 -0
  122. amsdal/contrib/auth/settings.py +36 -0
  123. amsdal/contrib/auth/settings.pyi +26 -0
  124. amsdal/contrib/frontend_configs/__init__.py +0 -0
  125. amsdal/contrib/frontend_configs/__init__.pyi +0 -0
  126. amsdal/contrib/frontend_configs/app.py +24 -0
  127. amsdal/contrib/frontend_configs/app.pyi +19 -0
  128. amsdal/contrib/frontend_configs/constants.py +1 -0
  129. amsdal/contrib/frontend_configs/constants.pyi +1 -0
  130. amsdal/contrib/frontend_configs/conversion/__init__.py +5 -0
  131. amsdal/contrib/frontend_configs/conversion/__init__.pyi +3 -0
  132. amsdal/contrib/frontend_configs/conversion/convert.py +286 -0
  133. amsdal/contrib/frontend_configs/conversion/convert.pyi +22 -0
  134. amsdal/contrib/frontend_configs/lifecycle/__init__.py +0 -0
  135. amsdal/contrib/frontend_configs/lifecycle/__init__.pyi +0 -0
  136. amsdal/contrib/frontend_configs/lifecycle/consumer.py +306 -0
  137. amsdal/contrib/frontend_configs/lifecycle/consumer.pyi +98 -0
  138. amsdal/contrib/frontend_configs/migrations/0000_initial.py +256 -0
  139. amsdal/contrib/frontend_configs/models/__init__.py +0 -0
  140. amsdal/contrib/frontend_configs/models/__init__.pyi +0 -0
  141. amsdal/contrib/frontend_configs/models/frontend_activator_config.py +22 -0
  142. amsdal/contrib/frontend_configs/models/frontend_activator_config.pyi +12 -0
  143. amsdal/contrib/frontend_configs/models/frontend_config_async_validator.py +11 -0
  144. amsdal/contrib/frontend_configs/models/frontend_config_async_validator.pyi +7 -0
  145. amsdal/contrib/frontend_configs/models/frontend_config_control_action.py +54 -0
  146. amsdal/contrib/frontend_configs/models/frontend_config_control_action.pyi +32 -0
  147. amsdal/contrib/frontend_configs/models/frontend_config_group_validator.py +21 -0
  148. amsdal/contrib/frontend_configs/models/frontend_config_group_validator.pyi +11 -0
  149. amsdal/contrib/frontend_configs/models/frontend_config_option.py +12 -0
  150. amsdal/contrib/frontend_configs/models/frontend_config_option.pyi +8 -0
  151. amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.py +17 -0
  152. amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.pyi +8 -0
  153. amsdal/contrib/frontend_configs/models/frontend_config_slider_option.py +13 -0
  154. amsdal/contrib/frontend_configs/models/frontend_config_slider_option.pyi +9 -0
  155. amsdal/contrib/frontend_configs/models/frontend_config_text_mask.py +14 -0
  156. amsdal/contrib/frontend_configs/models/frontend_config_text_mask.pyi +10 -0
  157. amsdal/contrib/frontend_configs/models/frontend_config_validator.py +28 -0
  158. amsdal/contrib/frontend_configs/models/frontend_config_validator.pyi +15 -0
  159. amsdal/contrib/frontend_configs/models/frontend_control_config.py +87 -0
  160. amsdal/contrib/frontend_configs/models/frontend_control_config.pyi +35 -0
  161. amsdal/contrib/frontend_configs/models/frontend_model_config.py +14 -0
  162. amsdal/contrib/frontend_configs/models/frontend_model_config.pyi +9 -0
  163. amsdal/contrib/frontend_configs/utils.py +29 -0
  164. amsdal/contrib/frontend_configs/utils.pyi +17 -0
  165. amsdal/errors.py +31 -0
  166. amsdal/errors.pyi +12 -0
  167. amsdal/fixtures/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  168. amsdal/fixtures/__init__.pyi +0 -0
  169. amsdal/fixtures/manager.cpython-311-x86_64-linux-gnu.so +0 -0
  170. amsdal/fixtures/manager.pyi +170 -0
  171. amsdal/fixtures/utils.cpython-311-x86_64-linux-gnu.so +0 -0
  172. amsdal/fixtures/utils.pyi +9 -0
  173. amsdal/manager.cpython-311-x86_64-linux-gnu.so +0 -0
  174. amsdal/manager.pyi +265 -0
  175. amsdal/mixins/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  176. amsdal/mixins/__init__.pyi +0 -0
  177. amsdal/mixins/class_versions_mixin.cpython-311-x86_64-linux-gnu.so +0 -0
  178. amsdal/mixins/class_versions_mixin.pyi +12 -0
  179. amsdal/models/__init__.py +19 -0
  180. amsdal/models/__init__.pyi +9 -0
  181. amsdal/models/core/__init__.py +0 -0
  182. amsdal/models/core/__init__.pyi +0 -0
  183. amsdal/models/core/class_object.py +37 -0
  184. amsdal/models/core/class_object.pyi +24 -0
  185. amsdal/models/core/class_object_meta.py +26 -0
  186. amsdal/models/core/class_object_meta.pyi +15 -0
  187. amsdal/models/core/class_property.py +20 -0
  188. amsdal/models/core/class_property.pyi +11 -0
  189. amsdal/models/core/class_property_meta.py +15 -0
  190. amsdal/models/core/class_property_meta.pyi +10 -0
  191. amsdal/models/core/file.py +156 -0
  192. amsdal/models/core/file.pyi +104 -0
  193. amsdal/models/core/fixture.py +25 -0
  194. amsdal/models/core/fixture.pyi +14 -0
  195. amsdal/models/core/option.py +11 -0
  196. amsdal/models/core/option.pyi +8 -0
  197. amsdal/models/core/validator.py +12 -0
  198. amsdal/models/core/validator.pyi +8 -0
  199. amsdal/models/types/__init__.py +0 -0
  200. amsdal/models/types/__init__.pyi +0 -0
  201. amsdal/models/types/object.py +26 -0
  202. amsdal/models/types/object.pyi +16 -0
  203. amsdal/py.typed +0 -0
  204. amsdal/queryset/__init__.py +21 -0
  205. amsdal/queryset/__init__.pyi +6 -0
  206. amsdal/schemas/__init__.py +0 -0
  207. amsdal/schemas/__init__.pyi +0 -0
  208. amsdal/schemas/core/class_object/model.json +31 -0
  209. amsdal/schemas/core/class_object/properties/display_name.py +9 -0
  210. amsdal/schemas/core/class_object_meta/model.json +59 -0
  211. amsdal/schemas/core/class_property/model.json +22 -0
  212. amsdal/schemas/core/class_property_meta/model.json +23 -0
  213. amsdal/schemas/core/file/hooks/pre_create.py +24 -0
  214. amsdal/schemas/core/file/hooks/pre_update.py +24 -0
  215. amsdal/schemas/core/file/model.json +23 -0
  216. amsdal/schemas/core/file/properties/from_file.py +34 -0
  217. amsdal/schemas/core/file/properties/mimetype.py +13 -0
  218. amsdal/schemas/core/file/properties/str.py +6 -0
  219. amsdal/schemas/core/file/properties/to_file.py +24 -0
  220. amsdal/schemas/core/file/properties/validate_data.py +32 -0
  221. amsdal/schemas/core/fixture/model.json +35 -0
  222. amsdal/schemas/core/option/model.json +19 -0
  223. amsdal/schemas/core/validator/model.json +19 -0
  224. amsdal/schemas/interfaces.py +25 -0
  225. amsdal/schemas/interfaces.pyi +20 -0
  226. amsdal/schemas/manager.cpython-311-x86_64-linux-gnu.so +0 -0
  227. amsdal/schemas/manager.py +0 -0
  228. amsdal/schemas/manager.pyi +0 -0
  229. amsdal/schemas/mixins/__init__.py +0 -0
  230. amsdal/schemas/mixins/__init__.pyi +0 -0
  231. amsdal/schemas/mixins/check_dependencies_mixin.py +125 -0
  232. amsdal/schemas/mixins/check_dependencies_mixin.pyi +45 -0
  233. amsdal/schemas/mixins/verify_schemas_mixin.py +96 -0
  234. amsdal/schemas/mixins/verify_schemas_mixin.pyi +33 -0
  235. amsdal/schemas/repository.py +84 -0
  236. amsdal/schemas/repository.pyi +22 -0
  237. amsdal/schemas/types/anything/model.json +7 -0
  238. amsdal/schemas/types/array/model.json +7 -0
  239. amsdal/schemas/types/binary/model.json +7 -0
  240. amsdal/schemas/types/boolean/model.json +17 -0
  241. amsdal/schemas/types/date/model.json +7 -0
  242. amsdal/schemas/types/datetime/model.json +7 -0
  243. amsdal/schemas/types/dictionary/model.json +8 -0
  244. amsdal/schemas/types/number/model.json +8 -0
  245. amsdal/schemas/types/object/model.json +53 -0
  246. amsdal/schemas/types/string/model.json +8 -0
  247. amsdal/schemas/utils.py +16 -0
  248. amsdal/schemas/utils.pyi +10 -0
  249. amsdal/services/__init__.cpython-311-x86_64-linux-gnu.so +0 -0
  250. amsdal/services/__init__.pyi +0 -0
  251. amsdal/services/transaction_execution.cpython-311-x86_64-linux-gnu.so +0 -0
  252. amsdal/services/transaction_execution.pyi +93 -0
  253. amsdal/transactions/__init__.py +13 -0
  254. amsdal/transactions/__init__.pyi +4 -0
  255. amsdal/utils/__init__.py +0 -0
  256. amsdal/utils/__init__.pyi +0 -0
  257. amsdal/utils/contrib_paths.py +23 -0
  258. amsdal/utils/contrib_paths.pyi +14 -0
  259. amsdal/utils/rollback/__init__.py +440 -0
  260. amsdal/utils/rollback/__init__.pyi +38 -0
  261. amsdal/utils/tests/__init__.py +0 -0
  262. amsdal/utils/tests/enums.py +18 -0
  263. amsdal/utils/tests/factories.py +49 -0
  264. amsdal/utils/tests/helpers.py +499 -0
  265. amsdal-0.4.13.dist-info/METADATA +369 -0
  266. amsdal-0.4.13.dist-info/RECORD +269 -0
  267. amsdal-0.4.13.dist-info/WHEEL +6 -0
  268. amsdal-0.4.13.dist-info/licenses/LICENSE.txt +107 -0
  269. amsdal-0.4.13.dist-info/top_level.txt +1 -0
@@ -0,0 +1 @@
1
+ ON_RESPONSE_EVENT: str
@@ -0,0 +1,5 @@
1
+ from amsdal.contrib.frontend_configs.conversion.convert import convert_to_frontend_config
2
+
3
+ __all__ = [
4
+ 'convert_to_frontend_config',
5
+ ]
@@ -0,0 +1,3 @@
1
+ from amsdal.contrib.frontend_configs.conversion.convert import convert_to_frontend_config as convert_to_frontend_config
2
+
3
+ __all__ = ['convert_to_frontend_config']
@@ -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)