architectonics 0.0.27__tar.gz → 0.0.29__tar.gz
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.
- {architectonics-0.0.27 → architectonics-0.0.29}/PKG-INFO +1 -1
- architectonics-0.0.29/architectonics/core/result/error_message.py +6 -0
- architectonics-0.0.29/architectonics/core/result/service_result.py +71 -0
- architectonics-0.0.29/architectonics/core/services/base_schemas.py +21 -0
- architectonics-0.0.29/architectonics/core/services/base_service.py +114 -0
- architectonics-0.0.29/architectonics/core/validation/base_model_validation_errors.py +14 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/repositories/base_repository.py +6 -6
- {architectonics-0.0.27 → architectonics-0.0.29}/pyproject.toml +1 -1
- {architectonics-0.0.27 → architectonics-0.0.29}/setup.py +4 -1
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/common/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/common/utils/utils.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/core/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/core/config/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/core/config/application_settings.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/core/factory/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/core/factory/factory.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/core/models/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/core/models/base_model.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/config/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/config/database.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/config/database_settings.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/entities/base_entity.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/repositories/__init__.py +0 -0
- {architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/repositories/base_exceptions.py +0 -0
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Generic, TypeVar
|
|
4
|
+
|
|
5
|
+
from architectonics.core.result.error_message import ErrorMessage
|
|
6
|
+
|
|
7
|
+
TValue = TypeVar("TValue")
|
|
8
|
+
TValidationErrors = TypeVar("TValidationErrors")
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class ServiceResult(
|
|
12
|
+
Generic[
|
|
13
|
+
TValue,
|
|
14
|
+
TValidationErrors,
|
|
15
|
+
],
|
|
16
|
+
):
|
|
17
|
+
__slots__ = (
|
|
18
|
+
"_value",
|
|
19
|
+
"_validation_errors",
|
|
20
|
+
"_error_message",
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
def __init__(
|
|
24
|
+
self,
|
|
25
|
+
value: TValue | None = None,
|
|
26
|
+
validation_errors: TValidationErrors | None = None,
|
|
27
|
+
error_message: ErrorMessage | None = None,
|
|
28
|
+
) -> None:
|
|
29
|
+
self._value = value
|
|
30
|
+
self._validation_errors = validation_errors
|
|
31
|
+
self._error_message = error_message
|
|
32
|
+
|
|
33
|
+
@property
|
|
34
|
+
def value(self) -> TValue | None:
|
|
35
|
+
return self._value
|
|
36
|
+
|
|
37
|
+
@property
|
|
38
|
+
def validation_errors(self) -> TValidationErrors | None:
|
|
39
|
+
return self._validation_errors
|
|
40
|
+
|
|
41
|
+
@property
|
|
42
|
+
def error_message(self) -> ErrorMessage | None:
|
|
43
|
+
return self._error_message
|
|
44
|
+
|
|
45
|
+
@property
|
|
46
|
+
def is_validation_error(self) -> bool:
|
|
47
|
+
return self._validation_errors is not None
|
|
48
|
+
|
|
49
|
+
@property
|
|
50
|
+
def is_success(self) -> bool:
|
|
51
|
+
return self._value is not None and self._validation_errors is None and self._error_message is None
|
|
52
|
+
|
|
53
|
+
@classmethod
|
|
54
|
+
def success(cls, value: TValue) -> ServiceResult[TValue, TValidationErrors]:
|
|
55
|
+
return cls(
|
|
56
|
+
value=value,
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
@classmethod
|
|
60
|
+
def validation_failure(cls, validation_errors: TValidationErrors) -> ServiceResult[TValue, TValidationErrors]:
|
|
61
|
+
return cls(
|
|
62
|
+
validation_errors=validation_errors,
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def failure(cls, error_message: str) -> ServiceResult[TValue, TValidationErrors]:
|
|
67
|
+
return cls(
|
|
68
|
+
error_message=ErrorMessage(
|
|
69
|
+
message=error_message,
|
|
70
|
+
),
|
|
71
|
+
)
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
from pydantic import BaseModel
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class BaseModelCreateSchema(BaseModel):
|
|
5
|
+
pass
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BaseModelCreateErrorsSchema(BaseModel):
|
|
9
|
+
pass
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
class BaseModelGetSchema(BaseModel):
|
|
13
|
+
pass
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class BaseModelUpdateSchema(BaseModel):
|
|
17
|
+
pass
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
class BaseModelUpdateErrorsSchema(BaseModel):
|
|
21
|
+
pass
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
from starlette.status import (
|
|
2
|
+
HTTP_200_OK,
|
|
3
|
+
HTTP_404_NOT_FOUND,
|
|
4
|
+
HTTP_409_CONFLICT,
|
|
5
|
+
HTTP_422_UNPROCESSABLE_CONTENT,
|
|
6
|
+
)
|
|
7
|
+
|
|
8
|
+
from architectonics.core.models.base_model import BaseModel
|
|
9
|
+
from architectonics.core.result.service_result import ServiceResult
|
|
10
|
+
from architectonics.core.services.base_schemas import (
|
|
11
|
+
BaseModelCreateSchema,
|
|
12
|
+
BaseModelUpdateSchema,
|
|
13
|
+
)
|
|
14
|
+
from architectonics.core.validation.base_model_validation_errors import BaseModelValidationErrors
|
|
15
|
+
from architectonics.infrastructure.repositories.base_exceptions import (
|
|
16
|
+
IntegrityErrorException,
|
|
17
|
+
ObjectAlreadyExistsException,
|
|
18
|
+
ObjectNotFoundException,
|
|
19
|
+
)
|
|
20
|
+
from architectonics.infrastructure.repositories.base_repository import BaseRepository
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class BaseService:
|
|
24
|
+
_repository: BaseRepository = NotImplemented
|
|
25
|
+
|
|
26
|
+
async def get_models_list(
|
|
27
|
+
self,
|
|
28
|
+
) -> ServiceResult[list[BaseModel], BaseModelValidationErrors]:
|
|
29
|
+
|
|
30
|
+
models = await self._repository.get_models_list()
|
|
31
|
+
|
|
32
|
+
return ServiceResult[list[BaseModel], BaseModelValidationErrors].success(models)
|
|
33
|
+
|
|
34
|
+
"""async def validate(self, model: BaseModel) -> BaseModelValidationErrors:
|
|
35
|
+
|
|
36
|
+
errors = BaseModelValidationErrors()
|
|
37
|
+
|
|
38
|
+
return errors
|
|
39
|
+
|
|
40
|
+
async def create_model(
|
|
41
|
+
self,
|
|
42
|
+
model: BaseModel,
|
|
43
|
+
) -> ServiceResult[BaseModel, BaseModelValidationErrors]:
|
|
44
|
+
|
|
45
|
+
model_errors = await self.validate(model)
|
|
46
|
+
|
|
47
|
+
if model_errors.has_errors():
|
|
48
|
+
return None, model_errors, HTTP_422_UNPROCESSABLE_CONTENT
|
|
49
|
+
|
|
50
|
+
try:
|
|
51
|
+
model = await self._repository.create_model(
|
|
52
|
+
values=values,
|
|
53
|
+
)
|
|
54
|
+
except ObjectAlreadyExistsException:
|
|
55
|
+
return None, "object_already_exist", HTTP_409_CONFLICT
|
|
56
|
+
|
|
57
|
+
return model, None, HTTP_200_OK
|
|
58
|
+
|
|
59
|
+
async def get_model(
|
|
60
|
+
self,
|
|
61
|
+
model_id: str,
|
|
62
|
+
) -> tuple[BaseModel | None, str | None, int]:
|
|
63
|
+
|
|
64
|
+
try:
|
|
65
|
+
model = await self._repository.get_model(
|
|
66
|
+
model_id=model_id,
|
|
67
|
+
)
|
|
68
|
+
except ObjectNotFoundException:
|
|
69
|
+
return None, "object_not_found", HTTP_404_NOT_FOUND
|
|
70
|
+
|
|
71
|
+
return model, None, HTTP_200_OK
|
|
72
|
+
|
|
73
|
+
async def update_model(
|
|
74
|
+
self,
|
|
75
|
+
model_id: str,
|
|
76
|
+
update_schema: BaseModelUpdateSchema,
|
|
77
|
+
) -> tuple[BaseModel | None, str | dict[str, list[str]] | None, int]:
|
|
78
|
+
|
|
79
|
+
schema_dict = update_schema.model_dump(by_alias=False)
|
|
80
|
+
|
|
81
|
+
attrs, errors = await self._validate_values(**schema_dict)
|
|
82
|
+
|
|
83
|
+
if errors:
|
|
84
|
+
return None, errors, HTTP_422_UNPROCESSABLE_CONTENT
|
|
85
|
+
|
|
86
|
+
try:
|
|
87
|
+
model = await self._repository.update_model(
|
|
88
|
+
model_id=model_id,
|
|
89
|
+
values=attrs,
|
|
90
|
+
)
|
|
91
|
+
except IntegrityErrorException as e:
|
|
92
|
+
return None, f"{e}", HTTP_404_NOT_FOUND
|
|
93
|
+
except ObjectNotFoundException:
|
|
94
|
+
return None, "object_not_found", HTTP_404_NOT_FOUND
|
|
95
|
+
|
|
96
|
+
return model, None, HTTP_200_OK
|
|
97
|
+
|
|
98
|
+
async def delete_model(
|
|
99
|
+
self,
|
|
100
|
+
model_id: str,
|
|
101
|
+
) -> tuple[None | str, str | dict[str, list[str]] | None, int]:
|
|
102
|
+
|
|
103
|
+
_, errors, status_code = await self.get_model(
|
|
104
|
+
model_id=model_id,
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
if errors:
|
|
108
|
+
return None, errors, status_code
|
|
109
|
+
|
|
110
|
+
await self._repository.delete_model(
|
|
111
|
+
model_id=model_id,
|
|
112
|
+
)
|
|
113
|
+
|
|
114
|
+
return "object_deleted", None, HTTP_200_OK"""
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
from dataclasses import dataclass, fields
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
@dataclass
|
|
5
|
+
class BaseModelValidationErrors:
|
|
6
|
+
|
|
7
|
+
def has_errors(self) -> bool:
|
|
8
|
+
for f in fields(self):
|
|
9
|
+
value = getattr(self, f.name)
|
|
10
|
+
|
|
11
|
+
if isinstance(value, list) and len(value) > 0:
|
|
12
|
+
return True
|
|
13
|
+
|
|
14
|
+
return False
|
|
@@ -2,17 +2,17 @@ from abc import ABC
|
|
|
2
2
|
from typing import Callable
|
|
3
3
|
|
|
4
4
|
from asyncpg.exceptions import ForeignKeyViolationError
|
|
5
|
+
from architectonics.infrastructure.entities.base_entity import BaseEntity
|
|
6
|
+
from architectonics.infrastructure.repositories.base_exceptions import (
|
|
7
|
+
IntegrityErrorException,
|
|
8
|
+
ObjectAlreadyExistsException,
|
|
9
|
+
ObjectNotFoundException,
|
|
10
|
+
)
|
|
5
11
|
from sqlalchemy import delete, select, update
|
|
6
12
|
from sqlalchemy.exc import IntegrityError
|
|
7
13
|
from sqlalchemy.ext.asyncio import AsyncSession
|
|
8
14
|
|
|
9
15
|
from architectonics.core.models.base_model import BaseModel
|
|
10
|
-
from infrastructure.entities.base_entity import BaseEntity
|
|
11
|
-
from infrastructure.repositories.base_exceptions import (
|
|
12
|
-
IntegrityErrorException,
|
|
13
|
-
ObjectAlreadyExistsException,
|
|
14
|
-
ObjectNotFoundException,
|
|
15
|
-
)
|
|
16
16
|
|
|
17
17
|
|
|
18
18
|
class BaseRepository(ABC):
|
|
@@ -9,6 +9,9 @@ packages = \
|
|
|
9
9
|
'architectonics.core.config',
|
|
10
10
|
'architectonics.core.factory',
|
|
11
11
|
'architectonics.core.models',
|
|
12
|
+
'architectonics.core.result',
|
|
13
|
+
'architectonics.core.services',
|
|
14
|
+
'architectonics.core.validation',
|
|
12
15
|
'architectonics.infrastructure',
|
|
13
16
|
'architectonics.infrastructure.config',
|
|
14
17
|
'architectonics.infrastructure.entities',
|
|
@@ -29,7 +32,7 @@ install_requires = \
|
|
|
29
32
|
|
|
30
33
|
setup_kwargs = {
|
|
31
34
|
'name': 'architectonics',
|
|
32
|
-
'version': '0.0.
|
|
35
|
+
'version': '0.0.29',
|
|
33
36
|
'description': '',
|
|
34
37
|
'long_description': None,
|
|
35
38
|
'author': 'Your Name',
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{architectonics-0.0.27 → architectonics-0.0.29}/architectonics/core/config/application_settings.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/config/__init__.py
RENAMED
|
File without changes
|
{architectonics-0.0.27 → architectonics-0.0.29}/architectonics/infrastructure/config/database.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|