explicit-python-dlq 1.0.0__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.
Files changed (38) hide show
  1. explicit_python_dlq-1.0.0/MANIFEST.in +18 -0
  2. explicit_python_dlq-1.0.0/PKG-INFO +105 -0
  3. explicit_python_dlq-1.0.0/README.md +80 -0
  4. explicit_python_dlq-1.0.0/pyproject.toml +132 -0
  5. explicit_python_dlq-1.0.0/setup.cfg +4 -0
  6. explicit_python_dlq-1.0.0/src/explicit/dlq/__init__.py +1 -0
  7. explicit_python_dlq-1.0.0/src/explicit/dlq/adapters/__init__.py +0 -0
  8. explicit_python_dlq-1.0.0/src/explicit/dlq/adapters/db.py +13 -0
  9. explicit_python_dlq-1.0.0/src/explicit/dlq/config.py +63 -0
  10. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/__init__.py +0 -0
  11. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/django/README.md +40 -0
  12. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/django/__init__.py +0 -0
  13. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/django/adapters/__init__.py +0 -0
  14. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/django/adapters/db.py +104 -0
  15. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/django/apps.py +12 -0
  16. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/django/migrations/0001_initial.py +60 -0
  17. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/django/migrations/__init__.py +0 -0
  18. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/django/models.py +45 -0
  19. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/drf/README.md +32 -0
  20. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/drf/__init__.py +0 -0
  21. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/drf/serializers.py +45 -0
  22. explicit_python_dlq-1.0.0/src/explicit/dlq/contrib/drf/views.py +72 -0
  23. explicit_python_dlq-1.0.0/src/explicit/dlq/domain/__init__.py +0 -0
  24. explicit_python_dlq-1.0.0/src/explicit/dlq/domain/commands.py +37 -0
  25. explicit_python_dlq-1.0.0/src/explicit/dlq/domain/factories.py +81 -0
  26. explicit_python_dlq-1.0.0/src/explicit/dlq/domain/model.py +71 -0
  27. explicit_python_dlq-1.0.0/src/explicit/dlq/domain/services.py +64 -0
  28. explicit_python_dlq-1.0.0/src/explicit/dlq/infrastructure/__init__.py +0 -0
  29. explicit_python_dlq-1.0.0/src/explicit/dlq/infrastructure/handlers.py +72 -0
  30. explicit_python_dlq-1.0.0/src/explicit/dlq/py.typed +0 -0
  31. explicit_python_dlq-1.0.0/src/explicit/dlq/services/__init__.py +0 -0
  32. explicit_python_dlq-1.0.0/src/explicit/dlq/services/handlers.py +95 -0
  33. explicit_python_dlq-1.0.0/src/explicit/dlq/types.py +15 -0
  34. explicit_python_dlq-1.0.0/src/explicit_python_dlq.egg-info/PKG-INFO +105 -0
  35. explicit_python_dlq-1.0.0/src/explicit_python_dlq.egg-info/SOURCES.txt +36 -0
  36. explicit_python_dlq-1.0.0/src/explicit_python_dlq.egg-info/dependency_links.txt +1 -0
  37. explicit_python_dlq-1.0.0/src/explicit_python_dlq.egg-info/requires.txt +11 -0
  38. explicit_python_dlq-1.0.0/src/explicit_python_dlq.egg-info/top_level.txt +2 -0
