explicit-python-dlq 1.2.0__py3-none-any.whl → 2.0.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.
- explicit/dlq/config.py +0 -6
- explicit/dlq/contrib/django/adapters/db.py +4 -3
- explicit/dlq/contrib/django/models.py +16 -6
- explicit/dlq/domain/commands.py +7 -7
- explicit/dlq/domain/factories.py +7 -13
- explicit/dlq/domain/model.py +18 -11
- explicit/dlq/domain/services.py +1 -5
- explicit/dlq/infrastructure/handlers.py +1 -4
- {explicit_python_dlq-1.2.0.dist-info → explicit_python_dlq-2.0.0.dist-info}/METADATA +5 -5
- {explicit_python_dlq-1.2.0.dist-info → explicit_python_dlq-2.0.0.dist-info}/RECORD +12 -12
- {explicit_python_dlq-1.2.0.dist-info → explicit_python_dlq-2.0.0.dist-info}/WHEEL +0 -0
- {explicit_python_dlq-1.2.0.dist-info → explicit_python_dlq-2.0.0.dist-info}/top_level.txt +0 -0
explicit/dlq/config.py
CHANGED
|
@@ -7,10 +7,6 @@ from typing import (
|
|
|
7
7
|
TYPE_CHECKING,
|
|
8
8
|
)
|
|
9
9
|
|
|
10
|
-
from pydantic import (
|
|
11
|
-
ConfigDict,
|
|
12
|
-
)
|
|
13
|
-
|
|
14
10
|
from explicit.dlq.domain import (
|
|
15
11
|
commands,
|
|
16
12
|
)
|
|
@@ -35,8 +31,6 @@ if TYPE_CHECKING:
|
|
|
35
31
|
class DLQConfig:
|
|
36
32
|
"""Конфигурация DLQ."""
|
|
37
33
|
|
|
38
|
-
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
39
|
-
|
|
40
34
|
bus: 'MessageBus'
|
|
41
35
|
repository: 'AbstractDLQRepository'
|
|
42
36
|
message_dispatcher: 'DLQMessageDispatcher'
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Адаптеры для работы с Dead Letter Queue (DLQ) в базе данных."""
|
|
2
2
|
|
|
3
|
+
from collections.abc import (
|
|
4
|
+
Iterator,
|
|
5
|
+
)
|
|
3
6
|
from typing import (
|
|
4
7
|
TYPE_CHECKING,
|
|
5
|
-
Iterator,
|
|
6
|
-
Union,
|
|
7
8
|
)
|
|
8
9
|
|
|
9
10
|
from django.core.exceptions import (
|
|
@@ -95,7 +96,7 @@ class Repository(AbstractDLQRepository):
|
|
|
95
96
|
for db_instance in self._base_qs.iterator():
|
|
96
97
|
yield self._to_domain(db_instance)
|
|
97
98
|
|
|
98
|
-
def get_object_by_id(self, identifier: '
|
|
99
|
+
def get_object_by_id(self, identifier: 'UUID | str') -> DeadLetter:
|
|
99
100
|
"""Получить сообщение по идентификатору."""
|
|
100
101
|
try:
|
|
101
102
|
db_instance = self._base_qs.get(pk=identifier)
|
|
@@ -12,8 +12,12 @@ from django.db import (
|
|
|
12
12
|
)
|
|
13
13
|
|
|
14
14
|
from explicit.dlq.domain.model import (
|
|
15
|
+
TOPIC_MAX_LENGTH,
|
|
15
16
|
DeadLetter as DomainDeadLetter,
|
|
16
17
|
)
|
|
18
|
+
from explicit.domain import (
|
|
19
|
+
get_field_title,
|
|
20
|
+
)
|
|
17
21
|
|
|
18
22
|
|
|
19
23
|
if DJANGO_VERSION >= (4, 0):
|
|
@@ -27,12 +31,18 @@ else:
|
|
|
27
31
|
class DeadLetter(models.Model):
|
|
28
32
|
"""Недоставленное/необработанное сообщение."""
|
|
29
33
|
|
|
30
|
-
id = models.UUIDField(
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
id = models.UUIDField(
|
|
35
|
+
primary_key=True, default=uuid4, editable=False, verbose_name=get_field_title(DomainDeadLetter, 'id')
|
|
36
|
+
)
|
|
37
|
+
raw_message_value = models.BinaryField(verbose_name=get_field_title(DomainDeadLetter, 'raw_message_value'))
|
|
38
|
+
raw_message_key = models.BinaryField(
|
|
39
|
+
verbose_name=get_field_title(DomainDeadLetter, 'raw_message_key'), null=True, blank=True
|
|
40
|
+
)
|
|
41
|
+
topic = models.CharField(verbose_name=get_field_title(DomainDeadLetter, 'topic'), max_length=TOPIC_MAX_LENGTH)
|
|
42
|
+
attempts = JSONField(verbose_name=get_field_title(DomainDeadLetter, 'attempts'))
|
|
43
|
+
processed_at = models.DateTimeField(
|
|
44
|
+
verbose_name=get_field_title(DomainDeadLetter, 'processed_at'), null=True, blank=True
|
|
45
|
+
)
|
|
36
46
|
|
|
37
47
|
# служебные поля используемые для фильтрации и сортировки, чтобы не лезть в json
|
|
38
48
|
_first_attempted_at = models.DateTimeField(db_index=True)
|
explicit/dlq/domain/commands.py
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
1
|
"""Команды DLQ."""
|
|
2
2
|
|
|
3
|
-
from typing import (
|
|
4
|
-
Optional,
|
|
5
|
-
)
|
|
6
3
|
from uuid import (
|
|
7
4
|
UUID,
|
|
8
5
|
)
|
|
9
6
|
|
|
7
|
+
from pydantic import (
|
|
8
|
+
ConfigDict,
|
|
9
|
+
)
|
|
10
|
+
|
|
10
11
|
from explicit.messagebus.commands import (
|
|
11
12
|
Command,
|
|
12
13
|
)
|
|
@@ -15,14 +16,13 @@ from explicit.messagebus.commands import (
|
|
|
15
16
|
class RegisterDeadLetter(Command):
|
|
16
17
|
"""Зарегистрировать сообщение, которое не удалось обработать."""
|
|
17
18
|
|
|
19
|
+
model_config = ConfigDict(arbitrary_types_allowed=True)
|
|
20
|
+
|
|
18
21
|
topic: str
|
|
19
22
|
raw_message_value: bytes
|
|
20
|
-
raw_message_key:
|
|
23
|
+
raw_message_key: bytes | None = None
|
|
21
24
|
exception: Exception
|
|
22
25
|
|
|
23
|
-
class Config: # noqa: D106
|
|
24
|
-
arbitrary_types_allowed = True
|
|
25
|
-
|
|
26
26
|
|
|
27
27
|
class ProcessDeadLetter(Command):
|
|
28
28
|
"""Выполнить попытку обработки сообщения."""
|
explicit/dlq/domain/factories.py
CHANGED
|
@@ -5,9 +5,6 @@ from datetime import (
|
|
|
5
5
|
datetime,
|
|
6
6
|
timezone,
|
|
7
7
|
)
|
|
8
|
-
from typing import (
|
|
9
|
-
Union,
|
|
10
|
-
)
|
|
11
8
|
|
|
12
9
|
from explicit.domain import (
|
|
13
10
|
Str,
|
|
@@ -21,9 +18,6 @@ from explicit.domain.model import (
|
|
|
21
18
|
Unset,
|
|
22
19
|
unset,
|
|
23
20
|
)
|
|
24
|
-
from explicit.domain.types import (
|
|
25
|
-
NoneStr,
|
|
26
|
-
)
|
|
27
21
|
|
|
28
22
|
from .model import (
|
|
29
23
|
Attempt,
|
|
@@ -34,7 +28,7 @@ from .model import (
|
|
|
34
28
|
class AttemptDTO(DTOBase):
|
|
35
29
|
"""Объект передачи данных о попытке обработки сообщения."""
|
|
36
30
|
|
|
37
|
-
failed_at:
|
|
31
|
+
failed_at: datetime | Unset = unset
|
|
38
32
|
error_message: Str = unset
|
|
39
33
|
traceback: Str = unset
|
|
40
34
|
|
|
@@ -43,11 +37,11 @@ class DeadLetterDTO(DTOBase):
|
|
|
43
37
|
"""Объект передачи данных о необработанном сообщении."""
|
|
44
38
|
|
|
45
39
|
id: UnsetUUID = unset
|
|
46
|
-
raw_message_value:
|
|
47
|
-
raw_message_key:
|
|
40
|
+
raw_message_value: bytes | Unset = unset
|
|
41
|
+
raw_message_key: bytes | None | Unset = unset
|
|
48
42
|
topic: Str = unset
|
|
49
|
-
attempts:
|
|
50
|
-
processed_at:
|
|
43
|
+
attempts: list[AttemptDTO] | Unset = unset
|
|
44
|
+
processed_at: datetime | None | Unset = unset
|
|
51
45
|
|
|
52
46
|
|
|
53
47
|
class Factory(AbstractDomainFactory):
|
|
@@ -55,7 +49,7 @@ class Factory(AbstractDomainFactory):
|
|
|
55
49
|
|
|
56
50
|
def create(self, data: DeadLetterDTO) -> DeadLetter:
|
|
57
51
|
"""Создать DLQ из DTO."""
|
|
58
|
-
params = data.
|
|
52
|
+
params = data.model_dump()
|
|
59
53
|
|
|
60
54
|
return DeadLetter(**params)
|
|
61
55
|
|
|
@@ -68,7 +62,7 @@ class Factory(AbstractDomainFactory):
|
|
|
68
62
|
)
|
|
69
63
|
|
|
70
64
|
def create_from_exception(
|
|
71
|
-
self, raw_message_value: bytes, raw_message_key:
|
|
65
|
+
self, raw_message_value: bytes, raw_message_key: bytes | None, topic: str, exception: Exception
|
|
72
66
|
) -> DeadLetter:
|
|
73
67
|
"""Создать DLQ из исключения."""
|
|
74
68
|
attempt = self.create_attempt_from_exception(exception)
|
explicit/dlq/domain/model.py
CHANGED
|
@@ -7,13 +7,11 @@ from datetime import (
|
|
|
7
7
|
)
|
|
8
8
|
from typing import (
|
|
9
9
|
TYPE_CHECKING,
|
|
10
|
-
Optional,
|
|
11
|
-
Union,
|
|
12
10
|
)
|
|
13
11
|
|
|
14
12
|
from pydantic import (
|
|
15
13
|
Field,
|
|
16
|
-
|
|
14
|
+
field_validator,
|
|
17
15
|
)
|
|
18
16
|
|
|
19
17
|
from explicit.contrib.domain.model import (
|
|
@@ -22,9 +20,16 @@ from explicit.contrib.domain.model import (
|
|
|
22
20
|
|
|
23
21
|
|
|
24
22
|
if TYPE_CHECKING:
|
|
25
|
-
from dataclasses import
|
|
23
|
+
from dataclasses import (
|
|
24
|
+
dataclass,
|
|
25
|
+
)
|
|
26
26
|
else:
|
|
27
|
-
from pydantic.dataclasses import
|
|
27
|
+
from pydantic.dataclasses import (
|
|
28
|
+
dataclass,
|
|
29
|
+
)
|
|
30
|
+
|
|
31
|
+
|
|
32
|
+
TOPIC_MAX_LENGTH = 256
|
|
28
33
|
|
|
29
34
|
|
|
30
35
|
class DeadLetterNotFound(Exception): # noqa: N818
|
|
@@ -49,12 +54,14 @@ class DeadLetter:
|
|
|
49
54
|
|
|
50
55
|
id: uuid.UUID = uuid_identifier()
|
|
51
56
|
raw_message_value: bytes = Field(title='Содержимое сообщения')
|
|
52
|
-
raw_message_key:
|
|
53
|
-
topic: str = Field(title='Топик сообщения', max_length=
|
|
54
|
-
attempts: tuple[Attempt, ...] = Field(
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
57
|
+
raw_message_key: bytes | None = Field(title='Ключ сообщения', default=None)
|
|
58
|
+
topic: str = Field(title='Топик сообщения', max_length=TOPIC_MAX_LENGTH)
|
|
59
|
+
attempts: tuple[Attempt, ...] = Field(
|
|
60
|
+
default_factory=tuple, validate_default=True, title='Попытки обработки сообщения'
|
|
61
|
+
)
|
|
62
|
+
processed_at: datetime | None = Field(default=None, title='Время успешной обработки сообщения')
|
|
63
|
+
|
|
64
|
+
@field_validator('attempts')
|
|
58
65
|
@classmethod
|
|
59
66
|
def _validate_attempts(cls, value):
|
|
60
67
|
if not value:
|
explicit/dlq/domain/services.py
CHANGED
|
@@ -1,9 +1,5 @@
|
|
|
1
1
|
"""Сервисы предметной области для работы с DLQ."""
|
|
2
2
|
|
|
3
|
-
from typing import (
|
|
4
|
-
Optional,
|
|
5
|
-
)
|
|
6
|
-
|
|
7
3
|
from explicit.unit_of_work import (
|
|
8
4
|
AbstractUnitOfWork,
|
|
9
5
|
)
|
|
@@ -19,7 +15,7 @@ from .model import (
|
|
|
19
15
|
|
|
20
16
|
def register_dead_letter(
|
|
21
17
|
raw_message_value: bytes,
|
|
22
|
-
raw_message_key:
|
|
18
|
+
raw_message_key: bytes | None,
|
|
23
19
|
topic: str,
|
|
24
20
|
exception: Exception,
|
|
25
21
|
uow: 'AbstractUnitOfWork',
|
|
@@ -3,9 +3,6 @@
|
|
|
3
3
|
from contextlib import (
|
|
4
4
|
AbstractContextManager,
|
|
5
5
|
)
|
|
6
|
-
from typing import (
|
|
7
|
-
Optional,
|
|
8
|
-
)
|
|
9
6
|
|
|
10
7
|
from explicit.dlq.domain.commands import (
|
|
11
8
|
RegisterDeadLetter,
|
|
@@ -40,7 +37,7 @@ class RegisterInDLQOnFailure(AbstractContextManager):
|
|
|
40
37
|
bus: 'MessageBus',
|
|
41
38
|
topic: str,
|
|
42
39
|
raw_message_value: bytes,
|
|
43
|
-
raw_message_key:
|
|
40
|
+
raw_message_key: bytes | None = None,
|
|
44
41
|
):
|
|
45
42
|
self._bus = bus
|
|
46
43
|
self._topic = topic
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: explicit-python-dlq
|
|
3
|
-
Version:
|
|
3
|
+
Version: 2.0.0
|
|
4
4
|
Summary: Набор компонентов реализующих DQL для систем на базе explicit
|
|
5
5
|
Author-email: "АО \"БАРС Груп\"" <education_dev@bars-open.ru>
|
|
6
6
|
Classifier: Intended Audience :: Developers
|
|
@@ -8,18 +8,18 @@ Classifier: Environment :: Web Environment
|
|
|
8
8
|
Classifier: Natural Language :: Russian
|
|
9
9
|
Classifier: Operating System :: OS Independent
|
|
10
10
|
Classifier: Development Status :: 5 - Production/Stable
|
|
11
|
-
Classifier: Programming Language :: Python :: 3.9
|
|
12
11
|
Classifier: Programming Language :: Python :: 3.10
|
|
13
12
|
Classifier: Programming Language :: Python :: 3.11
|
|
14
13
|
Classifier: Programming Language :: Python :: 3.12
|
|
15
14
|
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
-
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
16
|
+
Requires-Python: >=3.10
|
|
17
17
|
Description-Content-Type: text/markdown
|
|
18
|
-
Requires-Dist: explicit-python<3
|
|
18
|
+
Requires-Dist: explicit-python<4,>=3
|
|
19
19
|
Provides-Extra: dev
|
|
20
20
|
Requires-Dist: freezegun; extra == "dev"
|
|
21
21
|
Provides-Extra: django
|
|
22
|
-
Requires-Dist: explicit-python-django<
|
|
22
|
+
Requires-Dist: explicit-python-django<3,>=2; extra == "django"
|
|
23
23
|
Provides-Extra: rest
|
|
24
24
|
Requires-Dist: djangorestframework>=3.13.0; extra == "rest"
|
|
25
25
|
Requires-Dist: django_filter>=23.3; extra == "rest"
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
explicit/dlq/__init__.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
2
|
-
explicit/dlq/config.py,sha256=
|
|
2
|
+
explicit/dlq/config.py,sha256=GcB6tJop2CYC49IlgEPBmE7iISLaNpuhJ5Qmk9oV7hY,1385
|
|
3
3
|
explicit/dlq/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
explicit/dlq/types.py,sha256=aVqdsdKZ-U_5TD3dpLtzksyL3jVedIhJnu7IihW3vh4,544
|
|
5
5
|
explicit/dlq/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -8,9 +8,9 @@ explicit/dlq/contrib/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hS
|
|
|
8
8
|
explicit/dlq/contrib/django/README.md,sha256=TE9tt1M1qDQIgMegeQgesVeyLC1ajBbVjINGYDxpJcI,1266
|
|
9
9
|
explicit/dlq/contrib/django/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
explicit/dlq/contrib/django/apps.py,sha256=WDloauYls-krHbJOp_brpS8WifMyK8O59eUTSQDZROU,263
|
|
11
|
-
explicit/dlq/contrib/django/models.py,sha256=
|
|
11
|
+
explicit/dlq/contrib/django/models.py,sha256=eu_sobtXL1Q1xrJ9St7HiT-N6_JGgd0aEwMBRGXg_vE,1956
|
|
12
12
|
explicit/dlq/contrib/django/adapters/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
|
-
explicit/dlq/contrib/django/adapters/db.py,sha256=
|
|
13
|
+
explicit/dlq/contrib/django/adapters/db.py,sha256=IdJGnbtzRNyTcClLR5Gf3S5-HkCFRDJs9SPddh06iHs,3324
|
|
14
14
|
explicit/dlq/contrib/django/migrations/0001_initial.py,sha256=sDjCDZl1RvR_oOhBvftbW3LvRf6FbuLmRF_eQ-dauhM,2133
|
|
15
15
|
explicit/dlq/contrib/django/migrations/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
16
16
|
explicit/dlq/contrib/drf/README.md,sha256=0VlBusFcswT8i9FnCV_llkhDJlFCHQ7ih0qFb_hc7FU,747
|
|
@@ -18,15 +18,15 @@ explicit/dlq/contrib/drf/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZ
|
|
|
18
18
|
explicit/dlq/contrib/drf/serializers.py,sha256=zNASKGtXbw5unWqMJgDaqITwtVAsW54liJsXoK4FRBU,1774
|
|
19
19
|
explicit/dlq/contrib/drf/views.py,sha256=xBRUSzBbZ10PL8bHktZLosR-Tw-Q9jgN1n76oQmqLa4,2099
|
|
20
20
|
explicit/dlq/domain/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
21
|
-
explicit/dlq/domain/commands.py,sha256=
|
|
22
|
-
explicit/dlq/domain/factories.py,sha256=
|
|
23
|
-
explicit/dlq/domain/model.py,sha256=
|
|
24
|
-
explicit/dlq/domain/services.py,sha256=
|
|
21
|
+
explicit/dlq/domain/commands.py,sha256=X94H6DrUtaArdGArUR-VxQQNpfHvg9u5hRnwgMboG58,794
|
|
22
|
+
explicit/dlq/domain/factories.py,sha256=7p3niFzPeghhAY6Vk1F6DlUahGk66o-PNVJzg4sfKw0,2175
|
|
23
|
+
explicit/dlq/domain/model.py,sha256=YUtLtLvtsXR_mxxopVv4XtEOO7n_EQwMZ6AxxwMj2l0,2349
|
|
24
|
+
explicit/dlq/domain/services.py,sha256=DCuTZXPGQLBhWQMYRyO_RiUlYLd71QCVRtOeCDUET5A,2010
|
|
25
25
|
explicit/dlq/infrastructure/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
explicit/dlq/infrastructure/handlers.py,sha256=
|
|
26
|
+
explicit/dlq/infrastructure/handlers.py,sha256=uQ05w0_9RyOfh8HQB4GtNcgENfWXSa-3h4IsLa6BwzE,2284
|
|
27
27
|
explicit/dlq/services/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
28
|
explicit/dlq/services/handlers.py,sha256=U6y4ElqXi1VocS3SE3SvwK69MMApAXILJes-xDbk_Ps,3232
|
|
29
|
-
explicit_python_dlq-
|
|
30
|
-
explicit_python_dlq-
|
|
31
|
-
explicit_python_dlq-
|
|
32
|
-
explicit_python_dlq-
|
|
29
|
+
explicit_python_dlq-2.0.0.dist-info/METADATA,sha256=Z9SM9ki1Yg5c_J4oNi3L7gFx6_NIEQlCdXb7FSuaTVo,5482
|
|
30
|
+
explicit_python_dlq-2.0.0.dist-info/WHEEL,sha256=1tXe9gY0PYatrMPMDd6jXqjfpz_B-Wqm32CPfRC58XU,91
|
|
31
|
+
explicit_python_dlq-2.0.0.dist-info/top_level.txt,sha256=tt6T8l4Yji4ww87qZQcD4CbcwTIHy7NAPmU7QAfMcpY,9
|
|
32
|
+
explicit_python_dlq-2.0.0.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|