amsdal 0.3.0__cp311-cp311-macosx_10_9_universal2.whl → 0.3.2__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.
- amsdal/__about__.py +1 -1
- amsdal/cloud/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/client.cpython-311-darwin.so +0 -0
- amsdal/cloud/constants.cpython-311-darwin.so +0 -0
- amsdal/cloud/enums.cpython-311-darwin.so +0 -0
- amsdal/cloud/models/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/models/base.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/add_allowlist_ip.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/add_basic_auth.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/add_dependency.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/add_secret.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/base.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/create_deploy.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/create_env.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/create_session.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_allowlist_ip.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_basic_auth.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_dependency.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_env.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/delete_secret.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/destroy_deploy.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/expose_db.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/get_basic_auth_credentials.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/get_monitoring_info.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/list_dependencies.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/list_deploys.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/list_envs.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/list_secrets.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/manager.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/signup_action.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/actions/update_deploy.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/__init__.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/base.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/credentials.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/manager.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/signup_service.cpython-311-darwin.so +0 -0
- amsdal/cloud/services/auth/token.cpython-311-darwin.so +0 -0
- amsdal/contrib/__init__.cpython-311-darwin.so +0 -0
- amsdal/contrib/auth/lifecycle/consumer.py +172 -0
- amsdal/contrib/auth/lifecycle/consumer.pyi +36 -0
- amsdal/contrib/auth/models/login_session/hooks/pre_init.py +14 -10
- amsdal/contrib/frontend_configs/lifecycle/consumer.py +45 -0
- amsdal/contrib/frontend_configs/lifecycle/consumer.pyi +12 -0
- amsdal/fixtures/__init__.cpython-311-darwin.so +0 -0
- amsdal/fixtures/manager.cpython-311-darwin.so +0 -0
- amsdal/fixtures/manager.pyi +102 -0
- amsdal/manager.cpython-311-darwin.so +0 -0
- amsdal/manager.pyi +170 -1
- amsdal/migration/__init__.cpython-311-darwin.so +0 -0
- amsdal/migration/base_migration_schemas.cpython-311-darwin.so +0 -0
- amsdal/migration/data_classes.cpython-311-darwin.so +0 -0
- amsdal/migration/executors/__init__.cpython-311-darwin.so +0 -0
- amsdal/migration/executors/base.cpython-311-darwin.so +0 -0
- amsdal/migration/executors/base.pyi +12 -0
- amsdal/migration/executors/default_executor.cpython-311-darwin.so +0 -0
- amsdal/migration/executors/default_executor.pyi +88 -1
- amsdal/migration/executors/state_executor.cpython-311-darwin.so +0 -0
- amsdal/migration/file_migration_executor.cpython-311-darwin.so +0 -0
- amsdal/migration/file_migration_generator.cpython-311-darwin.so +0 -0
- amsdal/migration/file_migration_store.cpython-311-darwin.so +0 -0
- amsdal/migration/file_migration_writer.cpython-311-darwin.so +0 -0
- amsdal/migration/migrations.cpython-311-darwin.so +0 -0
- amsdal/migration/migrations_loader.cpython-311-darwin.so +0 -0
- amsdal/migration/schemas_loaders.cpython-311-darwin.so +0 -0
- amsdal/migration/utils.cpython-311-darwin.so +0 -0
- amsdal/mixins/__init__.cpython-311-darwin.so +0 -0
- amsdal/mixins/build_mixin.cpython-311-darwin.so +0 -0
- amsdal/mixins/class_versions_mixin.cpython-311-darwin.so +0 -0
- amsdal/mixins/class_versions_mixin.pyi +7 -1
- amsdal/schemas/manager.cpython-311-darwin.so +0 -0
- amsdal/services/__init__.cpython-311-darwin.so +0 -0
- amsdal/services/transaction_execution.cpython-311-darwin.so +0 -0
- amsdal/utils/rollback/__init__.py +195 -0
- amsdal/utils/rollback/__init__.pyi +16 -0
- {amsdal-0.3.0.dist-info → amsdal-0.3.2.dist-info}/METADATA +5 -3
- {amsdal-0.3.0.dist-info → amsdal-0.3.2.dist-info}/RECORD +82 -82
- {amsdal-0.3.0.dist-info → amsdal-0.3.2.dist-info}/LICENSE.txt +0 -0
- {amsdal-0.3.0.dist-info → amsdal-0.3.2.dist-info}/WHEEL +0 -0
- {amsdal-0.3.0.dist-info → amsdal-0.3.2.dist-info}/licenses/LICENSE.txt +0 -0
- {amsdal-0.3.0.dist-info → amsdal-0.3.2.dist-info}/top_level.txt +0 -0
amsdal/__about__.py
CHANGED
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -2,6 +2,7 @@ import logging
|
|
|
2
2
|
from typing import Any
|
|
3
3
|
|
|
4
4
|
import jwt
|
|
5
|
+
from amsdal_data.transactions.decorators import async_transaction
|
|
5
6
|
from amsdal_data.transactions.decorators import transaction
|
|
6
7
|
from amsdal_models.classes.helpers.reference_loader import ReferenceLoader
|
|
7
8
|
from amsdal_models.classes.model import Model
|
|
@@ -70,6 +71,54 @@ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
|
|
70
71
|
instance.save(force_insert=True)
|
|
71
72
|
logger.info('Super user created successfully')
|
|
72
73
|
|
|
74
|
+
@async_transaction
|
|
75
|
+
async def on_event_async(self) -> None: # type: ignore[override]
|
|
76
|
+
"""
|
|
77
|
+
Checks for the existence of a super user and creates one if necessary.
|
|
78
|
+
|
|
79
|
+
This method ensures that a super user exists by checking the email and password
|
|
80
|
+
in the authentication settings. If the super user does not exist, it creates one
|
|
81
|
+
with the necessary permissions.
|
|
82
|
+
"""
|
|
83
|
+
from amsdal.contrib.auth.settings import auth_settings
|
|
84
|
+
from models.contrib.permission import Permission # type: ignore[import-not-found]
|
|
85
|
+
from models.contrib.user import User # type: ignore[import-not-found]
|
|
86
|
+
|
|
87
|
+
logger.info('Ensure super user exists')
|
|
88
|
+
|
|
89
|
+
if not (auth_settings.ADMIN_USER_EMAIL and auth_settings.ADMIN_USER_PASSWORD):
|
|
90
|
+
logger.info('Email / password missing for super user - skipping')
|
|
91
|
+
return
|
|
92
|
+
|
|
93
|
+
user = (
|
|
94
|
+
await User.objects.filter(email=auth_settings.ADMIN_USER_EMAIL, _address__object_version=Versions.LATEST)
|
|
95
|
+
.get_or_none()
|
|
96
|
+
.aexecute()
|
|
97
|
+
)
|
|
98
|
+
if user is not None:
|
|
99
|
+
logger.info('Super user already exists - skipping')
|
|
100
|
+
return
|
|
101
|
+
|
|
102
|
+
logger.info("Super user doesn't exist - creating now")
|
|
103
|
+
|
|
104
|
+
access_all_permission = (
|
|
105
|
+
await Permission.objects.filter(
|
|
106
|
+
model='*',
|
|
107
|
+
action='*',
|
|
108
|
+
_address__object_version=Versions.LATEST,
|
|
109
|
+
)
|
|
110
|
+
.get()
|
|
111
|
+
.aexecute()
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
instance = User(
|
|
115
|
+
email=auth_settings.ADMIN_USER_EMAIL,
|
|
116
|
+
password=auth_settings.ADMIN_USER_PASSWORD,
|
|
117
|
+
permissions=[access_all_permission],
|
|
118
|
+
)
|
|
119
|
+
await instance.asave(force_insert=True)
|
|
120
|
+
logger.info('Super user created successfully')
|
|
121
|
+
|
|
73
122
|
|
|
74
123
|
class AuthenticateUserConsumer(LifecycleConsumer):
|
|
75
124
|
"""
|
|
@@ -120,6 +169,46 @@ class AuthenticateUserConsumer(LifecycleConsumer):
|
|
|
120
169
|
|
|
121
170
|
authentication_info.user = user
|
|
122
171
|
|
|
172
|
+
async def on_event_async(self, auth_header: str, authentication_info: Any) -> None:
|
|
173
|
+
"""
|
|
174
|
+
Authenticates the user using the provided JWT token.
|
|
175
|
+
|
|
176
|
+
This method decodes the JWT token from the authorization header and retrieves
|
|
177
|
+
the corresponding user from the database. If the token is invalid or expired,
|
|
178
|
+
it raises an `AuthenticationError`.
|
|
179
|
+
|
|
180
|
+
Args:
|
|
181
|
+
auth_header (str): The JWT token from the authorization header.
|
|
182
|
+
authentication_info (Any): The authentication information object to update with the user.
|
|
183
|
+
"""
|
|
184
|
+
from amsdal.contrib.auth.settings import auth_settings
|
|
185
|
+
from models.contrib.user import User # type: ignore[import-not-found]
|
|
186
|
+
|
|
187
|
+
authentication_info.user = None
|
|
188
|
+
email: str | None
|
|
189
|
+
|
|
190
|
+
try:
|
|
191
|
+
jwt_payload = jwt.decode(
|
|
192
|
+
auth_header,
|
|
193
|
+
auth_settings.AUTH_JWT_KEY, # type: ignore[arg-type]
|
|
194
|
+
algorithms=['HS256'],
|
|
195
|
+
)
|
|
196
|
+
email = jwt_payload['email']
|
|
197
|
+
except jwt.ExpiredSignatureError as exc:
|
|
198
|
+
logger.error('Auth token expired. Defaulting to anonymous user.')
|
|
199
|
+
|
|
200
|
+
msg = 'Auth token has expired.'
|
|
201
|
+
raise AuthenticationError(msg) from exc
|
|
202
|
+
except Exception as exc:
|
|
203
|
+
logger.error('Auth token decode failure. Defaulting to anonymous user.')
|
|
204
|
+
|
|
205
|
+
msg = 'Failed to decode auth token.'
|
|
206
|
+
raise AuthenticationError(msg) from exc
|
|
207
|
+
|
|
208
|
+
user = await User.objects.filter(email=email, _address__object_version=Versions.LATEST).get_or_none().aexecute()
|
|
209
|
+
|
|
210
|
+
authentication_info.user = user
|
|
211
|
+
|
|
123
212
|
|
|
124
213
|
class CheckPermissionConsumer(LifecycleConsumer):
|
|
125
214
|
"""
|
|
@@ -155,6 +244,32 @@ class CheckPermissionConsumer(LifecycleConsumer):
|
|
|
155
244
|
elif required_permission.action == 'delete':
|
|
156
245
|
permissions_info.has_delete_permission = False
|
|
157
246
|
|
|
247
|
+
async def _async_prepopulate_default_permissions(self, object_class: type[Model], permissions_info: Any) -> None:
|
|
248
|
+
from amsdal.contrib.auth.settings import auth_settings
|
|
249
|
+
from models.contrib.permission import Permission # type: ignore[import-not-found]
|
|
250
|
+
|
|
251
|
+
permissions_info.has_read_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
|
252
|
+
permissions_info.has_create_permission = (
|
|
253
|
+
(not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION) if object_class.__name__ != 'LoginSession' else True
|
|
254
|
+
)
|
|
255
|
+
permissions_info.has_update_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
|
256
|
+
permissions_info.has_delete_permission = not auth_settings.REQUIRE_DEFAULT_AUTHORIZATION
|
|
257
|
+
|
|
258
|
+
required_permissions = await Permission.objects.filter(
|
|
259
|
+
model=object_class.__name__,
|
|
260
|
+
_address__object_version=Versions.LATEST,
|
|
261
|
+
).aexecute()
|
|
262
|
+
|
|
263
|
+
for required_permission in required_permissions:
|
|
264
|
+
if required_permission.action == 'read':
|
|
265
|
+
permissions_info.has_read_permission = False
|
|
266
|
+
elif required_permission.action == 'create':
|
|
267
|
+
permissions_info.has_create_permission = False
|
|
268
|
+
elif required_permission.action == 'update':
|
|
269
|
+
permissions_info.has_update_permission = False
|
|
270
|
+
elif required_permission.action == 'delete':
|
|
271
|
+
permissions_info.has_delete_permission = False
|
|
272
|
+
|
|
158
273
|
def _check_class_permissions(self, object_class: type[Model], user: Any, permissions_info: Any) -> None:
|
|
159
274
|
if hasattr(object_class, 'has_permission'):
|
|
160
275
|
for action in ['read', 'create', 'update', 'delete']:
|
|
@@ -185,6 +300,36 @@ class CheckPermissionConsumer(LifecycleConsumer):
|
|
|
185
300
|
permissions_info.has_update_permission = True
|
|
186
301
|
permissions_info.has_delete_permission = True
|
|
187
302
|
|
|
303
|
+
async def _async_check_class_permissions(self, object_class: type[Model], user: Any, permissions_info: Any) -> None:
|
|
304
|
+
if hasattr(object_class, 'has_permission'):
|
|
305
|
+
for action in ['read', 'create', 'update', 'delete']:
|
|
306
|
+
setattr(permissions_info, f'has_{action}_permission', object_class.has_permission(user, action))
|
|
307
|
+
|
|
308
|
+
if not user or not getattr(user, 'permissions', None):
|
|
309
|
+
return
|
|
310
|
+
|
|
311
|
+
user_permissions = [
|
|
312
|
+
await ReferenceLoader(p).aload_reference() if isinstance(p, Reference) else p for p in user.permissions
|
|
313
|
+
]
|
|
314
|
+
|
|
315
|
+
for user_permission in user_permissions:
|
|
316
|
+
if user_permission.model not in [object_class.__name__, '*']:
|
|
317
|
+
continue
|
|
318
|
+
|
|
319
|
+
if user_permission.action == 'read':
|
|
320
|
+
permissions_info.has_read_permission = True
|
|
321
|
+
elif user_permission.action == 'create':
|
|
322
|
+
permissions_info.has_create_permission = True
|
|
323
|
+
elif user_permission.action == 'update':
|
|
324
|
+
permissions_info.has_update_permission = True
|
|
325
|
+
elif user_permission.action == 'delete':
|
|
326
|
+
permissions_info.has_delete_permission = True
|
|
327
|
+
elif user_permission.action == '*':
|
|
328
|
+
permissions_info.has_read_permission = True
|
|
329
|
+
permissions_info.has_create_permission = True
|
|
330
|
+
permissions_info.has_update_permission = True
|
|
331
|
+
permissions_info.has_delete_permission = True
|
|
332
|
+
|
|
188
333
|
def _check_object_permissions(self, obj: Model, user: Any, permissions_info: Any) -> None:
|
|
189
334
|
if hasattr(obj, 'has_object_permission'):
|
|
190
335
|
for action in ['read', 'update', 'delete']:
|
|
@@ -220,3 +365,30 @@ class CheckPermissionConsumer(LifecycleConsumer):
|
|
|
220
365
|
|
|
221
366
|
if obj:
|
|
222
367
|
self._check_object_permissions(obj, user, permissions_info)
|
|
368
|
+
|
|
369
|
+
async def on_event_async(
|
|
370
|
+
self,
|
|
371
|
+
object_class: type[Model],
|
|
372
|
+
user: Any,
|
|
373
|
+
access_types: list[Any], # noqa: ARG002
|
|
374
|
+
permissions_info: Any,
|
|
375
|
+
obj: Model | None = None,
|
|
376
|
+
) -> None:
|
|
377
|
+
"""
|
|
378
|
+
Main method to check permissions for a given user and object.
|
|
379
|
+
|
|
380
|
+
This method prepopulates default permissions, checks class-level permissions,
|
|
381
|
+
and object-level permissions for the given user and object.
|
|
382
|
+
|
|
383
|
+
Args:
|
|
384
|
+
object_class (type[Model]): The class of the object to check permissions for.
|
|
385
|
+
user (Any): The user to check permissions for.
|
|
386
|
+
access_types (list[Any]): The list of access types to check.
|
|
387
|
+
permissions_info (Any): The permissions information object to update.
|
|
388
|
+
obj (Model | None): The object to check permissions for, if any.
|
|
389
|
+
"""
|
|
390
|
+
await self._async_prepopulate_default_permissions(object_class, permissions_info)
|
|
391
|
+
await self._async_check_class_permissions(object_class, user, permissions_info)
|
|
392
|
+
|
|
393
|
+
if obj:
|
|
394
|
+
self._check_object_permissions(obj, user, permissions_info)
|
|
@@ -17,6 +17,14 @@ class CheckAndCreateSuperUserConsumer(LifecycleConsumer):
|
|
|
17
17
|
"""
|
|
18
18
|
Checks for the existence of a super user and creates one if necessary.
|
|
19
19
|
|
|
20
|
+
This method ensures that a super user exists by checking the email and password
|
|
21
|
+
in the authentication settings. If the super user does not exist, it creates one
|
|
22
|
+
with the necessary permissions.
|
|
23
|
+
"""
|
|
24
|
+
async def on_event_async(self) -> None:
|
|
25
|
+
"""
|
|
26
|
+
Checks for the existence of a super user and creates one if necessary.
|
|
27
|
+
|
|
20
28
|
This method ensures that a super user exists by checking the email and password
|
|
21
29
|
in the authentication settings. If the super user does not exist, it creates one
|
|
22
30
|
with the necessary permissions.
|
|
@@ -38,6 +46,18 @@ class AuthenticateUserConsumer(LifecycleConsumer):
|
|
|
38
46
|
the corresponding user from the database. If the token is invalid or expired,
|
|
39
47
|
it raises an `AuthenticationError`.
|
|
40
48
|
|
|
49
|
+
Args:
|
|
50
|
+
auth_header (str): The JWT token from the authorization header.
|
|
51
|
+
authentication_info (Any): The authentication information object to update with the user.
|
|
52
|
+
"""
|
|
53
|
+
async def on_event_async(self, auth_header: str, authentication_info: Any) -> None:
|
|
54
|
+
"""
|
|
55
|
+
Authenticates the user using the provided JWT token.
|
|
56
|
+
|
|
57
|
+
This method decodes the JWT token from the authorization header and retrieves
|
|
58
|
+
the corresponding user from the database. If the token is invalid or expired,
|
|
59
|
+
it raises an `AuthenticationError`.
|
|
60
|
+
|
|
41
61
|
Args:
|
|
42
62
|
auth_header (str): The JWT token from the authorization header.
|
|
43
63
|
authentication_info (Any): The authentication information object to update with the user.
|
|
@@ -51,7 +71,9 @@ class CheckPermissionConsumer(LifecycleConsumer):
|
|
|
51
71
|
and object-level permissions for a given user and object.
|
|
52
72
|
"""
|
|
53
73
|
def _prepopulate_default_permissions(self, object_class: type[Model], permissions_info: Any) -> None: ...
|
|
74
|
+
async def _async_prepopulate_default_permissions(self, object_class: type[Model], permissions_info: Any) -> None: ...
|
|
54
75
|
def _check_class_permissions(self, object_class: type[Model], user: Any, permissions_info: Any) -> None: ...
|
|
76
|
+
async def _async_check_class_permissions(self, object_class: type[Model], user: Any, permissions_info: Any) -> None: ...
|
|
55
77
|
def _check_object_permissions(self, obj: Model, user: Any, permissions_info: Any) -> None: ...
|
|
56
78
|
def on_event(self, object_class: type[Model], user: Any, access_types: list[Any], permissions_info: Any, obj: Model | None = None) -> None:
|
|
57
79
|
"""
|
|
@@ -60,6 +82,20 @@ class CheckPermissionConsumer(LifecycleConsumer):
|
|
|
60
82
|
This method prepopulates default permissions, checks class-level permissions,
|
|
61
83
|
and object-level permissions for the given user and object.
|
|
62
84
|
|
|
85
|
+
Args:
|
|
86
|
+
object_class (type[Model]): The class of the object to check permissions for.
|
|
87
|
+
user (Any): The user to check permissions for.
|
|
88
|
+
access_types (list[Any]): The list of access types to check.
|
|
89
|
+
permissions_info (Any): The permissions information object to update.
|
|
90
|
+
obj (Model | None): The object to check permissions for, if any.
|
|
91
|
+
"""
|
|
92
|
+
async def on_event_async(self, object_class: type[Model], user: Any, access_types: list[Any], permissions_info: Any, obj: Model | None = None) -> None:
|
|
93
|
+
"""
|
|
94
|
+
Main method to check permissions for a given user and object.
|
|
95
|
+
|
|
96
|
+
This method prepopulates default permissions, checks class-level permissions,
|
|
97
|
+
and object-level permissions for the given user and object.
|
|
98
|
+
|
|
63
99
|
Args:
|
|
64
100
|
object_class (type[Model]): The class of the object to check permissions for.
|
|
65
101
|
user (Any): The user to check permissions for.
|
|
@@ -3,9 +3,10 @@ from datetime import timedelta
|
|
|
3
3
|
from datetime import timezone
|
|
4
4
|
from typing import Any
|
|
5
5
|
|
|
6
|
-
import bcrypt
|
|
6
|
+
# import bcrypt
|
|
7
7
|
import jwt
|
|
8
|
-
|
|
8
|
+
|
|
9
|
+
# from amsdal_utils.models.enums import Versions
|
|
9
10
|
|
|
10
11
|
|
|
11
12
|
def pre_init(self, *, is_new_object: bool, kwargs: dict[str, Any]) -> None: # type: ignore[no-untyped-def] # noqa: ARG001
|
|
@@ -41,17 +42,20 @@ def pre_init(self, *, is_new_object: bool, kwargs: dict[str, Any]) -> None: # t
|
|
|
41
42
|
|
|
42
43
|
lowercased_email = email.lower()
|
|
43
44
|
|
|
44
|
-
from models.contrib.user import User # type: ignore[import-not-found]
|
|
45
|
+
# from models.contrib.user import User # type: ignore[import-not-found]
|
|
45
46
|
|
|
46
|
-
user = User.objects.filter(
|
|
47
|
+
# user = User.objects.filter(
|
|
48
|
+
# email=lowercased_email,
|
|
49
|
+
# _address__object_version=Versions.LATEST
|
|
50
|
+
# ).get_or_none().execute()
|
|
47
51
|
|
|
48
|
-
if not user:
|
|
49
|
-
|
|
50
|
-
|
|
52
|
+
# if not user:
|
|
53
|
+
# msg = 'Invalid email / password'
|
|
54
|
+
# raise AuthenticationError(msg)
|
|
51
55
|
|
|
52
|
-
if not bcrypt.checkpw(password.encode('utf-8') if isinstance(password, str) else password, user.password):
|
|
53
|
-
|
|
54
|
-
|
|
56
|
+
# if not bcrypt.checkpw(password.encode('utf-8') if isinstance(password, str) else password, user.password):
|
|
57
|
+
# msg = 'Invalid email / password'
|
|
58
|
+
# raise AuthenticationError(msg)
|
|
55
59
|
|
|
56
60
|
kwargs['password'] = 'validated'
|
|
57
61
|
expiration_time = datetime.now(tz=timezone.utc) + timedelta(seconds=auth_settings.AUTH_TOKEN_EXPIRATION)
|
|
@@ -240,3 +240,48 @@ class ProcessResponseConsumer(LifecycleConsumer):
|
|
|
240
240
|
)
|
|
241
241
|
else:
|
|
242
242
|
response['control'] = populate_frontend_config_with_values(get_default_control(class_name), values)
|
|
243
|
+
|
|
244
|
+
async def on_event_async(
|
|
245
|
+
self,
|
|
246
|
+
request: Any,
|
|
247
|
+
response: dict[str, Any],
|
|
248
|
+
) -> None:
|
|
249
|
+
"""
|
|
250
|
+
Handles the event by extracting the class name and values from the request and response,
|
|
251
|
+
and populates the frontend configuration accordingly.
|
|
252
|
+
|
|
253
|
+
Args:
|
|
254
|
+
request (Any): The request object containing query and path parameters.
|
|
255
|
+
response (dict[str, Any]): The response dictionary to be processed.
|
|
256
|
+
|
|
257
|
+
Returns:
|
|
258
|
+
None
|
|
259
|
+
"""
|
|
260
|
+
from models.contrib.frontend_model_config import FrontendModelConfig # type: ignore[import-not-found]
|
|
261
|
+
|
|
262
|
+
class_name = None
|
|
263
|
+
values = {}
|
|
264
|
+
if hasattr(request, 'query_params') and 'class_name' in request.query_params:
|
|
265
|
+
class_name = request.query_params['class_name']
|
|
266
|
+
|
|
267
|
+
if hasattr(request, 'path_params') and 'address' in request.path_params:
|
|
268
|
+
class_name = Address.from_string(request.path_params['address']).class_name
|
|
269
|
+
values = get_values_from_response(response)
|
|
270
|
+
|
|
271
|
+
if class_name and isinstance(response, dict):
|
|
272
|
+
config = (
|
|
273
|
+
await FrontendModelConfig.objects.all()
|
|
274
|
+
.first(
|
|
275
|
+
class_name=class_name,
|
|
276
|
+
_metadata__is_deleted=False,
|
|
277
|
+
_address__object_version=Versions.LATEST,
|
|
278
|
+
)
|
|
279
|
+
.aexecute()
|
|
280
|
+
)
|
|
281
|
+
|
|
282
|
+
if config and config.control:
|
|
283
|
+
response['control'] = populate_frontend_config_with_values(
|
|
284
|
+
config.control.model_dump(exclude_none=True), values
|
|
285
|
+
)
|
|
286
|
+
else:
|
|
287
|
+
response['control'] = populate_frontend_config_with_values(get_default_control(class_name), values)
|
|
@@ -77,6 +77,18 @@ class ProcessResponseConsumer(LifecycleConsumer):
|
|
|
77
77
|
Handles the event by extracting the class name and values from the request and response,
|
|
78
78
|
and populates the frontend configuration accordingly.
|
|
79
79
|
|
|
80
|
+
Args:
|
|
81
|
+
request (Any): The request object containing query and path parameters.
|
|
82
|
+
response (dict[str, Any]): The response dictionary to be processed.
|
|
83
|
+
|
|
84
|
+
Returns:
|
|
85
|
+
None
|
|
86
|
+
"""
|
|
87
|
+
async def on_event_async(self, request: Any, response: dict[str, Any]) -> None:
|
|
88
|
+
"""
|
|
89
|
+
Handles the event by extracting the class name and values from the request and response,
|
|
90
|
+
and populates the frontend configuration accordingly.
|
|
91
|
+
|
|
80
92
|
Args:
|
|
81
93
|
request (Any): The request object containing query and path parameters.
|
|
82
94
|
response (dict[str, Any]): The response dictionary to be processed.
|
|
Binary file
|
|
Binary file
|
amsdal/fixtures/manager.pyi
CHANGED
|
@@ -116,3 +116,105 @@ class FixturesManager:
|
|
|
116
116
|
None
|
|
117
117
|
"""
|
|
118
118
|
def _process_file_fixture(self, file_path: Path, file_key: str) -> None: ...
|
|
119
|
+
|
|
120
|
+
class AsyncFixturesManager:
|
|
121
|
+
"""
|
|
122
|
+
Manager class for handling fixture data.
|
|
123
|
+
|
|
124
|
+
This class is responsible for loading, processing, and applying fixture data
|
|
125
|
+
to the database. It supports nested object construction, data processing,
|
|
126
|
+
and file fixture handling.
|
|
127
|
+
"""
|
|
128
|
+
fixtures_path: Incomplete
|
|
129
|
+
fixtures: Incomplete
|
|
130
|
+
_created_cache: Incomplete
|
|
131
|
+
data_to_process: Incomplete
|
|
132
|
+
_class_manager: Incomplete
|
|
133
|
+
_config_manager: Incomplete
|
|
134
|
+
_schema_manager: Incomplete
|
|
135
|
+
def __init__(self, fixtures_path: Path, class_manager: ClassManager, config_manager: AmsdalConfigManager, schema_manager: SchemaManager) -> None: ...
|
|
136
|
+
def load_fixtures(self) -> None:
|
|
137
|
+
"""
|
|
138
|
+
Loads fixture data from the specified path.
|
|
139
|
+
|
|
140
|
+
This method reads fixture data from a JSON file located at the `fixtures_path`.
|
|
141
|
+
It populates the `fixtures` dictionary with the loaded data, where each fixture
|
|
142
|
+
is indexed by its external ID.
|
|
143
|
+
|
|
144
|
+
Returns:
|
|
145
|
+
None
|
|
146
|
+
"""
|
|
147
|
+
def construct_nested_object(self, external_id: Any) -> Any:
|
|
148
|
+
"""
|
|
149
|
+
Constructs a nested object reference from the given external ID.
|
|
150
|
+
|
|
151
|
+
This method takes an external ID and constructs a nested object reference
|
|
152
|
+
dictionary. If the external ID is a dictionary containing an '_object_id' key,
|
|
153
|
+
it extracts the ID. If the external ID is not a string or integer, it returns
|
|
154
|
+
the external ID as is.
|
|
155
|
+
|
|
156
|
+
Args:
|
|
157
|
+
external_id (Any): The external ID to construct the nested object reference from.
|
|
158
|
+
|
|
159
|
+
Returns:
|
|
160
|
+
Any: A dictionary representing the nested object reference or the original
|
|
161
|
+
external ID if it is not a string or integer.
|
|
162
|
+
"""
|
|
163
|
+
def _process_object_data(self, model_properties: dict[str, PropertyData], data: dict[str, Any]) -> dict[str, Any]: ...
|
|
164
|
+
def _process_object_value(self, field_configuration: PropertyData | DictSchema | TypeData, value: Any) -> Any: ...
|
|
165
|
+
async def process_fixture_object_data(self, class_name: str, external_id: str, data: dict[str, Any]) -> None:
|
|
166
|
+
"""
|
|
167
|
+
Processes and saves fixture object data to the database.
|
|
168
|
+
|
|
169
|
+
This method takes the class name, external ID, and data dictionary of a fixture object,
|
|
170
|
+
processes the data according to the class schema, and saves the object to the database.
|
|
171
|
+
If the object already exists, it updates the existing object with the new data.
|
|
172
|
+
|
|
173
|
+
Args:
|
|
174
|
+
class_name (str): The name of the class to which the fixture object belongs.
|
|
175
|
+
external_id (str): The external ID of the fixture object.
|
|
176
|
+
data (dict[str, Any]): The data dictionary of the fixture object.
|
|
177
|
+
|
|
178
|
+
Returns:
|
|
179
|
+
None
|
|
180
|
+
"""
|
|
181
|
+
async def process_fixture(self, fixture: dict[str, Any]) -> None:
|
|
182
|
+
"""
|
|
183
|
+
Processes a single fixture and adds it to the processing queue.
|
|
184
|
+
|
|
185
|
+
This method takes a fixture dictionary, checks if the fixture already exists in the database,
|
|
186
|
+
and either updates the existing fixture or creates a new one. It then adds the fixture data
|
|
187
|
+
to the processing queue for further processing.
|
|
188
|
+
|
|
189
|
+
Args:
|
|
190
|
+
fixture (dict[str, Any]): The fixture dictionary containing the external ID, class name,
|
|
191
|
+
and data of the fixture.
|
|
192
|
+
|
|
193
|
+
Returns:
|
|
194
|
+
None
|
|
195
|
+
"""
|
|
196
|
+
async def apply_fixtures(self) -> None:
|
|
197
|
+
"""
|
|
198
|
+
Applies all loaded fixtures to the database.
|
|
199
|
+
|
|
200
|
+
This method processes each fixture in the `fixtures` dictionary in the order
|
|
201
|
+
specified by their 'order' value. It calls the `process_fixture` method for
|
|
202
|
+
each fixture and then processes the data in the processing queue.
|
|
203
|
+
|
|
204
|
+
Returns:
|
|
205
|
+
None
|
|
206
|
+
"""
|
|
207
|
+
async def _process_data(self) -> None: ...
|
|
208
|
+
async def apply_file_fixtures(self) -> None:
|
|
209
|
+
"""
|
|
210
|
+
Applies file fixtures from the specified directory.
|
|
211
|
+
|
|
212
|
+
This method processes file fixtures located in the 'files' directory adjacent to the
|
|
213
|
+
`fixtures_path`. It iterates through each file, reads its content, and processes it
|
|
214
|
+
as a fixture. If the file fixture already exists in the database, it updates the
|
|
215
|
+
existing fixture; otherwise, it creates a new one.
|
|
216
|
+
|
|
217
|
+
Returns:
|
|
218
|
+
None
|
|
219
|
+
"""
|
|
220
|
+
async def _process_file_fixture(self, file_path: Path, file_key: str) -> None: ...
|
|
Binary file
|