amsdal 0.5.23__cp311-cp311-macosx_10_9_universal2.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.

Potentially problematic release.


This version of amsdal might be problematic. Click here for more details.

Files changed (252) 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 +36 -0
  7. amsdal/__migrations__/0001_create_class_file.py +61 -0
  8. amsdal/__migrations__/0002_create_class_file.py +109 -0
  9. amsdal/__migrations__/0003_update_class_file.py +91 -0
  10. amsdal/__migrations__/0004_update_class_file.py +45 -0
  11. amsdal/cloud/__init__.cpython-311-darwin.so +0 -0
  12. amsdal/cloud/__init__.pyi +0 -0
  13. amsdal/cloud/client.cpython-311-darwin.so +0 -0
  14. amsdal/cloud/client.pyi +57 -0
  15. amsdal/cloud/constants.cpython-311-darwin.so +0 -0
  16. amsdal/cloud/constants.pyi +13 -0
  17. amsdal/cloud/enums.cpython-311-darwin.so +0 -0
  18. amsdal/cloud/enums.pyi +68 -0
  19. amsdal/cloud/models/__init__.cpython-311-darwin.so +0 -0
  20. amsdal/cloud/models/__init__.pyi +0 -0
  21. amsdal/cloud/models/base.cpython-311-darwin.so +0 -0
  22. amsdal/cloud/models/base.pyi +247 -0
  23. amsdal/cloud/services/__init__.cpython-311-darwin.so +0 -0
  24. amsdal/cloud/services/__init__.pyi +0 -0
  25. amsdal/cloud/services/actions/__init__.cpython-311-darwin.so +0 -0
  26. amsdal/cloud/services/actions/__init__.pyi +0 -0
  27. amsdal/cloud/services/actions/add_allowlist_ip.cpython-311-darwin.so +0 -0
  28. amsdal/cloud/services/actions/add_allowlist_ip.pyi +19 -0
  29. amsdal/cloud/services/actions/add_basic_auth.cpython-311-darwin.so +0 -0
  30. amsdal/cloud/services/actions/add_basic_auth.pyi +21 -0
  31. amsdal/cloud/services/actions/add_dependency.cpython-311-darwin.so +0 -0
  32. amsdal/cloud/services/actions/add_dependency.pyi +19 -0
  33. amsdal/cloud/services/actions/add_secret.cpython-311-darwin.so +0 -0
  34. amsdal/cloud/services/actions/add_secret.pyi +20 -0
  35. amsdal/cloud/services/actions/base.cpython-311-darwin.so +0 -0
  36. amsdal/cloud/services/actions/base.pyi +122 -0
  37. amsdal/cloud/services/actions/create_deploy.cpython-311-darwin.so +0 -0
  38. amsdal/cloud/services/actions/create_deploy.pyi +41 -0
  39. amsdal/cloud/services/actions/create_env.cpython-311-darwin.so +0 -0
  40. amsdal/cloud/services/actions/create_env.pyi +19 -0
  41. amsdal/cloud/services/actions/create_session.cpython-311-darwin.so +0 -0
  42. amsdal/cloud/services/actions/create_session.pyi +17 -0
  43. amsdal/cloud/services/actions/delete_allowlist_ip.cpython-311-darwin.so +0 -0
  44. amsdal/cloud/services/actions/delete_allowlist_ip.pyi +19 -0
  45. amsdal/cloud/services/actions/delete_basic_auth.cpython-311-darwin.so +0 -0
  46. amsdal/cloud/services/actions/delete_basic_auth.pyi +20 -0
  47. amsdal/cloud/services/actions/delete_dependency.cpython-311-darwin.so +0 -0
  48. amsdal/cloud/services/actions/delete_dependency.pyi +21 -0
  49. amsdal/cloud/services/actions/delete_env.cpython-311-darwin.so +0 -0
  50. amsdal/cloud/services/actions/delete_env.pyi +21 -0
  51. amsdal/cloud/services/actions/delete_secret.cpython-311-darwin.so +0 -0
  52. amsdal/cloud/services/actions/delete_secret.pyi +21 -0
  53. amsdal/cloud/services/actions/destroy_deploy.cpython-311-darwin.so +0 -0
  54. amsdal/cloud/services/actions/destroy_deploy.pyi +18 -0
  55. amsdal/cloud/services/actions/expose_db.cpython-311-darwin.so +0 -0
  56. amsdal/cloud/services/actions/expose_db.pyi +22 -0
  57. amsdal/cloud/services/actions/get_basic_auth_credentials.cpython-311-darwin.so +0 -0
  58. amsdal/cloud/services/actions/get_basic_auth_credentials.pyi +21 -0
  59. amsdal/cloud/services/actions/get_monitoring_info.cpython-311-darwin.so +0 -0
  60. amsdal/cloud/services/actions/get_monitoring_info.pyi +21 -0
  61. amsdal/cloud/services/actions/list_dependencies.cpython-311-darwin.so +0 -0
  62. amsdal/cloud/services/actions/list_dependencies.pyi +21 -0
  63. amsdal/cloud/services/actions/list_deploys.cpython-311-darwin.so +0 -0
  64. amsdal/cloud/services/actions/list_deploys.pyi +19 -0
  65. amsdal/cloud/services/actions/list_envs.cpython-311-darwin.so +0 -0
  66. amsdal/cloud/services/actions/list_envs.pyi +20 -0
  67. amsdal/cloud/services/actions/list_secrets.cpython-311-darwin.so +0 -0
  68. amsdal/cloud/services/actions/list_secrets.pyi +22 -0
  69. amsdal/cloud/services/actions/manager.cpython-311-darwin.so +0 -0
  70. amsdal/cloud/services/actions/manager.pyi +278 -0
  71. amsdal/cloud/services/actions/signup_action.cpython-311-darwin.so +0 -0
  72. amsdal/cloud/services/actions/signup_action.pyi +20 -0
  73. amsdal/cloud/services/actions/update_deploy.cpython-311-darwin.so +0 -0
  74. amsdal/cloud/services/actions/update_deploy.pyi +19 -0
  75. amsdal/cloud/services/auth/__init__.cpython-311-darwin.so +0 -0
  76. amsdal/cloud/services/auth/__init__.pyi +0 -0
  77. amsdal/cloud/services/auth/base.cpython-311-darwin.so +0 -0
  78. amsdal/cloud/services/auth/base.pyi +6 -0
  79. amsdal/cloud/services/auth/credentials.cpython-311-darwin.so +0 -0
  80. amsdal/cloud/services/auth/credentials.pyi +30 -0
  81. amsdal/cloud/services/auth/manager.cpython-311-darwin.so +0 -0
  82. amsdal/cloud/services/auth/manager.pyi +26 -0
  83. amsdal/cloud/services/auth/signup_service.cpython-311-darwin.so +0 -0
  84. amsdal/cloud/services/auth/signup_service.pyi +32 -0
  85. amsdal/cloud/services/auth/token.cpython-311-darwin.so +0 -0
  86. amsdal/cloud/services/auth/token.pyi +27 -0
  87. amsdal/configs/__init__.py +0 -0
  88. amsdal/configs/__init__.pyi +0 -0
  89. amsdal/configs/constants.py +33 -0
  90. amsdal/configs/constants.pyi +22 -0
  91. amsdal/configs/main.py +274 -0
  92. amsdal/configs/main.pyi +178 -0
  93. amsdal/context/__init__.py +0 -0
  94. amsdal/context/__init__.pyi +0 -0
  95. amsdal/context/manager.py +69 -0
  96. amsdal/context/manager.pyi +50 -0
  97. amsdal/contrib/__init__.cpython-311-darwin.so +0 -0
  98. amsdal/contrib/__init__.pyi +0 -0
  99. amsdal/contrib/app_config.py +7 -0
  100. amsdal/contrib/app_config.pyi +6 -0
  101. amsdal/contrib/auth/__init__.py +0 -0
  102. amsdal/contrib/auth/__init__.pyi +0 -0
  103. amsdal/contrib/auth/app.py +27 -0
  104. amsdal/contrib/auth/app.pyi +15 -0
  105. amsdal/contrib/auth/decorators/__init__.py +35 -0
  106. amsdal/contrib/auth/decorators/__init__.pyi +6 -0
  107. amsdal/contrib/auth/errors.py +7 -0
  108. amsdal/contrib/auth/errors.pyi +4 -0
  109. amsdal/contrib/auth/fixtures/basic_permissions.json +64 -0
  110. amsdal/contrib/auth/lifecycle/__init__.py +0 -0
  111. amsdal/contrib/auth/lifecycle/__init__.pyi +0 -0
  112. amsdal/contrib/auth/lifecycle/consumer.py +394 -0
  113. amsdal/contrib/auth/lifecycle/consumer.pyi +108 -0
  114. amsdal/contrib/auth/migrations/0000_initial.py +87 -0
  115. amsdal/contrib/auth/models/__init__.py +0 -0
  116. amsdal/contrib/auth/models/login_session.py +118 -0
  117. amsdal/contrib/auth/models/permission.py +23 -0
  118. amsdal/contrib/auth/models/user.py +106 -0
  119. amsdal/contrib/auth/settings.py +36 -0
  120. amsdal/contrib/auth/settings.pyi +26 -0
  121. amsdal/contrib/frontend_configs/__init__.py +0 -0
  122. amsdal/contrib/frontend_configs/__init__.pyi +0 -0
  123. amsdal/contrib/frontend_configs/app.py +24 -0
  124. amsdal/contrib/frontend_configs/app.pyi +19 -0
  125. amsdal/contrib/frontend_configs/constants.py +1 -0
  126. amsdal/contrib/frontend_configs/constants.pyi +1 -0
  127. amsdal/contrib/frontend_configs/conversion/__init__.py +5 -0
  128. amsdal/contrib/frontend_configs/conversion/__init__.pyi +3 -0
  129. amsdal/contrib/frontend_configs/conversion/convert.py +310 -0
  130. amsdal/contrib/frontend_configs/conversion/convert.pyi +22 -0
  131. amsdal/contrib/frontend_configs/lifecycle/__init__.py +0 -0
  132. amsdal/contrib/frontend_configs/lifecycle/__init__.pyi +0 -0
  133. amsdal/contrib/frontend_configs/lifecycle/consumer.py +306 -0
  134. amsdal/contrib/frontend_configs/lifecycle/consumer.pyi +98 -0
  135. amsdal/contrib/frontend_configs/migrations/0000_initial.py +227 -0
  136. amsdal/contrib/frontend_configs/migrations/0001_update_frontend_control_config.py +245 -0
  137. amsdal/contrib/frontend_configs/migrations/0002_add_button_and_invoke_actions.py +352 -0
  138. amsdal/contrib/frontend_configs/models/__init__.py +0 -0
  139. amsdal/contrib/frontend_configs/models/frontend_activator_config.py +22 -0
  140. amsdal/contrib/frontend_configs/models/frontend_config_async_validator.py +11 -0
  141. amsdal/contrib/frontend_configs/models/frontend_config_control_action.py +110 -0
  142. amsdal/contrib/frontend_configs/models/frontend_config_group_validator.py +21 -0
  143. amsdal/contrib/frontend_configs/models/frontend_config_option.py +12 -0
  144. amsdal/contrib/frontend_configs/models/frontend_config_skip_none_base.py +17 -0
  145. amsdal/contrib/frontend_configs/models/frontend_config_slider_option.py +13 -0
  146. amsdal/contrib/frontend_configs/models/frontend_config_text_mask.py +14 -0
  147. amsdal/contrib/frontend_configs/models/frontend_config_validator.py +28 -0
  148. amsdal/contrib/frontend_configs/models/frontend_control_config.py +108 -0
  149. amsdal/contrib/frontend_configs/models/frontend_model_config.py +14 -0
  150. amsdal/contrib/frontend_configs/utils.py +29 -0
  151. amsdal/contrib/frontend_configs/utils.pyi +17 -0
  152. amsdal/errors.py +31 -0
  153. amsdal/errors.pyi +12 -0
  154. amsdal/fixtures/__init__.cpython-311-darwin.so +0 -0
  155. amsdal/fixtures/__init__.pyi +0 -0
  156. amsdal/fixtures/manager.cpython-311-darwin.so +0 -0
  157. amsdal/fixtures/manager.pyi +170 -0
  158. amsdal/fixtures/utils.cpython-311-darwin.so +0 -0
  159. amsdal/fixtures/utils.pyi +9 -0
  160. amsdal/manager.cpython-311-darwin.so +0 -0
  161. amsdal/manager.pyi +265 -0
  162. amsdal/mixins/__init__.cpython-311-darwin.so +0 -0
  163. amsdal/mixins/__init__.pyi +0 -0
  164. amsdal/mixins/class_versions_mixin.cpython-311-darwin.so +0 -0
  165. amsdal/mixins/class_versions_mixin.pyi +12 -0
  166. amsdal/models/__init__.py +19 -0
  167. amsdal/models/core/__init__.py +0 -0
  168. amsdal/models/core/class_object.py +38 -0
  169. amsdal/models/core/class_property.py +26 -0
  170. amsdal/models/core/file.py +243 -0
  171. amsdal/models/core/fixture.py +25 -0
  172. amsdal/models/core/option.py +11 -0
  173. amsdal/models/core/storage_metadata.py +15 -0
  174. amsdal/models/core/validator.py +12 -0
  175. amsdal/models/mixins.py +31 -0
  176. amsdal/models/types/__init__.py +0 -0
  177. amsdal/models/types/object.py +26 -0
  178. amsdal/py.typed +0 -0
  179. amsdal/queryset/__init__.py +21 -0
  180. amsdal/queryset/__init__.pyi +6 -0
  181. amsdal/schemas/__init__.py +0 -0
  182. amsdal/schemas/__init__.pyi +0 -0
  183. amsdal/schemas/core/class_object/model.json +51 -0
  184. amsdal/schemas/core/class_object/properties/display_name.py +9 -0
  185. amsdal/schemas/core/class_property/model.json +41 -0
  186. amsdal/schemas/core/file/hooks/pre_create.py +24 -0
  187. amsdal/schemas/core/file/hooks/pre_update.py +24 -0
  188. amsdal/schemas/core/file/model.json +23 -0
  189. amsdal/schemas/core/file/properties/from_file.py +34 -0
  190. amsdal/schemas/core/file/properties/mimetype.py +13 -0
  191. amsdal/schemas/core/file/properties/str.py +6 -0
  192. amsdal/schemas/core/file/properties/to_file.py +24 -0
  193. amsdal/schemas/core/file/properties/validate_data.py +31 -0
  194. amsdal/schemas/core/fixture/model.json +35 -0
  195. amsdal/schemas/core/option/model.json +19 -0
  196. amsdal/schemas/core/storage_metadata/model.json +52 -0
  197. amsdal/schemas/core/validator/model.json +19 -0
  198. amsdal/schemas/interfaces.py +25 -0
  199. amsdal/schemas/interfaces.pyi +20 -0
  200. amsdal/schemas/manager.cpython-311-darwin.so +0 -0
  201. amsdal/schemas/manager.py +0 -0
  202. amsdal/schemas/manager.pyi +0 -0
  203. amsdal/schemas/mixins/__init__.py +0 -0
  204. amsdal/schemas/mixins/__init__.pyi +0 -0
  205. amsdal/schemas/mixins/check_dependencies_mixin.py +130 -0
  206. amsdal/schemas/mixins/check_dependencies_mixin.pyi +45 -0
  207. amsdal/schemas/mixins/verify_schemas_mixin.py +96 -0
  208. amsdal/schemas/mixins/verify_schemas_mixin.pyi +33 -0
  209. amsdal/schemas/repository.py +84 -0
  210. amsdal/schemas/repository.pyi +22 -0
  211. amsdal/schemas/types/anything/model.json +7 -0
  212. amsdal/schemas/types/array/model.json +7 -0
  213. amsdal/schemas/types/binary/model.json +7 -0
  214. amsdal/schemas/types/boolean/model.json +17 -0
  215. amsdal/schemas/types/date/model.json +7 -0
  216. amsdal/schemas/types/datetime/model.json +7 -0
  217. amsdal/schemas/types/dictionary/model.json +8 -0
  218. amsdal/schemas/types/number/model.json +8 -0
  219. amsdal/schemas/types/object/model.json +53 -0
  220. amsdal/schemas/types/string/model.json +8 -0
  221. amsdal/schemas/utils.py +16 -0
  222. amsdal/schemas/utils.pyi +10 -0
  223. amsdal/services/__init__.py +11 -0
  224. amsdal/services/__init__.pyi +4 -0
  225. amsdal/services/external_connections.py +262 -0
  226. amsdal/services/external_connections.pyi +190 -0
  227. amsdal/services/external_model_generator.py +350 -0
  228. amsdal/services/external_model_generator.pyi +134 -0
  229. amsdal/services/transaction_execution.cpython-311-darwin.so +0 -0
  230. amsdal/services/transaction_execution.pyi +93 -0
  231. amsdal/storages/__init__.py +20 -0
  232. amsdal/storages/__init__.pyi +8 -0
  233. amsdal/storages/file_system.py +214 -0
  234. amsdal/storages/file_system.pyi +36 -0
  235. amsdal/transactions/__init__.py +13 -0
  236. amsdal/transactions/__init__.pyi +4 -0
  237. amsdal/utils/__init__.py +0 -0
  238. amsdal/utils/__init__.pyi +0 -0
  239. amsdal/utils/contrib_paths.py +23 -0
  240. amsdal/utils/contrib_paths.pyi +14 -0
  241. amsdal/utils/rollback/__init__.py +440 -0
  242. amsdal/utils/rollback/__init__.pyi +38 -0
  243. amsdal/utils/tests/__init__.py +0 -0
  244. amsdal/utils/tests/enums.py +16 -0
  245. amsdal/utils/tests/factories.py +49 -0
  246. amsdal/utils/tests/helpers.py +331 -0
  247. amsdal/utils/tests/migrations.py +157 -0
  248. amsdal-0.5.23.dist-info/METADATA +373 -0
  249. amsdal-0.5.23.dist-info/RECORD +252 -0
  250. amsdal-0.5.23.dist-info/WHEEL +5 -0
  251. amsdal-0.5.23.dist-info/licenses/LICENSE.txt +107 -0
  252. amsdal-0.5.23.dist-info/top_level.txt +1 -0
