alpha-python 0.5.0__py3-none-any.whl → 0.6.0__py3-none-any.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.
- alpha/__init__.py +61 -5
- alpha/adapters/rest_api_unit_of_work.py +9 -3
- alpha/adapters/sqla_unit_of_work.py +23 -5
- alpha/containers/container.py +52 -47
- alpha/domain/__init__.py +12 -1
- alpha/domain/models/__init__.py +12 -1
- alpha/domain/models/base_model.py +37 -1
- alpha/domain/models/group.py +51 -1
- alpha/domain/models/life_cycle_base.py +19 -0
- alpha/domain/models/role.py +127 -0
- alpha/domain/models/user.py +81 -4
- alpha/encoder.py +21 -0
- alpha/factories/class_factories.py +49 -22
- alpha/factories/default_field_factory.py +1 -0
- alpha/factories/field_iterator.py +8 -2
- alpha/factories/jwt_factory.py +119 -36
- alpha/factories/logging_handler_factory.py +7 -5
- alpha/factories/model_class_factory.py +6 -3
- alpha/factories/models/factory_classes.py +20 -1
- alpha/factories/password_factory.py +32 -14
- alpha/factories/request_factory.py +24 -13
- alpha/factories/response_factory.py +5 -2
- alpha/factories/type_factories.py +16 -2
- alpha/handlers/api_generate_handler.py +32 -33
- alpha/handlers/run-api.sh +0 -8
- alpha/handlers/templates/python-flask/controller.mustache +140 -32
- alpha/handlers/templates/python-flask/controller_test.mustache +1 -1
- alpha/handlers/templates/python-flask/model.mustache +7 -1
- alpha/handlers/templates/python-flask/requirements.mustache +0 -19
- alpha/handlers/templates/python-flask/setup.mustache +0 -19
- alpha/infra/__init__.py +14 -5
- alpha/infra/connectors/__init__.py +9 -4
- alpha/infra/connectors/ldap_connector.py +38 -29
- alpha/infra/connectors/oidc_connector.py +1 -1
- alpha/infra/connectors/sql_alchemy.py +166 -0
- alpha/infra/databases/sql_alchemy.py +7 -160
- alpha/infra/models/filter_operators.py +6 -1
- alpha/infra/models/json_patch.py +31 -5
- alpha/infra/models/order_by.py +75 -3
- alpha/infra/models/query_clause.py +31 -6
- alpha/infra/models/search_filter.py +96 -0
- alpha/interfaces/__init__.py +2 -2
- alpha/interfaces/api_repository.py +11 -5
- alpha/interfaces/factories.py +5 -2
- alpha/interfaces/patchable.py +17 -1
- alpha/interfaces/providers.py +4 -4
- alpha/interfaces/sql_mapper.py +6 -3
- alpha/interfaces/sql_repository.py +128 -141
- alpha/interfaces/token_factory.py +13 -8
- alpha/interfaces/unit_of_work.py +32 -15
- alpha/interfaces/updatable.py +21 -0
- alpha/mixins/__init__.py +3 -1
- alpha/mixins/group_lifecycle.py +159 -0
- alpha/mixins/user_lifecycle.py +188 -0
- alpha/providers/database_provider.py +5 -4
- alpha/providers/ldap_provider.py +82 -66
- alpha/providers/models/credentials.py +28 -6
- alpha/providers/models/identity.py +16 -7
- alpha/providers/models/token.py +56 -0
- alpha/providers/oidc_provider.py +56 -33
- alpha/repositories/models/repository_model.py +1 -1
- alpha/repositories/rest_api_repository.py +23 -24
- alpha/repositories/sql_alchemy_repository.py +179 -133
- alpha/services/__init__.py +2 -0
- alpha/services/authentication_service.py +285 -57
- alpha/services/models/cookie.py +3 -49
- alpha/services/user_lifecycle_management.py +63 -0
- alpha/utils/__init__.py +4 -0
- alpha/utils/_http_codes.py +8 -0
- alpha/utils/cookie.py +51 -0
- alpha/utils/is_attrs.py +1 -1
- alpha/utils/is_pydantic.py +1 -1
- alpha/utils/logging_configurator.py +18 -16
- alpha/utils/logging_level_checker.py +3 -3
- alpha/utils/openapi_test/__init__.py +0 -0
- alpha/utils/openapi_test/container.py +181 -0
- alpha/utils/openapi_test/exceptions.py +17 -0
- alpha/utils/openapi_test/models.py +83 -0
- alpha/utils/openapi_test/orm.py +90 -0
- alpha/utils/openapi_test/service.py +172 -0
- alpha/utils/request_headers.py +116 -0
- alpha/utils/response_object.py +9 -7
- alpha/utils/secret_generator.py +2 -2
- {alpha_python-0.5.0.dist-info → alpha_python-0.6.0.dist-info}/METADATA +23 -6
- alpha_python-0.6.0.dist-info/RECORD +143 -0
- alpha/interfaces/updateable.py +0 -7
- alpha_python-0.5.0.dist-info/RECORD +0 -130
- {alpha_python-0.5.0.dist-info → alpha_python-0.6.0.dist-info}/WHEEL +0 -0
- {alpha_python-0.5.0.dist-info → alpha_python-0.6.0.dist-info}/entry_points.txt +0 -0
- {alpha_python-0.5.0.dist-info → alpha_python-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {alpha_python-0.5.0.dist-info → alpha_python-0.6.0.dist-info}/top_level.txt +0 -0
alpha/__init__.py
CHANGED
|
@@ -1,16 +1,25 @@
|
|
|
1
|
+
from importlib.metadata import version
|
|
2
|
+
|
|
1
3
|
from alpha.adapters.rest_api_unit_of_work import RestApiUnitOfWork
|
|
2
4
|
from alpha.adapters.sqla_unit_of_work import SqlAlchemyUnitOfWork
|
|
3
5
|
from alpha.factories.jwt_factory import JWTFactory
|
|
4
6
|
from alpha.factories.logging_handler_factory import LoggingHandlerFactory
|
|
5
7
|
from alpha.factories.model_class_factory import ModelClassFactory
|
|
6
8
|
from alpha.domain.models.user import User
|
|
7
|
-
from alpha.domain.models.
|
|
9
|
+
from alpha.domain.models.group import Group
|
|
10
|
+
from alpha.domain.models.role import Role
|
|
11
|
+
from alpha.domain.models.base_model import (
|
|
12
|
+
BaseDomainModel,
|
|
13
|
+
DomainModel,
|
|
14
|
+
DomainModelCovariant,
|
|
15
|
+
DomainModelContravariant,
|
|
16
|
+
)
|
|
8
17
|
from alpha.domain.models.life_cycle_base import LifeCycleBase
|
|
9
18
|
from alpha.infra.connectors.oidc_connector import (
|
|
10
19
|
OIDCConnector,
|
|
11
20
|
KeyCloakOIDCConnector,
|
|
12
21
|
)
|
|
13
|
-
from alpha.infra.
|
|
22
|
+
from alpha.infra.connectors.sql_alchemy import SqlAlchemyDatabase
|
|
14
23
|
from alpha.infra.models.filter_operators import And, Or
|
|
15
24
|
from alpha.infra.models.json_patch import JsonPatch
|
|
16
25
|
from alpha.infra.models.order_by import OrderBy, Order
|
|
@@ -19,7 +28,7 @@ from alpha.interfaces.attrs_instance import AttrsInstance
|
|
|
19
28
|
from alpha.interfaces.dataclass_instance import DataclassInstance
|
|
20
29
|
from alpha.interfaces.pydantic_instance import PydanticInstance
|
|
21
30
|
from alpha.interfaces.openapi_model import OpenAPIModel
|
|
22
|
-
from alpha.interfaces.
|
|
31
|
+
from alpha.interfaces.updatable import Updatable
|
|
23
32
|
from alpha.interfaces.patchable import Patchable
|
|
24
33
|
from alpha.interfaces.api_repository import ApiRepository
|
|
25
34
|
from alpha.interfaces.sql_repository import SqlRepository
|
|
@@ -36,6 +45,8 @@ from alpha.interfaces.providers import (
|
|
|
36
45
|
)
|
|
37
46
|
from alpha.interfaces.token_factory import TokenFactory
|
|
38
47
|
from alpha.mixins.jwt_provider import JWTProviderMixin
|
|
48
|
+
from alpha.mixins.user_lifecycle import UserLifecycleMixin
|
|
49
|
+
from alpha.mixins.group_lifecycle import GroupLifecycleMixin
|
|
39
50
|
from alpha.providers.models.identity import (
|
|
40
51
|
Identity,
|
|
41
52
|
DEFAULT_LDAP_MAPPINGS,
|
|
@@ -44,11 +55,17 @@ from alpha.providers.models.identity import (
|
|
|
44
55
|
)
|
|
45
56
|
from alpha.providers.models.credentials import PasswordCredentials
|
|
46
57
|
from alpha.providers.models.token import Token
|
|
47
|
-
from alpha.providers.oidc_provider import
|
|
58
|
+
from alpha.providers.oidc_provider import (
|
|
59
|
+
OIDCProvider,
|
|
60
|
+
KeyCloakProvider,
|
|
61
|
+
DEFAULT_OIDC_MAPPINGS,
|
|
62
|
+
DEFAULT_KEYCLOAK_MAPPINGS,
|
|
63
|
+
)
|
|
48
64
|
from alpha.repositories.models.repository_model import RepositoryModel
|
|
49
65
|
from alpha.repositories.rest_api_repository import RestApiRepository
|
|
50
66
|
from alpha.repositories.sql_alchemy_repository import SqlAlchemyRepository
|
|
51
67
|
from alpha.services.authentication_service import AuthenticationService
|
|
68
|
+
from alpha.services.user_lifecycle_management import UserLifecycleManagement
|
|
52
69
|
from alpha.utils.is_attrs import is_attrs
|
|
53
70
|
from alpha.utils.is_pydantic import is_pydantic
|
|
54
71
|
from alpha.utils.logging_configurator import (
|
|
@@ -56,11 +73,13 @@ from alpha.utils.logging_configurator import (
|
|
|
56
73
|
GunicornLogger,
|
|
57
74
|
)
|
|
58
75
|
from alpha.utils.logging_level_checker import logging_level_checker
|
|
76
|
+
from alpha.utils.request_headers import Headers
|
|
59
77
|
from alpha.utils.response_object import create_response_object
|
|
60
78
|
from alpha.utils.verify_identity import verify_identity
|
|
61
79
|
from alpha.utils.version_checker import minor_version_gte
|
|
62
80
|
from alpha.encoder import JSONEncoder
|
|
63
81
|
|
|
82
|
+
|
|
64
83
|
# Optional LDAP support - only import if ldap3 is available
|
|
65
84
|
try:
|
|
66
85
|
from alpha.infra.connectors.ldap_connector import (
|
|
@@ -75,6 +94,9 @@ try:
|
|
|
75
94
|
except ImportError:
|
|
76
95
|
_LDAP_AVAILABLE = False # type: ignore
|
|
77
96
|
|
|
97
|
+
|
|
98
|
+
__version__ = version("alpha-python")
|
|
99
|
+
|
|
78
100
|
__all__ = [
|
|
79
101
|
"RestApiUnitOfWork",
|
|
80
102
|
"SqlAlchemyUnitOfWork",
|
|
@@ -83,8 +105,12 @@ __all__ = [
|
|
|
83
105
|
"ModelClassFactory",
|
|
84
106
|
"BaseDomainModel",
|
|
85
107
|
"DomainModel",
|
|
108
|
+
"DomainModelCovariant",
|
|
109
|
+
"DomainModelContravariant",
|
|
86
110
|
"LifeCycleBase",
|
|
87
111
|
"User",
|
|
112
|
+
"Group",
|
|
113
|
+
"Role",
|
|
88
114
|
"OIDCConnector",
|
|
89
115
|
"KeyCloakOIDCConnector",
|
|
90
116
|
"SqlAlchemyDatabase",
|
|
@@ -99,7 +125,7 @@ __all__ = [
|
|
|
99
125
|
"DataclassInstance",
|
|
100
126
|
"PydanticInstance",
|
|
101
127
|
"OpenAPIModel",
|
|
102
|
-
"
|
|
128
|
+
"Updatable",
|
|
103
129
|
"Patchable",
|
|
104
130
|
"ApiRepository",
|
|
105
131
|
"SqlRepository",
|
|
@@ -114,6 +140,8 @@ __all__ = [
|
|
|
114
140
|
"TokenIssuer",
|
|
115
141
|
"TokenFactory",
|
|
116
142
|
"JWTProviderMixin",
|
|
143
|
+
"UserLifecycleMixin",
|
|
144
|
+
"GroupLifecycleMixin",
|
|
117
145
|
"Identity",
|
|
118
146
|
"DEFAULT_LDAP_MAPPINGS",
|
|
119
147
|
"DEFAULT_AD_MAPPINGS",
|
|
@@ -122,15 +150,19 @@ __all__ = [
|
|
|
122
150
|
"Token",
|
|
123
151
|
"OIDCProvider",
|
|
124
152
|
"KeyCloakProvider",
|
|
153
|
+
"DEFAULT_OIDC_MAPPINGS",
|
|
154
|
+
"DEFAULT_KEYCLOAK_MAPPINGS",
|
|
125
155
|
"RepositoryModel",
|
|
126
156
|
"RestApiRepository",
|
|
127
157
|
"SqlAlchemyRepository",
|
|
128
158
|
"AuthenticationService",
|
|
159
|
+
"UserLifecycleManagement",
|
|
129
160
|
"is_attrs",
|
|
130
161
|
"is_pydantic",
|
|
131
162
|
"LoggingConfigurator",
|
|
132
163
|
"GunicornLogger",
|
|
133
164
|
"logging_level_checker",
|
|
165
|
+
"Headers",
|
|
134
166
|
"create_response_object",
|
|
135
167
|
"verify_identity",
|
|
136
168
|
"minor_version_gte",
|
|
@@ -147,3 +179,27 @@ if _LDAP_AVAILABLE:
|
|
|
147
179
|
"ADProvider",
|
|
148
180
|
]
|
|
149
181
|
)
|
|
182
|
+
|
|
183
|
+
|
|
184
|
+
def _ensure_ast_str_compat() -> None:
|
|
185
|
+
"""Provide ast.Str compatibility for Python 3.14+.
|
|
186
|
+
|
|
187
|
+
Older Werkzeug versions (used by Flask 1.x) still instantiate ast.Str.
|
|
188
|
+
Python 3.14 removed that alias, so we recreate it with ast.Constant.
|
|
189
|
+
"""
|
|
190
|
+
if hasattr(ast, "Str"):
|
|
191
|
+
return
|
|
192
|
+
|
|
193
|
+
class _StrCompat(ast.Constant):
|
|
194
|
+
def __new__(cls, s: str = "", **kwargs): # noqa: N804
|
|
195
|
+
return ast.Constant(value=s, **kwargs)
|
|
196
|
+
|
|
197
|
+
setattr(ast, "Str", _StrCompat)
|
|
198
|
+
|
|
199
|
+
|
|
200
|
+
try:
|
|
201
|
+
import ast
|
|
202
|
+
|
|
203
|
+
_ensure_ast_str_compat()
|
|
204
|
+
except ImportError:
|
|
205
|
+
pass
|
|
@@ -11,7 +11,13 @@ UOW = TypeVar("UOW", bound="RestApiUnitOfWork")
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
class RestApiUnitOfWork:
|
|
14
|
-
"""Unit of Work implementation for REST API interactions.
|
|
14
|
+
"""Unit of Work implementation for REST API interactions.
|
|
15
|
+
|
|
16
|
+
This class manages the lifecycle of a shared HTTP session and provides
|
|
17
|
+
access to configured repositories for API interactions. It does not support
|
|
18
|
+
transactional operations like commit, flush, rollback, or refresh, as these
|
|
19
|
+
concepts do not apply to REST API interactions.
|
|
20
|
+
"""
|
|
15
21
|
|
|
16
22
|
def __init__(
|
|
17
23
|
self,
|
|
@@ -22,9 +28,9 @@ class RestApiUnitOfWork:
|
|
|
22
28
|
|
|
23
29
|
Parameters
|
|
24
30
|
----------
|
|
25
|
-
repos
|
|
31
|
+
repos
|
|
26
32
|
The list of repository models to use.
|
|
27
|
-
session
|
|
33
|
+
session
|
|
28
34
|
The requests session (or compatible HTTP client, e.g., httpx) to
|
|
29
35
|
use for context management, by default None
|
|
30
36
|
|
|
@@ -12,7 +12,12 @@ UOW = TypeVar("UOW", bound="SqlAlchemyUnitOfWork")
|
|
|
12
12
|
|
|
13
13
|
|
|
14
14
|
class SqlAlchemyUnitOfWork:
|
|
15
|
-
"""Unit of Work implementation for SQLAlchemy databases.
|
|
15
|
+
"""Unit of Work implementation for SQLAlchemy databases.
|
|
16
|
+
|
|
17
|
+
This class manages the lifecycle of a SQLAlchemy session and provides
|
|
18
|
+
access to configured repositories for database interactions. It supports
|
|
19
|
+
transactional operations such as commit, flush, rollback, and refresh.
|
|
20
|
+
"""
|
|
16
21
|
|
|
17
22
|
def __init__(
|
|
18
23
|
self,
|
|
@@ -23,9 +28,9 @@ class SqlAlchemyUnitOfWork:
|
|
|
23
28
|
|
|
24
29
|
Parameters
|
|
25
30
|
----------
|
|
26
|
-
db
|
|
31
|
+
db
|
|
27
32
|
The database instance to use.
|
|
28
|
-
repos
|
|
33
|
+
repos
|
|
29
34
|
The list of repository models to use.
|
|
30
35
|
|
|
31
36
|
Raises
|
|
@@ -45,6 +50,7 @@ class SqlAlchemyUnitOfWork:
|
|
|
45
50
|
|
|
46
51
|
Returns
|
|
47
52
|
-------
|
|
53
|
+
UOW
|
|
48
54
|
The Unit of Work instance.
|
|
49
55
|
|
|
50
56
|
Raises
|
|
@@ -107,7 +113,13 @@ class SqlAlchemyUnitOfWork:
|
|
|
107
113
|
self._session.rollback()
|
|
108
114
|
|
|
109
115
|
def refresh(self, obj: object) -> None:
|
|
110
|
-
"""Refresh the state of a given object.
|
|
116
|
+
"""Refresh the state of a given object.
|
|
117
|
+
|
|
118
|
+
Parameters
|
|
119
|
+
----------
|
|
120
|
+
obj
|
|
121
|
+
The object to refresh.
|
|
122
|
+
"""
|
|
111
123
|
if not self._session:
|
|
112
124
|
raise exceptions.DatabaseSessionError(
|
|
113
125
|
"No active database session is defined"
|
|
@@ -116,5 +128,11 @@ class SqlAlchemyUnitOfWork:
|
|
|
116
128
|
|
|
117
129
|
@property
|
|
118
130
|
def session(self) -> Session | None:
|
|
119
|
-
"""Get the current database session.
|
|
131
|
+
"""Get the current database session.
|
|
132
|
+
|
|
133
|
+
Returns
|
|
134
|
+
-------
|
|
135
|
+
Session | None
|
|
136
|
+
The current database session.
|
|
137
|
+
"""
|
|
120
138
|
return self._session
|
alpha/containers/container.py
CHANGED
|
@@ -8,13 +8,18 @@ from alpha.handlers.models.argument import Argument
|
|
|
8
8
|
|
|
9
9
|
|
|
10
10
|
class Container(containers.DeclarativeContainer):
|
|
11
|
-
"""Dependency injection container for the alpha package.
|
|
11
|
+
"""Dependency injection container for the alpha package.
|
|
12
|
+
|
|
13
|
+
The container is used to manage the dependencies of the alpha package,
|
|
14
|
+
including the API generation and running commands. It allows for easy
|
|
15
|
+
configuration and extension of the commands and their dependencies.
|
|
16
|
+
"""
|
|
12
17
|
|
|
13
18
|
config = providers.Configuration()
|
|
14
19
|
|
|
15
20
|
api_gen_command = providers.Factory(
|
|
16
21
|
Command,
|
|
17
|
-
name=
|
|
22
|
+
name="gen",
|
|
18
23
|
help=(
|
|
19
24
|
"Generate the API code and watch OpenAPI spec file. The API code "
|
|
20
25
|
"will be generated into the ./api folder."
|
|
@@ -23,46 +28,46 @@ class Container(containers.DeclarativeContainer):
|
|
|
23
28
|
arguments=providers.List(
|
|
24
29
|
providers.Factory(
|
|
25
30
|
Argument,
|
|
26
|
-
default=
|
|
27
|
-
name=
|
|
28
|
-
help=
|
|
31
|
+
default="specification/openapi.yaml",
|
|
32
|
+
name="--spec-file",
|
|
33
|
+
help="Path to the specification file",
|
|
29
34
|
args={
|
|
30
|
-
|
|
31
|
-
|
|
35
|
+
"type": str,
|
|
36
|
+
"nargs": "?",
|
|
32
37
|
},
|
|
33
38
|
),
|
|
34
39
|
providers.Factory(
|
|
35
40
|
Argument,
|
|
36
41
|
default=config.api_package_name,
|
|
37
|
-
name=
|
|
42
|
+
name="--api-package",
|
|
38
43
|
help=(
|
|
39
44
|
"Name of the API package to generate. Automatically "
|
|
40
45
|
"determined or guessed. If incorrect, "
|
|
41
46
|
"just use this argument."
|
|
42
47
|
),
|
|
43
48
|
args={
|
|
44
|
-
|
|
45
|
-
|
|
49
|
+
"type": str,
|
|
50
|
+
"nargs": "?",
|
|
46
51
|
},
|
|
47
52
|
),
|
|
48
53
|
providers.Factory(
|
|
49
54
|
Argument,
|
|
50
55
|
default=config.service_package_name,
|
|
51
|
-
name=
|
|
56
|
+
name="--service-package",
|
|
52
57
|
help=(
|
|
53
58
|
"Name of the service package to use. Automatically "
|
|
54
59
|
"determined or guessed. If incorrect, "
|
|
55
60
|
"just use this argument."
|
|
56
61
|
),
|
|
57
62
|
args={
|
|
58
|
-
|
|
59
|
-
|
|
63
|
+
"type": str,
|
|
64
|
+
"nargs": "?",
|
|
60
65
|
},
|
|
61
66
|
),
|
|
62
67
|
providers.Factory(
|
|
63
68
|
Argument,
|
|
64
69
|
default=config.container_import,
|
|
65
|
-
name=
|
|
70
|
+
name="--container-import",
|
|
66
71
|
help=(
|
|
67
72
|
"Name of the container to use. Automatically "
|
|
68
73
|
"determined or guessed. If incorrect, "
|
|
@@ -70,76 +75,76 @@ class Container(containers.DeclarativeContainer):
|
|
|
70
75
|
"use empty string for this variable"
|
|
71
76
|
),
|
|
72
77
|
args={
|
|
73
|
-
|
|
74
|
-
|
|
78
|
+
"type": str,
|
|
79
|
+
"nargs": "?",
|
|
75
80
|
},
|
|
76
81
|
),
|
|
77
82
|
providers.Factory(
|
|
78
83
|
Argument,
|
|
79
84
|
default=config.init_container_from,
|
|
80
|
-
name=
|
|
85
|
+
name="--init-container-from",
|
|
81
86
|
help=(
|
|
82
87
|
"Location of where the container initialize function "
|
|
83
88
|
"should be imported from."
|
|
84
89
|
),
|
|
85
90
|
args={
|
|
86
|
-
|
|
87
|
-
|
|
91
|
+
"type": str,
|
|
92
|
+
"nargs": "?",
|
|
88
93
|
},
|
|
89
94
|
),
|
|
90
95
|
providers.Factory(
|
|
91
96
|
Argument,
|
|
92
97
|
default=config.init_container_function,
|
|
93
|
-
name=
|
|
98
|
+
name="--init-container-function",
|
|
94
99
|
help="Name of the container initialize function.",
|
|
95
100
|
args={
|
|
96
|
-
|
|
97
|
-
|
|
101
|
+
"type": str,
|
|
102
|
+
"nargs": "?",
|
|
98
103
|
},
|
|
99
104
|
),
|
|
100
105
|
providers.Factory(
|
|
101
106
|
Argument,
|
|
102
|
-
default=
|
|
103
|
-
name=
|
|
107
|
+
default="post_process.py",
|
|
108
|
+
name="--post-process-file",
|
|
104
109
|
help="Path to the post process file to use after generation",
|
|
105
110
|
args={
|
|
106
|
-
|
|
107
|
-
|
|
111
|
+
"type": str,
|
|
112
|
+
"nargs": "?",
|
|
108
113
|
},
|
|
109
114
|
),
|
|
110
115
|
providers.Factory(
|
|
111
116
|
Argument,
|
|
112
|
-
default=
|
|
113
|
-
name=
|
|
117
|
+
default="python-flask",
|
|
118
|
+
name="--generator-name",
|
|
114
119
|
help="Name of the openapi generator to use",
|
|
115
120
|
args={
|
|
116
|
-
|
|
117
|
-
|
|
121
|
+
"type": str,
|
|
122
|
+
"nargs": "?",
|
|
118
123
|
},
|
|
119
124
|
),
|
|
120
125
|
providers.Factory(
|
|
121
126
|
Argument,
|
|
122
127
|
default=False,
|
|
123
|
-
name=
|
|
128
|
+
name="--no-watch",
|
|
124
129
|
help=(
|
|
125
130
|
"To prevent watching the spec file for changes and only "
|
|
126
131
|
"run the generation once."
|
|
127
132
|
),
|
|
128
133
|
args={
|
|
129
|
-
|
|
134
|
+
"action": "store_true",
|
|
130
135
|
},
|
|
131
136
|
),
|
|
132
137
|
providers.Factory(
|
|
133
138
|
Argument,
|
|
134
139
|
default=False,
|
|
135
|
-
name=
|
|
140
|
+
name="--templates-only",
|
|
136
141
|
help=(
|
|
137
142
|
"Only create a templates folder containing openapi "
|
|
138
143
|
"mustache templates in the current working directory. "
|
|
139
144
|
"Skip generation of API code."
|
|
140
145
|
),
|
|
141
146
|
args={
|
|
142
|
-
|
|
147
|
+
"action": "store_true",
|
|
143
148
|
},
|
|
144
149
|
),
|
|
145
150
|
),
|
|
@@ -147,28 +152,28 @@ class Container(containers.DeclarativeContainer):
|
|
|
147
152
|
|
|
148
153
|
api_run_command = providers.Factory(
|
|
149
154
|
Command,
|
|
150
|
-
name=
|
|
151
|
-
help=
|
|
155
|
+
name="run",
|
|
156
|
+
help="Run API in development mode",
|
|
152
157
|
handler=providers.Factory(ApiRunHandler),
|
|
153
158
|
arguments=providers.List(
|
|
154
159
|
providers.Factory(
|
|
155
160
|
Argument,
|
|
156
161
|
default=config.api_package_name,
|
|
157
|
-
name=
|
|
158
|
-
help=
|
|
162
|
+
name="--api-package",
|
|
163
|
+
help="Name of the API package to generate",
|
|
159
164
|
args={
|
|
160
|
-
|
|
161
|
-
|
|
165
|
+
"type": str,
|
|
166
|
+
"nargs": "?",
|
|
162
167
|
},
|
|
163
168
|
),
|
|
164
169
|
providers.Factory(
|
|
165
170
|
Argument,
|
|
166
|
-
default=
|
|
167
|
-
name=
|
|
168
|
-
help=
|
|
171
|
+
default="8080",
|
|
172
|
+
name="--port",
|
|
173
|
+
help="Port to run server on",
|
|
169
174
|
args={
|
|
170
|
-
|
|
171
|
-
|
|
175
|
+
"type": str,
|
|
176
|
+
"nargs": "?",
|
|
172
177
|
},
|
|
173
178
|
),
|
|
174
179
|
),
|
|
@@ -176,8 +181,8 @@ class Container(containers.DeclarativeContainer):
|
|
|
176
181
|
|
|
177
182
|
api_section = providers.Factory(
|
|
178
183
|
Section,
|
|
179
|
-
name=
|
|
180
|
-
description=
|
|
184
|
+
name="api",
|
|
185
|
+
description="OpenAPI development commands.",
|
|
181
186
|
help=(
|
|
182
187
|
"All commands for generating and developing an API using the "
|
|
183
188
|
"OpenAPI standards"
|
alpha/domain/__init__.py
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
from alpha.domain.models.user import User
|
|
2
|
-
from alpha.domain.models.
|
|
2
|
+
from alpha.domain.models.group import Group
|
|
3
|
+
from alpha.domain.models.role import Role
|
|
4
|
+
from alpha.domain.models.base_model import (
|
|
5
|
+
BaseDomainModel,
|
|
6
|
+
DomainModel,
|
|
7
|
+
DomainModelCovariant,
|
|
8
|
+
DomainModelContravariant,
|
|
9
|
+
)
|
|
3
10
|
from alpha.domain.models.life_cycle_base import LifeCycleBase
|
|
4
11
|
|
|
5
12
|
__all__ = [
|
|
6
13
|
"BaseDomainModel",
|
|
7
14
|
"DomainModel",
|
|
15
|
+
"DomainModelCovariant",
|
|
16
|
+
"DomainModelContravariant",
|
|
8
17
|
"LifeCycleBase",
|
|
9
18
|
"User",
|
|
19
|
+
"Group",
|
|
20
|
+
"Role",
|
|
10
21
|
]
|
alpha/domain/models/__init__.py
CHANGED
|
@@ -1,10 +1,21 @@
|
|
|
1
1
|
from alpha.domain.models.user import User
|
|
2
|
-
from alpha.domain.models.
|
|
2
|
+
from alpha.domain.models.group import Group
|
|
3
|
+
from alpha.domain.models.role import Role
|
|
4
|
+
from alpha.domain.models.base_model import (
|
|
5
|
+
BaseDomainModel,
|
|
6
|
+
DomainModel,
|
|
7
|
+
DomainModelCovariant,
|
|
8
|
+
DomainModelContravariant,
|
|
9
|
+
)
|
|
3
10
|
from alpha.domain.models.life_cycle_base import LifeCycleBase
|
|
4
11
|
|
|
5
12
|
__all__ = [
|
|
6
13
|
"BaseDomainModel",
|
|
7
14
|
"DomainModel",
|
|
15
|
+
"DomainModelCovariant",
|
|
16
|
+
"DomainModelContravariant",
|
|
8
17
|
"LifeCycleBase",
|
|
9
18
|
"User",
|
|
19
|
+
"Group",
|
|
20
|
+
"Role",
|
|
10
21
|
]
|
|
@@ -12,7 +12,24 @@ DomainModelContravariant = TypeVar(
|
|
|
12
12
|
|
|
13
13
|
@dataclass
|
|
14
14
|
class BaseDomainModel:
|
|
15
|
+
"""Base class for all domain models which can be inherited by all domain
|
|
16
|
+
models in the application.
|
|
17
|
+
|
|
18
|
+
This class is mainly being used to provide a common interface for all
|
|
19
|
+
domain models, and to provide a common method for converting the domain
|
|
20
|
+
model instance to a dictionary.
|
|
21
|
+
|
|
22
|
+
It has no attributes of its own.
|
|
23
|
+
"""
|
|
24
|
+
|
|
15
25
|
def to_dict(self) -> dict[str, Any]:
|
|
26
|
+
"""Convert the domain model instance to a dictionary.
|
|
27
|
+
|
|
28
|
+
Returns
|
|
29
|
+
-------
|
|
30
|
+
dict[str, Any]
|
|
31
|
+
A dictionary representation of the domain model instance.
|
|
32
|
+
"""
|
|
16
33
|
obj: dict[str, Any] = {}
|
|
17
34
|
for attr in self.__dataclass_fields__.keys():
|
|
18
35
|
if not attr.startswith("_"):
|
|
@@ -22,4 +39,23 @@ class BaseDomainModel:
|
|
|
22
39
|
return obj
|
|
23
40
|
|
|
24
41
|
def update(self, obj: DomainModel) -> DomainModel:
|
|
25
|
-
|
|
42
|
+
"""Update the current instance with the values from another instance.
|
|
43
|
+
|
|
44
|
+
Parameters
|
|
45
|
+
----------
|
|
46
|
+
obj
|
|
47
|
+
Object to update the current instance with.
|
|
48
|
+
|
|
49
|
+
Returns
|
|
50
|
+
-------
|
|
51
|
+
DomainModel
|
|
52
|
+
The updated instance of the domain model.
|
|
53
|
+
|
|
54
|
+
Raises
|
|
55
|
+
------
|
|
56
|
+
NotImplementedError
|
|
57
|
+
If the method is not implemented in the subclass.
|
|
58
|
+
"""
|
|
59
|
+
raise NotImplementedError(
|
|
60
|
+
"Subclasses must implement the update method"
|
|
61
|
+
)
|
alpha/domain/models/group.py
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from dataclasses import dataclass
|
|
2
2
|
from datetime import datetime, timezone
|
|
3
|
-
from typing import Sequence, cast
|
|
3
|
+
from typing import Any, Sequence, cast
|
|
4
4
|
from uuid import UUID
|
|
5
5
|
|
|
6
6
|
from alpha.domain.models.base_model import BaseDomainModel, DomainModel
|
|
@@ -9,12 +9,52 @@ from alpha.domain.models.life_cycle_base import LifeCycleBase
|
|
|
9
9
|
|
|
10
10
|
@dataclass(kw_only=True)
|
|
11
11
|
class Group(LifeCycleBase, BaseDomainModel):
|
|
12
|
+
"""Group domain model which represents a group of users with specific
|
|
13
|
+
permissions. The Group model includes attributes for group identification,
|
|
14
|
+
description, permissions, and lifecycle attributes for tracking creation
|
|
15
|
+
and modification times.
|
|
16
|
+
|
|
17
|
+
Attributes
|
|
18
|
+
----------
|
|
19
|
+
id
|
|
20
|
+
Unique identifier for the group, which can be a UUID, integer, or
|
|
21
|
+
string.
|
|
22
|
+
name
|
|
23
|
+
The name of the group, used for identification and display purposes.
|
|
24
|
+
description
|
|
25
|
+
A brief description of the group and its purpose.
|
|
26
|
+
permissions
|
|
27
|
+
A list of specific permissions assigned to the group, which can be used
|
|
28
|
+
for group-based access control. Each permission can represent a
|
|
29
|
+
specific action or resource that the group has access to.
|
|
30
|
+
is_active
|
|
31
|
+
A boolean flag indicating whether the group is active. Inactive groups
|
|
32
|
+
may not be able to be assigned to users or may not grant permissions to
|
|
33
|
+
users assigned to the group.
|
|
34
|
+
"""
|
|
35
|
+
|
|
12
36
|
id: UUID | int | str | None = None
|
|
13
37
|
name: str | None = None
|
|
14
38
|
description: str | None = None
|
|
15
39
|
permissions: Sequence[str] | None = None
|
|
16
40
|
is_active: bool = True
|
|
17
41
|
|
|
42
|
+
def to_dict(self) -> dict[str, Any]:
|
|
43
|
+
"""Convert the Group instance to a dictionary.
|
|
44
|
+
|
|
45
|
+
Returns
|
|
46
|
+
-------
|
|
47
|
+
dict[str, Any]
|
|
48
|
+
A dictionary representation of the Group instance.
|
|
49
|
+
"""
|
|
50
|
+
return {
|
|
51
|
+
"id": self.id,
|
|
52
|
+
"name": self.name,
|
|
53
|
+
"description": self.description,
|
|
54
|
+
"permissions": self.permissions,
|
|
55
|
+
"is_active": self.is_active,
|
|
56
|
+
}
|
|
57
|
+
|
|
18
58
|
def update(self, obj: DomainModel) -> DomainModel:
|
|
19
59
|
"""Update the Group instance with data from another Group instance.
|
|
20
60
|
|
|
@@ -22,6 +62,16 @@ class Group(LifeCycleBase, BaseDomainModel):
|
|
|
22
62
|
----------
|
|
23
63
|
obj
|
|
24
64
|
Group object to update from.
|
|
65
|
+
|
|
66
|
+
Returns
|
|
67
|
+
-------
|
|
68
|
+
DomainModel
|
|
69
|
+
The updated instance of the Group.
|
|
70
|
+
|
|
71
|
+
Raises
|
|
72
|
+
------
|
|
73
|
+
TypeError
|
|
74
|
+
If the provided object is not a Group instance.
|
|
25
75
|
"""
|
|
26
76
|
if not isinstance(obj, Group):
|
|
27
77
|
raise TypeError("Group.update expects a Group instance.")
|
|
@@ -4,6 +4,25 @@ from datetime import datetime
|
|
|
4
4
|
|
|
5
5
|
@dataclass
|
|
6
6
|
class LifeCycleBase:
|
|
7
|
+
"""Base class for lifecycle model attributes which can be inherited by all
|
|
8
|
+
domain models in the application.
|
|
9
|
+
|
|
10
|
+
Attributes
|
|
11
|
+
----------
|
|
12
|
+
created_by
|
|
13
|
+
The identifier of the user who created the instance. This can be a
|
|
14
|
+
string or None.
|
|
15
|
+
created_at
|
|
16
|
+
The timestamp when the instance was created. This can be a datetime
|
|
17
|
+
object or None.
|
|
18
|
+
modified_by
|
|
19
|
+
The identifier of the user who last modified the instance. This can be
|
|
20
|
+
a string or None.
|
|
21
|
+
modified_at
|
|
22
|
+
The timestamp when the instance was last modified. This can be a
|
|
23
|
+
datetime object or None.
|
|
24
|
+
"""
|
|
25
|
+
|
|
7
26
|
created_by: str | None = None
|
|
8
27
|
created_at: datetime | None = None
|
|
9
28
|
modified_by: str | None = None
|