alpha-python 0.5.1__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 +57 -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 +78 -65
- 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 +54 -16
- 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 -15
- 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 +115 -15
- 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 +7 -7
- 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 +15 -6
- alpha/providers/models/token.py +56 -0
- alpha/providers/oidc_provider.py +56 -33
- alpha/repositories/rest_api_repository.py +23 -24
- alpha/repositories/sql_alchemy_repository.py +179 -133
- alpha/services/authentication_service.py +264 -47
- alpha/services/models/cookie.py +3 -49
- alpha/services/user_lifecycle_management.py +17 -285
- alpha/utils/__init__.py +2 -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 +8 -0
- alpha/utils/response_object.py +9 -7
- alpha/utils/secret_generator.py +2 -2
- {alpha_python-0.5.1.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.1.dist-info/RECORD +0 -132
- {alpha_python-0.5.1.dist-info → alpha_python-0.6.0.dist-info}/WHEEL +0 -0
- {alpha_python-0.5.1.dist-info → alpha_python-0.6.0.dist-info}/entry_points.txt +0 -0
- {alpha_python-0.5.1.dist-info → alpha_python-0.6.0.dist-info}/licenses/LICENSE +0 -0
- {alpha_python-0.5.1.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,7 +55,12 @@ 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
|
|
@@ -63,6 +79,7 @@ from alpha.utils.verify_identity import verify_identity
|
|
|
63
79
|
from alpha.utils.version_checker import minor_version_gte
|
|
64
80
|
from alpha.encoder import JSONEncoder
|
|
65
81
|
|
|
82
|
+
|
|
66
83
|
# Optional LDAP support - only import if ldap3 is available
|
|
67
84
|
try:
|
|
68
85
|
from alpha.infra.connectors.ldap_connector import (
|
|
@@ -77,6 +94,9 @@ try:
|
|
|
77
94
|
except ImportError:
|
|
78
95
|
_LDAP_AVAILABLE = False # type: ignore
|
|
79
96
|
|
|
97
|
+
|
|
98
|
+
__version__ = version("alpha-python")
|
|
99
|
+
|
|
80
100
|
__all__ = [
|
|
81
101
|
"RestApiUnitOfWork",
|
|
82
102
|
"SqlAlchemyUnitOfWork",
|
|
@@ -85,8 +105,12 @@ __all__ = [
|
|
|
85
105
|
"ModelClassFactory",
|
|
86
106
|
"BaseDomainModel",
|
|
87
107
|
"DomainModel",
|
|
108
|
+
"DomainModelCovariant",
|
|
109
|
+
"DomainModelContravariant",
|
|
88
110
|
"LifeCycleBase",
|
|
89
111
|
"User",
|
|
112
|
+
"Group",
|
|
113
|
+
"Role",
|
|
90
114
|
"OIDCConnector",
|
|
91
115
|
"KeyCloakOIDCConnector",
|
|
92
116
|
"SqlAlchemyDatabase",
|
|
@@ -101,7 +125,7 @@ __all__ = [
|
|
|
101
125
|
"DataclassInstance",
|
|
102
126
|
"PydanticInstance",
|
|
103
127
|
"OpenAPIModel",
|
|
104
|
-
"
|
|
128
|
+
"Updatable",
|
|
105
129
|
"Patchable",
|
|
106
130
|
"ApiRepository",
|
|
107
131
|
"SqlRepository",
|
|
@@ -116,6 +140,8 @@ __all__ = [
|
|
|
116
140
|
"TokenIssuer",
|
|
117
141
|
"TokenFactory",
|
|
118
142
|
"JWTProviderMixin",
|
|
143
|
+
"UserLifecycleMixin",
|
|
144
|
+
"GroupLifecycleMixin",
|
|
119
145
|
"Identity",
|
|
120
146
|
"DEFAULT_LDAP_MAPPINGS",
|
|
121
147
|
"DEFAULT_AD_MAPPINGS",
|
|
@@ -124,6 +150,8 @@ __all__ = [
|
|
|
124
150
|
"Token",
|
|
125
151
|
"OIDCProvider",
|
|
126
152
|
"KeyCloakProvider",
|
|
153
|
+
"DEFAULT_OIDC_MAPPINGS",
|
|
154
|
+
"DEFAULT_KEYCLOAK_MAPPINGS",
|
|
127
155
|
"RepositoryModel",
|
|
128
156
|
"RestApiRepository",
|
|
129
157
|
"SqlAlchemyRepository",
|
|
@@ -151,3 +179,27 @@ if _LDAP_AVAILABLE:
|
|
|
151
179
|
"ADProvider",
|
|
152
180
|
]
|
|
153
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
|