@@ -0,0 +1,18 @@
1
+ graft src
2
+
3
+ global-exclude *.pyc
4
+ global-exclude __pycache__
5
+
6
+ prune src/testapp
7
+
8
+ exclude pylintrc
9
+ exclude mypy.ini
10
+ exclude db.sqlite3
11
+ exclude isort.cfg
12
+ exclude .coveragerc
13
+ exclude .coverage
14
+
15
+ include README.md
16
+ include version.conf
17
+ include requirements/base.txt
18
+ include requirements/prod.txt
@@ -0,0 +1,105 @@
1
+ Metadata-Version: 2.4
2
+ Name: explicit-python-dlq
3
+ Version: 1.0.0
4
+ Summary: Набор компонентов реализующих DQL для систем на базе explicit
5
+ Author-email: "АО \"БАРС Груп\"" <education_dev@bars-open.ru>
6
+ Classifier: Intended Audience :: Developers
7
+ Classifier: Environment :: Web Environment
8
+ Classifier: Natural Language :: Russian
9
+ Classifier: Operating System :: OS Independent
10
+ Classifier: Development Status :: 5 - Production/Stable
11
+ Classifier: Programming Language :: Python :: 3.9
12
+ Classifier: Programming Language :: Python :: 3.10
13
+ Classifier: Programming Language :: Python :: 3.11
14
+ Classifier: Programming Language :: Python :: 3.12
15
+ Requires-Python: >=3.9
16
+ Description-Content-Type: text/markdown
17
+ Requires-Dist: explicit-python<3,>=2.2.1
18
+ Provides-Extra: dev
19
+ Requires-Dist: freezegun; extra == "dev"
20
+ Provides-Extra: django
21
+ Requires-Dist: explicit-python-django<2.0,>=1.0.2; extra == "django"
22
+ Provides-Extra: rest
23
+ Requires-Dist: djangorestframework>=3.13.0; extra == "rest"
24
+ Requires-Dist: django_filter>=23.3; extra == "rest"
25
+
26
+ # Набор компонентов реализующих очередь необработанных сообщений (Dead Letter Queue).
27
+
28
+ ## Пример подключения
29
+ testapp/dlq/apps.py:
30
+ ```python
31
+ from django.apps import AppConfig as AppConfigBase
32
+
33
+
34
+ class AppConfig(AppConfigBase):
35
+ name = __package__
36
+
37
+ def ready(self):
38
+ self._configure_dlq()
39
+
40
+ def _configure_dlq(self):
41
+ from testapp.core import bus
42
+ from explicit.dlq.config import DLQConfig, configure_dlq
43
+ # реализация репозитория специфичная для используемого слоя хранения данных
44
+ from testapp.dlq.adapters.db import Repository
45
+
46
+ # реализация обработчика сообщений, специфичного для приложения
47
+ from testapp.dlq.services.dispatch import dispatch_message
48
+
49
+ config = DLQConfig(
50
+ bus=bus,
51
+ repository=Repository(),
52
+ message_dispatcher=dispatch_message
53
+ )
54
+ # регистрация репозитория и регистрация обработчиков команд
55
+ configure_dlq(config)
56
+ ```
57
+
58
+ testapp/dlq/services/dispatch.py:
59
+ ```python
60
+ import json
61
+ from explicit.contrib.messagebus.event_registry import Message
62
+
63
+ from testapp.core import bus
64
+ from testapp.core import event_registry
65
+
66
+ def dispatch_message(raw_message_value: bytes, topic: str):
67
+ """Преобразует сообщение в событие и отправляет его в шину."""
68
+ message = json.loads(raw_message_value)
69
+
70
+ if event := event_registry.resolve(
71
+ Message(
72
+ topic=topic,
73
+ type=message.get('type'),
74
+ body=message
75
+ )
76
+ ):
77
+ bus.handle(event)
78
+
79
+ ````
80
+
81
+ testapp/entrypoints/kafka.py:
82
+
83
+ ```python
84
+ def bootstrap() -> None:
85
+ from explicit.dlq.infrastructure.handlers import RegisterInDLQOnFailure
86
+
87
+ from testapp.core import bus
88
+
89
+ from testapp.dlq.services.dispatch import dispatch_message
90
+ topics = ('test.foo.topic',)
91
+
92
+ for raw_message in adapter.subscribe(*topics):
93
+ with RegisterInDLQOnFailure(
94
+ bus=bus,
95
+ topic=raw_message.topic(),
96
+ raw_message_value=raw_message.value(),
97
+ raw_message_key=raw_message.key
98
+ ):
99
+ dispatch_message(raw_message.value(), raw_message.topic())
100
+ ```
101
+ ## Готовые компоненты (contrib)
102
+
103
+ В пакете реализованы готовые к использованию компоненты:
104
+ * Реализация абстрактного [хранилища сообщений](./src/explicit/dlq/contrib/django/README.md) на базе Django ORM
105
+ * [REST API](./src/explicit/dlq/contrib/drf/README.md) для работы с хранилищем сообщений. Предназначен для совместной работы с реализацией хранилища сообщений django ORM.
@@ -0,0 +1,80 @@
1
+ # Набор компонентов реализующих очередь необработанных сообщений (Dead Letter Queue).
2
+
3
+ ## Пример подключения
4
+ testapp/dlq/apps.py:
5
+ ```python
6
+ from django.apps import AppConfig as AppConfigBase
7
+
8
+
9
+ class AppConfig(AppConfigBase):
10
+ name = __package__
11
+
12
+ def ready(self):
13
+ self._configure_dlq()
14
+
15
+ def _configure_dlq(self):
16
+ from testapp.core import bus
17
+ from explicit.dlq.config import DLQConfig, configure_dlq
18
+ # реализация репозитория специфичная для используемого слоя хранения данных
19
+ from testapp.dlq.adapters.db import Repository
20
+
21
+ # реализация обработчика сообщений, специфичного для приложения
22
+ from testapp.dlq.services.dispatch import dispatch_message
23
+
24
+ config = DLQConfig(
25
+ bus=bus,
26
+ repository=Repository(),
27
+ message_dispatcher=dispatch_message
28
+ )
29
+ # регистрация репозитория и регистрация обработчиков команд
30
+ configure_dlq(config)
31
+ ```
32
+
33
+ testapp/dlq/services/dispatch.py:
34
+ ```python
35
+ import json
36
+ from explicit.contrib.messagebus.event_registry import Message
37
+
38
+ from testapp.core import bus
39
+ from testapp.core import event_registry
40
+
41
+ def dispatch_message(raw_message_value: bytes, topic: str):
42
+ """Преобразует сообщение в событие и отправляет его в шину."""
43
+ message = json.loads(raw_message_value)
44
+
45
+ if event := event_registry.resolve(
46
+ Message(
47
+ topic=topic,
48
+ type=message.get('type'),
49
+ body=message
50
+ )
51
+ ):
52
+ bus.handle(event)
53
+
54
+ ````
55
+
56
+ testapp/entrypoints/kafka.py:
57
+
58
+ ```python
59
+ def bootstrap() -> None:
60
+ from explicit.dlq.infrastructure.handlers import RegisterInDLQOnFailure
61
+
62
+ from testapp.core import bus
63
+
64
+ from testapp.dlq.services.dispatch import dispatch_message
65
+ topics = ('test.foo.topic',)
66
+
67
+ for raw_message in adapter.subscribe(*topics):
68
+ with RegisterInDLQOnFailure(
69
+ bus=bus,
70
+ topic=raw_message.topic(),
71
+ raw_message_value=raw_message.value(),
72
+ raw_message_key=raw_message.key
73
+ ):
74
+ dispatch_message(raw_message.value(), raw_message.topic())
75
+ ```
76
+ ## Готовые компоненты (contrib)
77
+
78
+ В пакете реализованы готовые к использованию компоненты:
79
+ * Реализация абстрактного [хранилища сообщений](./src/explicit/dlq/contrib/django/README.md) на базе Django ORM
80
+ * [REST API](./src/explicit/dlq/contrib/drf/README.md) для работы с хранилищем сообщений. Предназначен для совместной работы с реализацией хранилища сообщений django ORM.
@@ -0,0 +1,132 @@
1
+ [build-system]
2
+ requires = [
3
+ "packaging>=24,<25",
4
+ "wheel>=0.45,<0.46",
5
+ "setuptools>=77,<78",
6
+ "setuptools-git-versioning>=2,<3",
7
+ ]
8
+
9
+ build-backend = "setuptools.build_meta"
10
+
11
+ [project]
12
+ dynamic = ["version"]
13
+
14
+ name = "explicit-python-dlq"
15
+
16
+ authors = [
17
+ {name = "АО \"БАРС Груп\"", email = "education_dev@bars-open.ru"}
18
+ ]
19
+
20
+ readme = "README.md"
21
+
22
+ description = "Набор компонентов реализующих DQL для систем на базе explicit"
23
+
24
+ classifiers=[
25
+ "Intended Audience :: Developers",
26
+ "Environment :: Web Environment",
27
+ "Natural Language :: Russian",
28
+ "Operating System :: OS Independent",
29
+ "Development Status :: 5 - Production/Stable",
30
+ "Programming Language :: Python :: 3.9",
31
+ "Programming Language :: Python :: 3.10",
32
+ "Programming Language :: Python :: 3.11",
33
+ "Programming Language :: Python :: 3.12",
34
+ ]
35
+
36
+ requires-python = ">=3.9"
37
+
38
+ dependencies = [
39
+ "explicit-python>=2.2.1,<3",
40
+ ]
41
+
42
+ [project.optional-dependencies]
43
+ dev = [
44
+ "freezegun"
45
+ ]
46
+ django = [
47
+ "explicit-python-django>=1.0.2,<2.0"
48
+ ]
49
+ rest = [
50
+ "djangorestframework>=3.13.0",
51
+ "django_filter>=23.3",
52
+ ]
53
+
54
+
55
+ [tool.setuptools-git-versioning]
56
+ enabled = true
57
+
58
+ [tool.isort]
59
+ multi_line_output=3
60
+ force_grid_wrap=1
61
+ include_trailing_comma = true
62
+ line_length = 120
63
+ combine_as_imports = true
64
+ lines_after_imports = 2
65
+ extra_standard_library = ["dataclasses"]
66
+ known_project = ["explicit", "testapp"]
67
+ sections = ["FUTURE","STDLIB","THIRDPARTY","FIRSTPARTY","PROJECT","LOCALFOLDER"]
68
+
69
+
70
+ [tool.ruff]
71
+ target-version = "py312"
72
+ line-length = 120
73
+ indent-width = 4
74
+
75
+
76
+ [tool.ruff.lint]
77
+ select = ["F", "E", "D", "W", "S", "N",]
78
+ ignore = [
79
+ # Missing docstring in public module
80
+ "D100",
81
+ # Missing docstring in public package
82
+ "D104",
83
+ # Missing docstring in __init__
84
+ "D107",
85
+ # use of assert detected (useless with pytest)
86
+ "S101",
87
+ ]
88
+ exclude = [
89
+ ".bzr",
90
+ ".direnv",
91
+ ".eggs",
92
+ ".git",
93
+ ".git-rewrite",
94
+ ".hg",
95
+ ".idea",
96
+ ".ipynb_checkpoints",
97
+ ".mypy_cache",
98
+ ".nox",
99
+ ".pants.d",
100
+ ".pyenv",
101
+ ".pytest_cache",
102
+ ".pytype",
103
+ ".ruff_cache",
104
+ ".svn",
105
+ ".tox",
106
+ ".venv",
107
+ ".vscode",
108
+ "__pypackages__",
109
+ "_build",
110
+ "buck-out",
111
+ "build",
112
+ "dist",
113
+ "node_modules",
114
+ "site-packages",
115
+ "venv",
116
+ "py-libs/**",
117
+ ]
118
+
119
+ [tool.ruff.format]
120
+ quote-style = "single"
121
+ indent-style = "space"
122
+ docstring-code-format = true
123
+
124
+ [tool.ruff.lint.per-file-ignores]
125
+ "*/tests/*" = ["D"]
126
+ "*/migrations/*" = ["D"]
127
+
128
+ [tool.mypy]
129
+ plugins = ["pydantic.mypy"]
130
+ no_site_packages = true
131
+ ignore_missing_imports = true
132
+ exclude = "migrations/"
@@ -0,0 +1,4 @@
1
+ [egg_info]
2
+ tag_build =
3
+ tag_date = 0
4
+
@@ -0,0 +1,13 @@
1
+ """Адаптеры для работы с Dead Letter Queue (DLQ) в базе данных."""
2
+
3
+ from abc import (
4
+ ABCMeta,
5
+ )
6
+
7
+ from explicit.adapters.db import (
8
+ AbstractRepository,
9
+ )
10
+
11
+
12
+ class AbstractDLQRepository(AbstractRepository, metaclass=ABCMeta):
13
+ """Абстрактный репозиторий для работы с DLQ."""
@@ -0,0 +1,63 @@
1
+ """Конфигурация DLQ."""
2
+
3
+ from dataclasses import (
4
+ dataclass,
5
+ )
6
+ from typing import (
7
+ TYPE_CHECKING,
8
+ )
9
+
10
+ from pydantic import (
11
+ ConfigDict,
12
+ )
13
+
14
+ from explicit.dlq.domain import (
15
+ commands,
16
+ )
17
+ from explicit.dlq.services.handlers import (
18
+ DLQHandlers,
19
+ )
20
+
21
+
22
+ if TYPE_CHECKING:
23
+ from explicit.dlq.adapters.db import (
24
+ AbstractDLQRepository,
25
+ )
26
+ from explicit.dlq.types import (
27
+ DLQMessageDispatcher,
28
+ )
29
+ from explicit.messagebus import (
30
+ MessageBus,
31
+ )
32
+
33
+
34
+ @dataclass
35
+ class DLQConfig:
36
+ """Конфигурация DLQ."""
37
+
38
+ model_config = ConfigDict(arbitrary_types_allowed=True)
39
+
40
+ bus: 'MessageBus'
41
+ repository: 'AbstractDLQRepository'
42
+ message_dispatcher: 'DLQMessageDispatcher'
43
+
44
+
45
+ def configure_dlq(config: DLQConfig):
46
+ """Настроить DLQ."""
47
+ config.bus.get_uow().register_repositories(('dead_letters', config.repository))
48
+
49
+ handlers = DLQHandlers(bus=config.bus, message_dispatcher=config.message_dispatcher)
50
+
51
+ config.bus.add_command_handler(
52
+ commands.RegisterDeadLetter,
53
+ handlers.register_dead_letter, # type: ignore[attr-defined,arg-type]
54
+ )
55
+ config.bus.add_command_handler(
56
+ commands.ProcessDeadLetter,
57
+ handlers.process_dead_letter, # type: ignore[attr-defined,arg-type]
58
+ )
59
+
60
+ config.bus.add_command_handler(
61
+ commands.UpdateDeadLetterRawMessage,
62
+ handlers.update_raw_message_value, # type: ignore[attr-defined,arg-type]
63
+ )
@@ -0,0 +1,40 @@
1
+ ## Набор компонентов для интеграции explicit.dlq с Django.
2
+ Содержит реализацию репозитория необработанных сообщений.
3
+
4
+ ## Пример подключения
5
+ testapp/settings.py:
6
+ ```python
7
+ INSTALLED_APPS = [
8
+ # другие приложения
9
+ 'explicit.dlq.contrib.django', # подключение приложения с моделью DeadLetter
10
+ 'testapp.core', # настройка компонентов из explicit.dlq
11
+ ]
12
+ ```
13
+
14
+ testapp/dlq/apps.py:
15
+ ```python
16
+
17
+ from datetime import timedelta
18
+
19
+ from django.apps import AppConfig as AppConfigBase
20
+
21
+
22
+ class AppConfig(AppConfigBase):
23
+ name = __package__
24
+
25
+ def ready(self):
26
+ self._configure_dlq()
27
+
28
+ def _configure_dlq(self):
29
+ from explicit.dlq.config import DLQConfig, configure_dlq
30
+
31
+ # реализация репозитория на базе Django ORM
32
+ from explicit.dlq.contrib.django.adapters.db import Repository
33
+ config = DLQConfig(
34
+ # ...
35
+ repository=Repository(),
36
+ # ...
37
+ )
38
+ # регистрация репозитория и регистрация обработчиков команд
39
+ configure_dlq(config)
40
+ ```
@@ -0,0 +1,104 @@
1
+ """Адаптеры для работы с Dead Letter Queue (DLQ) в базе данных."""
2
+
3
+ from typing import (
4
+ TYPE_CHECKING,
5
+ Iterator,
6
+ Union,
7
+ )
8
+
9
+ from django.core.exceptions import (
10
+ ObjectDoesNotExist,
11
+ )
12
+
13
+ from explicit.dlq.adapters.db import (
14
+ AbstractDLQRepository,
15
+ )
16
+ from explicit.dlq.domain.model import (
17
+ DeadLetter,
18
+ DeadLetterNotFound,
19
+ )
20
+ from explicit.domain import (
21
+ asdict,
22
+ )
23
+
24
+
25
+ if TYPE_CHECKING:
26
+ from uuid import (
27
+ UUID,
28
+ )
29
+
30
+ from explicit.dlq.contrib.django import (
31
+ models as db,
32
+ )
33
+
34
+
35
+ class Repository(AbstractDLQRepository):
36
+ """Репозиторий DeadLetterQueue."""
37
+
38
+ @property
39
+ def _base_qs(self):
40
+ # pylint: disable-next=import-outside-toplevel
41
+ from explicit.dlq.contrib.django.models import (
42
+ DeadLetter as DBDeadLetter,
43
+ )
44
+
45
+ return DBDeadLetter.objects.order_by('_first_attempted_at')
46
+
47
+ def _to_domain(self, dbinstance: 'db.DeadLetter') -> DeadLetter:
48
+ return DeadLetter(
49
+ id=dbinstance.id,
50
+ raw_message_value=bytes(dbinstance.raw_message_value),
51
+ raw_message_key=bytes(dbinstance.raw_message_key) if dbinstance.raw_message_key else None,
52
+ topic=dbinstance.topic,
53
+ attempts=tuple(dbinstance.attempts),
54
+ processed_at=dbinstance.processed_at,
55
+ )
56
+
57
+ def _to_db(self, modelinstance: DeadLetter) -> DeadLetter:
58
+ # pylint: disable-next=import-outside-toplevel
59
+ from explicit.dlq.contrib.django.models import (
60
+ DeadLetter as DBDeadLetter,
61
+ )
62
+
63
+ assert isinstance(modelinstance, DeadLetter)
64
+ db_instance, _ = DBDeadLetter.objects.update_or_create(
65
+ pk=modelinstance.id,
66
+ defaults=asdict(modelinstance, exclude={'id', 'attempts'})
67
+ | {
68
+ 'attempts': [
69
+ asdict(attempt) | {'failed_at': attempt.failed_at.isoformat()} for attempt in modelinstance.attempts
70
+ ],
71
+ '_first_attempted_at': modelinstance.attempts[0].failed_at,
72
+ '_attempts_count': len(modelinstance.attempts),
73
+ },
74
+ )
75
+ return self.get_object_by_id(db_instance.pk)
76
+
77
+ def add(self, obj: DeadLetter) -> DeadLetter:
78
+ """Добавить сообщение."""
79
+ return self._to_db(obj)
80
+
81
+ def update(self, obj: DeadLetter) -> DeadLetter:
82
+ """Обновить сообщение."""
83
+ assert isinstance(obj, DeadLetter)
84
+
85
+ return self._to_db(obj)
86
+
87
+ def delete(self, obj: DeadLetter) -> None:
88
+ """Удалить сообщение."""
89
+ assert isinstance(obj, DeadLetter)
90
+
91
+ self._base_qs.filter(pk=obj.id).delete()
92
+
93
+ def get_all_objects(self) -> Iterator[DeadLetter]:
94
+ """Получить все сообщения."""
95
+ for db_instance in self._base_qs.iterator():
96
+ yield self._to_domain(db_instance)
97
+
98
+ def get_object_by_id(self, identifier: 'Union[UUID, str]') -> DeadLetter:
99
+ """Получить сообщение по идентификатору."""
100
+ try:
101
+ db_instance = self._base_qs.get(pk=identifier)
102
+ return self._to_domain(db_instance)
103
+ except ObjectDoesNotExist as exc:
104
+ raise DeadLetterNotFound from exc
@@ -0,0 +1,12 @@
1
+ """Конфигурация приложения."""
2
+
3
+ from django.apps import (
4
+ AppConfig as AppConfigBase,
5
+ )
6
+
7
+
8
+ class AppConfig(AppConfigBase):
9
+ """Конфигурация приложения."""
10
+
11
+ name = __package__
12
+ label = 'explicit_dlq_django'
@@ -0,0 +1,60 @@
1
+ # pylint: disable=missing-module-docstring,invalid-name
2
+ import uuid
3
+
4
+ from django import (
5
+ VERSION as DJANGO_VERSION,
6
+ )
7
+ from django.db import (
8
+ migrations,
9
+ models,
10
+ )
11
+
12
+
13
+ if DJANGO_VERSION >= (4, 0):
14
+ from django.db.models import (
15
+ JSONField,
16
+ )
17
+ else:
18
+ from django.contrib.postgres.fields import (
19
+ JSONField,
20
+ )
21
+
22
+
23
+ class Migration(migrations.Migration):
24
+ initial = True
25
+
26
+ dependencies = []
27
+
28
+ operations = [
29
+ migrations.CreateModel(
30
+ name='DeadLetter',
31
+ fields=[
32
+ (
33
+ 'id',
34
+ models.UUIDField(
35
+ default=uuid.uuid4,
36
+ editable=False,
37
+ primary_key=True,
38
+ serialize=False,
39
+ verbose_name='Идентификатор UUID',
40
+ ),
41
+ ),
42
+ ('raw_message_value', models.BinaryField(verbose_name='Содержимое сообщения')),
43
+ ('raw_message_key', models.BinaryField(verbose_name='Ключ сообщения', null=True, blank=True)),
44
+ ('topic', models.CharField(max_length=256, verbose_name='Топик сообщения')),
45
+ ('attempts', JSONField(verbose_name='Попытки обработки сообщения')),
46
+ (
47
+ 'processed_at',
48
+ models.DateTimeField(blank=True, null=True, verbose_name='Время успешной обработки сообщения'),
49
+ ),
50
+ ('_first_attempted_at', models.DateTimeField(db_index=True)),
51
+ ('_attempts_count', models.IntegerField(db_index=True)),
52
+ ],
53
+ options={
54
+ 'verbose_name': 'Необработанное сообщение',
55
+ 'verbose_name_plural': 'Необработанные сообщения',
56
+ 'indexes': [models.Index(fields=['_attempts_count', '_first_attempted_at'], name='dlq_attempts_idx')],
57
+ 'db_table': 'dlq_dead_letter',
58
+ },
59
+ ),
60
+ ]