@@ -0,0 +1,190 @@
1
+ from _typeshed import Incomplete
2
+ from amsdal_data.application import AsyncDataApplication, DataApplication
3
+ from amsdal_utils.utils.singleton import Singleton
4
+ from typing import Any, TypeVar
5
+
6
+ T = TypeVar('T')
7
+
8
+ class ExternalConnectionManager(metaclass=Singleton):
9
+ """
10
+ Manager for accessing external service connections.
11
+
12
+ Provides a convenient interface to access external connections configured
13
+ in the application, such as read-only databases, email services, etc.
14
+
15
+ Example usage:
16
+ manager = ExternalConnectionManager()
17
+
18
+ # Get read-only database connection
19
+ external_db = manager.get_connection('external_users_db')
20
+ rows = external_db.fetch_all('SELECT * FROM users WHERE active = 1')
21
+
22
+ # Get email service
23
+ email = manager.get_connection('email_service')
24
+ email.send_email(...)
25
+ """
26
+ _data_application: DataApplication | None
27
+ _async_data_application: AsyncDataApplication | None
28
+ def __init__(self) -> None: ...
29
+ def setup(self, data_application: DataApplication | None = None, async_data_application: AsyncDataApplication | None = None) -> None:
30
+ """
31
+ Set up the manager with the data application instance.
32
+
33
+ Args:
34
+ data_application: Sync DataApplication instance
35
+ async_data_application: Async DataApplication instance
36
+ """
37
+ def get_connection(self, name: str) -> Any:
38
+ """
39
+ Get an external service connection by name.
40
+
41
+ Args:
42
+ name: Name of the external connection (as configured in resources)
43
+
44
+ Returns:
45
+ The external connection object
46
+
47
+ Raises:
48
+ RuntimeError: If manager is not set up
49
+ KeyError: If connection not found
50
+ """
51
+ def has_connection(self, name: str) -> bool:
52
+ """
53
+ Check if an external connection exists.
54
+
55
+ Args:
56
+ name: Name of the external connection
57
+
58
+ Returns:
59
+ bool: True if connection exists, False otherwise
60
+ """
61
+ def list_connections(self) -> list[str]:
62
+ """
63
+ List all available external connection names.
64
+
65
+ Returns:
66
+ list[str]: List of connection names
67
+ """
68
+
69
+ class ExternalDatabaseReader:
70
+ """
71
+ Helper class for reading from external read-only databases.
72
+
73
+ Provides a convenient interface for querying external databases
74
+ with common patterns like filtering, mapping results, etc.
75
+
76
+ Example usage:
77
+ reader = ExternalDatabaseReader('external_users_db')
78
+
79
+ # Fetch all users
80
+ users = reader.fetch_all('SELECT * FROM users')
81
+
82
+ # Fetch with parameters
83
+ active_users = reader.fetch_all(
84
+ 'SELECT * FROM users WHERE active = ?',
85
+ (1,)
86
+ )
87
+
88
+ # Fetch one record
89
+ user = reader.fetch_one('SELECT * FROM users WHERE id = ?', (user_id,))
90
+
91
+ # Get as dictionaries
92
+ user_dicts = reader.fetch_all_as_dicts('SELECT * FROM users LIMIT 10')
93
+ """
94
+ connection_name: Incomplete
95
+ _manager: Incomplete
96
+ def __init__(self, connection_name: str) -> None:
97
+ """
98
+ Initialize the reader with a connection name.
99
+
100
+ Args:
101
+ connection_name: Name of the external database connection
102
+ """
103
+ @property
104
+ def connection(self) -> Any:
105
+ """Get the underlying connection object."""
106
+ def fetch_all(self, query: str, parameters: tuple[Any, ...] | None = None) -> list[Any]:
107
+ """
108
+ Execute query and fetch all results.
109
+
110
+ Args:
111
+ query: SQL query to execute
112
+ parameters: Query parameters (optional)
113
+
114
+ Returns:
115
+ list: List of result rows
116
+ """
117
+ def fetch_one(self, query: str, parameters: tuple[Any, ...] | None = None) -> Any | None:
118
+ """
119
+ Execute query and fetch one result.
120
+
121
+ Args:
122
+ query: SQL query to execute
123
+ parameters: Query parameters (optional)
124
+
125
+ Returns:
126
+ Single result row or None
127
+ """
128
+ def fetch_all_as_dicts(self, query: str, parameters: tuple[Any, ...] | None = None) -> list[dict[str, Any]]:
129
+ """
130
+ Execute query and fetch all results as dictionaries.
131
+
132
+ Args:
133
+ query: SQL query to execute
134
+ parameters: Query parameters (optional)
135
+
136
+ Returns:
137
+ list[dict]: List of result dictionaries
138
+ """
139
+ def fetch_one_as_dict(self, query: str, parameters: tuple[Any, ...] | None = None) -> dict[str, Any] | None:
140
+ """
141
+ Execute query and fetch one result as dictionary.
142
+
143
+ Args:
144
+ query: SQL query to execute
145
+ parameters: Query parameters (optional)
146
+
147
+ Returns:
148
+ dict | None: Result dictionary or None
149
+ """
150
+ def get_table_names(self) -> list[str]:
151
+ """
152
+ Get list of all tables in the database.
153
+
154
+ Returns:
155
+ list[str]: List of table names
156
+ """
157
+ def get_table_schema(self, table_name: str) -> list[dict[str, Any]]:
158
+ """
159
+ Get schema information for a table.
160
+
161
+ Args:
162
+ table_name: Name of the table
163
+
164
+ Returns:
165
+ list[dict]: List of column information dictionaries
166
+ """
167
+ def count(self, table: str, where_clause: str = '', parameters: tuple[Any, ...] | None = None) -> int:
168
+ """
169
+ Count rows in a table.
170
+
171
+ Args:
172
+ table: Table name
173
+ where_clause: Optional WHERE clause (without WHERE keyword)
174
+ parameters: Query parameters for WHERE clause
175
+
176
+ Returns:
177
+ int: Number of rows
178
+ """
179
+ def exists(self, table: str, where_clause: str, parameters: tuple[Any, ...]) -> bool:
180
+ """
181
+ Check if a record exists.
182
+
183
+ Args:
184
+ table: Table name
185
+ where_clause: WHERE clause (without WHERE keyword)
186
+ parameters: Query parameters
187
+
188
+ Returns:
189
+ bool: True if record exists, False otherwise
190
+ """
@@ -0,0 +1,350 @@
1
+ """
2
+ External Model Generator Service.
3
+
4
+ Generates ExternalModel classes from external connection schemas.
5
+ This allows runtime model generation from external databases without
6
+ manual model definition.
7
+ """
8
+
9
+ from typing import Any
10
+ from typing import cast
11
+
12
+ from amsdal_data.connections.external.base import SchemaIntrospectionProtocol
13
+ from amsdal_models.classes.external_model import ExternalModel
14
+ from amsdal_models.utils.schema_converter import ExternalSchemaConverter
15
+ from amsdal_utils.schemas.schema import ObjectSchema
16
+
17
+ from amsdal.services.external_connections import ExternalConnectionManager
18
+
19
+
20
+ class ExternalModelGenerator:
21
+ """
22
+ Service for generating ExternalModel classes from external connections.
23
+
24
+ This service introspects external database schemas and generates
25
+ corresponding ExternalModel classes that can be used immediately
26
+ for querying the external data.
27
+
28
+ Features:
29
+ - Automatic schema introspection
30
+ - Type mapping (SQL types -> Python types)
31
+ - Primary key detection
32
+ - In-memory model class generation
33
+ - No lakehouse schema creation
34
+
35
+ Example usage:
36
+ # Generate models for a single table
37
+ generator = ExternalModelGenerator()
38
+ User = generator.generate_model('external_db', 'users')
39
+
40
+ # Now use the generated model
41
+ users = User.objects.filter(active=True).execute()
42
+
43
+ # Generate models for all tables
44
+ models = generator.generate_models_for_connection('external_db')
45
+ User = models['User']
46
+ Post = models['Post']
47
+ """
48
+
49
+ def __init__(self) -> None:
50
+ self._connection_manager = ExternalConnectionManager()
51
+ self._schema_converter = ExternalSchemaConverter()
52
+
53
+ def generate_model(
54
+ self,
55
+ connection_name: str,
56
+ table_name: str,
57
+ model_name: str | None = None,
58
+ ) -> type[ExternalModel]:
59
+ """
60
+ Generate an ExternalModel class for a specific table.
61
+
62
+ Args:
63
+ connection_name: Name of the external connection
64
+ table_name: Name of the table to generate model for
65
+ model_name: Optional custom model name (defaults to classified table name)
66
+
67
+ Returns:
68
+ type[ExternalModel]: Generated model class ready to use
69
+
70
+ Raises:
71
+ ValueError: If connection doesn't support schema introspection
72
+ ConnectionError: If connection is not available
73
+ RuntimeError: If model generation fails
74
+
75
+ Example:
76
+ generator = ExternalModelGenerator()
77
+ User = generator.generate_model('external_db', 'users')
78
+
79
+ # Query using the generated model
80
+ active_users = User.objects.filter(active=True).execute()
81
+ """
82
+ # Get the connection
83
+ connection = self._connection_manager.get_connection(connection_name)
84
+
85
+ # Check if connection supports schema introspection
86
+ if not isinstance(connection, SchemaIntrospectionProtocol): # type: ignore[misc]
87
+ msg = (
88
+ f"Connection '{connection_name}' does not support schema introspection. "
89
+ f'Connection type: {type(connection).__name__}'
90
+ )
91
+ raise ValueError(msg)
92
+
93
+ # Get table schema
94
+ table_schema = connection.get_table_schema(table_name)
95
+
96
+ # Convert to ObjectSchema
97
+ # Detect connection type and use appropriate converter
98
+ object_schema = self._convert_schema(
99
+ connection=connection,
100
+ table_name=table_name,
101
+ table_schema=table_schema,
102
+ connection_name=connection_name,
103
+ )
104
+
105
+ # Generate model class from ObjectSchema
106
+ model_class = self._create_model_class(object_schema, model_name)
107
+
108
+ return model_class
109
+
110
+ def generate_models_for_connection(
111
+ self,
112
+ connection_name: str,
113
+ table_names: list[str] | None = None,
114
+ ) -> dict[str, type[ExternalModel]]:
115
+ """
116
+ Generate ExternalModel classes for all tables in a connection.
117
+
118
+ Args:
119
+ connection_name: Name of the external connection
120
+ table_names: Optional list of specific tables to generate models for.
121
+ If None, generates models for all tables.
122
+
123
+ Returns:
124
+ dict[str, type[ExternalModel]]: Dictionary mapping model names to model classes
125
+
126
+ Raises:
127
+ ValueError: If connection doesn't support schema introspection
128
+ ConnectionError: If connection is not available
129
+
130
+ Example:
131
+ generator = ExternalModelGenerator()
132
+ models = generator.generate_models_for_connection('external_db')
133
+
134
+ # Access generated models
135
+ User = models['User']
136
+ Post = models['Post']
137
+ Comment = models['Comment']
138
+
139
+ # Or generate only specific tables
140
+ models = generator.generate_models_for_connection(
141
+ 'external_db',
142
+ table_names=['users', 'posts']
143
+ )
144
+ """
145
+ # Get the connection
146
+ connection = self._connection_manager.get_connection(connection_name)
147
+
148
+ # Check if connection supports schema introspection
149
+ if not isinstance(connection, SchemaIntrospectionProtocol): # type: ignore[misc]
150
+ msg = (
151
+ f"Connection '{connection_name}' does not support schema introspection. "
152
+ f'Connection type: {type(connection).__name__}'
153
+ )
154
+ raise ValueError(msg)
155
+
156
+ # Get list of tables
157
+ if table_names is None:
158
+ table_names = connection.get_table_names()
159
+
160
+ # Generate models for each table
161
+ models: dict[str, type[ExternalModel]] = {}
162
+ for table_name in table_names:
163
+ try:
164
+ model = self.generate_model(connection_name, table_name)
165
+ models[model.__name__] = model
166
+ except Exception as e:
167
+ # Log error but continue with other tables
168
+ print(f"Warning: Failed to generate model for table '{table_name}': {e}")
169
+ continue
170
+
171
+ return models
172
+
173
+ def _convert_schema(
174
+ self,
175
+ connection: Any,
176
+ table_name: str,
177
+ table_schema: list[dict[str, Any]],
178
+ connection_name: str,
179
+ ) -> ObjectSchema:
180
+ """
181
+ Convert raw table schema to ObjectSchema based on connection type.
182
+
183
+ Args:
184
+ connection: The connection object
185
+ table_name: Name of the table
186
+ table_schema: Raw schema data from connection
187
+ connection_name: Name of the connection
188
+
189
+ Returns:
190
+ ObjectSchema: Converted schema
191
+ """
192
+ # Detect connection type and use appropriate converter
193
+ connection_type = type(connection).__name__
194
+
195
+ if 'sqlite' in connection_type.lower():
196
+ return self._schema_converter.sqlite_schema_to_object_schema(
197
+ table_name=table_name,
198
+ columns=table_schema,
199
+ connection_name=connection_name,
200
+ )
201
+
202
+ # For other connection types, try to use generic converter
203
+ # First, try to detect the schema format
204
+ if table_schema and isinstance(table_schema[0], dict):
205
+ # Check if it's SQLite format (has 'cid', 'name', 'type', 'pk', etc.)
206
+ if all(key in table_schema[0] for key in ('cid', 'name', 'type')):
207
+ return self._schema_converter.sqlite_schema_to_object_schema(
208
+ table_name=table_name,
209
+ columns=table_schema,
210
+ connection_name=connection_name,
211
+ )
212
+
213
+ # Check if it's PostgreSQL format (has 'column_name', 'data_type', etc.)
214
+ if 'column_name' in table_schema[0] and 'data_type' in table_schema[0]:
215
+ return self._schema_converter.postgres_schema_to_object_schema(
216
+ table_name=table_name,
217
+ columns=table_schema,
218
+ connection_name=connection_name,
219
+ )
220
+
221
+ # Try generic converter with format normalization
222
+ normalized_columns = self._normalize_schema_format(table_schema)
223
+ return self._schema_converter.generic_schema_to_object_schema(
224
+ table_name=table_name,
225
+ columns=normalized_columns,
226
+ connection_name=connection_name,
227
+ )
228
+
229
+ msg = f'Unknown schema format for connection type: {connection_type}'
230
+ raise ValueError(msg)
231
+
232
+ def _normalize_schema_format(self, table_schema: list[dict[str, Any]]) -> list[dict[str, Any]]:
233
+ """
234
+ Normalize various schema formats to generic format.
235
+
236
+ Converts various schema formats to the format expected by generic_schema_to_object_schema:
237
+ {'name': str, 'type': str, 'nullable': bool, 'primary_key': bool, 'default': Any}
238
+ """
239
+ normalized = []
240
+
241
+ for column in table_schema:
242
+ # Try to extract name
243
+ name = column.get('name') or column.get('column_name') or column.get('field')
244
+
245
+ # Try to extract type
246
+ col_type = column.get('type') or column.get('data_type') or column.get('field_type') or 'TEXT'
247
+
248
+ # Try to extract nullable
249
+ nullable = True
250
+ if 'nullable' in column:
251
+ nullable = column['nullable']
252
+ elif 'is_nullable' in column:
253
+ nullable = column['is_nullable'] in (True, 'YES', 'yes', 1)
254
+ elif 'notnull' in column:
255
+ nullable = column['notnull'] in (False, 0)
256
+
257
+ # Try to extract primary key
258
+ pk = column.get('primary_key') or column.get('pk') or False
259
+ if isinstance(pk, int):
260
+ pk = pk > 0
261
+
262
+ # Try to extract default
263
+ default = column.get('default') or column.get('dflt_value') or column.get('column_default')
264
+
265
+ normalized.append(
266
+ {
267
+ 'name': name,
268
+ 'type': col_type,
269
+ 'nullable': nullable,
270
+ 'primary_key': pk,
271
+ 'default': default,
272
+ }
273
+ )
274
+
275
+ return normalized
276
+
277
+ def _create_model_class(
278
+ self,
279
+ object_schema: ObjectSchema,
280
+ custom_name: str | None = None,
281
+ ) -> type[ExternalModel]:
282
+ """
283
+ Create an ExternalModel class from ObjectSchema.
284
+
285
+ Args:
286
+ object_schema: The schema to create model from
287
+ custom_name: Optional custom model name
288
+
289
+ Returns:
290
+ type[ExternalModel]: Generated model class
291
+ """
292
+ # Extract model metadata from schema
293
+ model_name = custom_name or object_schema.title
294
+ table_name = cast(str, object_schema.__table_name__) # type: ignore[attr-defined]
295
+ connection_name = cast(str, object_schema.__connection__) # type: ignore[attr-defined]
296
+ pk_fields = getattr(object_schema, '__primary_key__', None)
297
+
298
+ # Build class attributes
299
+ class_attrs: dict[str, Any] = {
300
+ '__table_name__': table_name,
301
+ '__connection__': connection_name,
302
+ '__module__': __name__,
303
+ }
304
+
305
+ # Add primary key if present
306
+ if pk_fields:
307
+ # For composite keys, use list; for single key, use string
308
+ if len(pk_fields) == 1:
309
+ class_attrs['__primary_key__'] = pk_fields[0]
310
+ else:
311
+ class_attrs['__primary_key__'] = pk_fields
312
+
313
+ # Add field annotations from schema properties
314
+ annotations: dict[str, type] = {}
315
+ if object_schema.properties:
316
+ for field_name, field_def in object_schema.properties.items():
317
+ # Map CoreTypes to Python types for annotations
318
+ field_type = self._core_type_to_python_type(getattr(field_def, 'type', 'string'))
319
+ annotations[field_name] = field_type
320
+
321
+ class_attrs['__annotations__'] = annotations
322
+
323
+ # Create the model class dynamically
324
+ model_class = type(model_name, (ExternalModel,), class_attrs)
325
+
326
+ return cast(type[ExternalModel], model_class)
327
+
328
+ @staticmethod
329
+ def _core_type_to_python_type(core_type: str) -> type:
330
+ """
331
+ Convert CoreType string to Python type for annotations.
332
+
333
+ Args:
334
+ core_type: CoreType value (e.g., 'string', 'integer')
335
+
336
+ Returns:
337
+ type: Corresponding Python type
338
+ """
339
+ type_mapping = {
340
+ 'string': str,
341
+ 'integer': int,
342
+ 'number': float,
343
+ 'boolean': bool,
344
+ 'date': str, # Will be string representation
345
+ 'datetime': str, # Will be string representation
346
+ 'binary': bytes,
347
+ 'array': list,
348
+ 'dictionary': dict,
349
+ }
350
+ return type_mapping.get(core_type, str)
@@ -0,0 +1,134 @@
1
+ from _typeshed import Incomplete
2
+ from amsdal.services.external_connections import ExternalConnectionManager as ExternalConnectionManager
3
+ from amsdal_models.classes.external_model import ExternalModel
4
+ from amsdal_utils.schemas.schema import ObjectSchema as ObjectSchema
5
+ from typing import Any
6
+
7
+ class ExternalModelGenerator:
8
+ """
9
+ Service for generating ExternalModel classes from external connections.
10
+
11
+ This service introspects external database schemas and generates
12
+ corresponding ExternalModel classes that can be used immediately
13
+ for querying the external data.
14
+
15
+ Features:
16
+ - Automatic schema introspection
17
+ - Type mapping (SQL types -> Python types)
18
+ - Primary key detection
19
+ - In-memory model class generation
20
+ - No lakehouse schema creation
21
+
22
+ Example usage:
23
+ # Generate models for a single table
24
+ generator = ExternalModelGenerator()
25
+ User = generator.generate_model('external_db', 'users')
26
+
27
+ # Now use the generated model
28
+ users = User.objects.filter(active=True).execute()
29
+
30
+ # Generate models for all tables
31
+ models = generator.generate_models_for_connection('external_db')
32
+ User = models['User']
33
+ Post = models['Post']
34
+ """
35
+ _connection_manager: Incomplete
36
+ _schema_converter: Incomplete
37
+ def __init__(self) -> None: ...
38
+ def generate_model(self, connection_name: str, table_name: str, model_name: str | None = None) -> type[ExternalModel]:
39
+ """
40
+ Generate an ExternalModel class for a specific table.
41
+
42
+ Args:
43
+ connection_name: Name of the external connection
44
+ table_name: Name of the table to generate model for
45
+ model_name: Optional custom model name (defaults to classified table name)
46
+
47
+ Returns:
48
+ type[ExternalModel]: Generated model class ready to use
49
+
50
+ Raises:
51
+ ValueError: If connection doesn't support schema introspection
52
+ ConnectionError: If connection is not available
53
+ RuntimeError: If model generation fails
54
+
55
+ Example:
56
+ generator = ExternalModelGenerator()
57
+ User = generator.generate_model('external_db', 'users')
58
+
59
+ # Query using the generated model
60
+ active_users = User.objects.filter(active=True).execute()
61
+ """
62
+ def generate_models_for_connection(self, connection_name: str, table_names: list[str] | None = None) -> dict[str, type[ExternalModel]]:
63
+ """
64
+ Generate ExternalModel classes for all tables in a connection.
65
+
66
+ Args:
67
+ connection_name: Name of the external connection
68
+ table_names: Optional list of specific tables to generate models for.
69
+ If None, generates models for all tables.
70
+
71
+ Returns:
72
+ dict[str, type[ExternalModel]]: Dictionary mapping model names to model classes
73
+
74
+ Raises:
75
+ ValueError: If connection doesn't support schema introspection
76
+ ConnectionError: If connection is not available
77
+
78
+ Example:
79
+ generator = ExternalModelGenerator()
80
+ models = generator.generate_models_for_connection('external_db')
81
+
82
+ # Access generated models
83
+ User = models['User']
84
+ Post = models['Post']
85
+ Comment = models['Comment']
86
+
87
+ # Or generate only specific tables
88
+ models = generator.generate_models_for_connection(
89
+ 'external_db',
90
+ table_names=['users', 'posts']
91
+ )
92
+ """
93
+ def _convert_schema(self, connection: Any, table_name: str, table_schema: list[dict[str, Any]], connection_name: str) -> ObjectSchema:
94
+ """
95
+ Convert raw table schema to ObjectSchema based on connection type.
96
+
97
+ Args:
98
+ connection: The connection object
99
+ table_name: Name of the table
100
+ table_schema: Raw schema data from connection
101
+ connection_name: Name of the connection
102
+
103
+ Returns:
104
+ ObjectSchema: Converted schema
105
+ """
106
+ def _normalize_schema_format(self, table_schema: list[dict[str, Any]]) -> list[dict[str, Any]]:
107
+ """
108
+ Normalize various schema formats to generic format.
109
+
110
+ Converts various schema formats to the format expected by generic_schema_to_object_schema:
111
+ {'name': str, 'type': str, 'nullable': bool, 'primary_key': bool, 'default': Any}
112
+ """
113
+ def _create_model_class(self, object_schema: ObjectSchema, custom_name: str | None = None) -> type[ExternalModel]:
114
+ """
115
+ Create an ExternalModel class from ObjectSchema.
116
+
117
+ Args:
118
+ object_schema: The schema to create model from
119
+ custom_name: Optional custom model name
120
+
121
+ Returns:
122
+ type[ExternalModel]: Generated model class
123
+ """
124
+ @staticmethod
125
+ def _core_type_to_python_type(core_type: str) -> type:
126
+ """
127
+ Convert CoreType string to Python type for annotations.
128
+
129
+ Args:
130
+ core_type: CoreType value (e.g., 'string', 'integer')
131
+
132
+ Returns:
133
+ type: Corresponding Python type
134
+ """