weenspace-queue 0.1.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.
- weenspace_queue/__init__.py +128 -0
- weenspace_queue/asyncio/__init__.py +4 -0
- weenspace_queue/asyncio/aws_async.py +38 -0
- weenspace_queue/asyncio/rabbitmq_async.py +182 -0
- weenspace_queue/base.py +103 -0
- weenspace_queue/constants.py +75 -0
- weenspace_queue/providers/__init__.py +4 -0
- weenspace_queue/providers/aws.py +273 -0
- weenspace_queue/providers/rabbitmq.py +205 -0
- weenspace_queue/utils.py +271 -0
- weenspace_queue-0.1.0.dist-info/METADATA +161 -0
- weenspace_queue-0.1.0.dist-info/RECORD +14 -0
- weenspace_queue-0.1.0.dist-info/WHEEL +4 -0
- weenspace_queue-0.1.0.dist-info/licenses/LICENSE +21 -0
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
from typing import Any, Callable, Dict, Type
|
|
2
|
+
|
|
3
|
+
from .asyncio.aws_async import AwsAsyncEngine
|
|
4
|
+
from .asyncio.rabbitmq_async import RabbitMqAsyncEngine
|
|
5
|
+
from .base import (
|
|
6
|
+
AsyncQueueEngine,
|
|
7
|
+
Message,
|
|
8
|
+
QueueEngine,
|
|
9
|
+
QueueSpecification,
|
|
10
|
+
TopicSpecification,
|
|
11
|
+
)
|
|
12
|
+
from .constants import (
|
|
13
|
+
PROVIDER_AWS,
|
|
14
|
+
PROVIDER_RABBITMQ,
|
|
15
|
+
ExchangeKind,
|
|
16
|
+
Provider,
|
|
17
|
+
QueueKind,
|
|
18
|
+
)
|
|
19
|
+
from .providers.aws import AwsEngine
|
|
20
|
+
from .providers.rabbitmq import RabbitMqEngine
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
class QueueClient:
|
|
24
|
+
"""Single client: pass provider name, then use the same publish/consume/topology methods."""
|
|
25
|
+
|
|
26
|
+
_ENGINES: Dict[str, Type[QueueEngine]] = {
|
|
27
|
+
PROVIDER_AWS: AwsEngine,
|
|
28
|
+
PROVIDER_RABBITMQ: RabbitMqEngine,
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
def __init__(self, provider: str, **config: Any) -> None:
|
|
32
|
+
prov_key = provider.lower().strip()
|
|
33
|
+
engine_cls = self._ENGINES.get(prov_key)
|
|
34
|
+
if engine_cls is None:
|
|
35
|
+
supported = ", ".join(sorted(self._ENGINES))
|
|
36
|
+
raise ValueError(
|
|
37
|
+
f"Unsupported provider '{provider}'. Supported: {supported}"
|
|
38
|
+
)
|
|
39
|
+
self.provider = prov_key
|
|
40
|
+
self.engine: QueueEngine = engine_cls(**config)
|
|
41
|
+
|
|
42
|
+
def declare_queue(self, spec: QueueSpecification) -> str:
|
|
43
|
+
return self.engine.declare_queue(spec)
|
|
44
|
+
|
|
45
|
+
def declare_topic(self, spec: TopicSpecification) -> str:
|
|
46
|
+
return self.engine.declare_topic(spec)
|
|
47
|
+
|
|
48
|
+
def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
49
|
+
self.engine.bind_pattern(queue_id, topic_id, pattern)
|
|
50
|
+
|
|
51
|
+
def publish(self, destination: str, message: Message) -> None:
|
|
52
|
+
self.engine.publish(destination, message)
|
|
53
|
+
|
|
54
|
+
def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
55
|
+
self.engine.consume(queue_id, handler)
|
|
56
|
+
|
|
57
|
+
def stop(self) -> None:
|
|
58
|
+
self.engine.stop()
|
|
59
|
+
|
|
60
|
+
def close(self) -> None:
|
|
61
|
+
self.engine.close()
|
|
62
|
+
|
|
63
|
+
def __enter__(self) -> "QueueClient":
|
|
64
|
+
return self
|
|
65
|
+
|
|
66
|
+
def __exit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
67
|
+
self.close()
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
class AsyncQueueClient:
|
|
71
|
+
"""Async twin of QueueClient. Same method names, same provider argument."""
|
|
72
|
+
|
|
73
|
+
_ENGINES: Dict[str, Type[AsyncQueueEngine]] = {
|
|
74
|
+
PROVIDER_AWS: AwsAsyncEngine,
|
|
75
|
+
PROVIDER_RABBITMQ: RabbitMqAsyncEngine,
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
def __init__(self, provider: str, **config: Any) -> None:
|
|
79
|
+
prov_key = provider.lower().strip()
|
|
80
|
+
engine_cls = self._ENGINES.get(prov_key)
|
|
81
|
+
if engine_cls is None:
|
|
82
|
+
supported = ", ".join(sorted(self._ENGINES))
|
|
83
|
+
raise ValueError(
|
|
84
|
+
f"Unsupported provider '{provider}'. Supported: {supported}"
|
|
85
|
+
)
|
|
86
|
+
self.provider = prov_key
|
|
87
|
+
self.engine: AsyncQueueEngine = engine_cls(**config)
|
|
88
|
+
|
|
89
|
+
async def declare_queue(self, spec: QueueSpecification) -> str:
|
|
90
|
+
return await self.engine.declare_queue(spec)
|
|
91
|
+
|
|
92
|
+
async def declare_topic(self, spec: TopicSpecification) -> str:
|
|
93
|
+
return await self.engine.declare_topic(spec)
|
|
94
|
+
|
|
95
|
+
async def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
96
|
+
await self.engine.bind_pattern(queue_id, topic_id, pattern)
|
|
97
|
+
|
|
98
|
+
async def publish(self, destination: str, message: Message) -> None:
|
|
99
|
+
await self.engine.publish(destination, message)
|
|
100
|
+
|
|
101
|
+
async def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
102
|
+
await self.engine.consume(queue_id, handler)
|
|
103
|
+
|
|
104
|
+
async def stop(self) -> None:
|
|
105
|
+
await self.engine.stop()
|
|
106
|
+
|
|
107
|
+
async def close(self) -> None:
|
|
108
|
+
await self.engine.close()
|
|
109
|
+
|
|
110
|
+
async def __aenter__(self) -> "AsyncQueueClient":
|
|
111
|
+
return self
|
|
112
|
+
|
|
113
|
+
async def __aexit__(self, exc_type: Any, exc: Any, tb: Any) -> None:
|
|
114
|
+
await self.close()
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
__all__ = [
|
|
118
|
+
"AsyncQueueClient",
|
|
119
|
+
"AsyncQueueEngine",
|
|
120
|
+
"ExchangeKind",
|
|
121
|
+
"Message",
|
|
122
|
+
"Provider",
|
|
123
|
+
"QueueClient",
|
|
124
|
+
"QueueEngine",
|
|
125
|
+
"QueueKind",
|
|
126
|
+
"QueueSpecification",
|
|
127
|
+
"TopicSpecification",
|
|
128
|
+
]
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import asyncio
|
|
4
|
+
from weenspace_queue.base import (
|
|
5
|
+
AsyncQueueEngine,
|
|
6
|
+
Message,
|
|
7
|
+
QueueSpecification,
|
|
8
|
+
TopicSpecification,
|
|
9
|
+
)
|
|
10
|
+
from weenspace_queue.providers.aws import AwsEngine
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
class AwsAsyncEngine(AsyncQueueEngine):
|
|
14
|
+
"""Async AWS engine. Uses aioboto3 when installed, otherwise boto3 in a worker thread."""
|
|
15
|
+
|
|
16
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
17
|
+
self._sync = AwsEngine(**kwargs)
|
|
18
|
+
|
|
19
|
+
async def declare_queue(self, spec: QueueSpecification) -> str:
|
|
20
|
+
return await asyncio.to_thread(self._sync.declare_queue, spec)
|
|
21
|
+
|
|
22
|
+
async def declare_topic(self, spec: TopicSpecification) -> str:
|
|
23
|
+
return await asyncio.to_thread(self._sync.declare_topic, spec)
|
|
24
|
+
|
|
25
|
+
async def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
26
|
+
await asyncio.to_thread(self._sync.bind_pattern, queue_id, topic_id, pattern)
|
|
27
|
+
|
|
28
|
+
async def publish(self, destination: str, message: Message) -> None:
|
|
29
|
+
await asyncio.to_thread(self._sync.publish, destination, message)
|
|
30
|
+
|
|
31
|
+
async def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
32
|
+
await asyncio.to_thread(self._sync.consume, queue_id, handler)
|
|
33
|
+
|
|
34
|
+
async def stop(self) -> None:
|
|
35
|
+
self._sync.stop()
|
|
36
|
+
|
|
37
|
+
async def close(self) -> None:
|
|
38
|
+
await asyncio.to_thread(self._sync.close)
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
from typing import Any, Callable, Optional
|
|
4
|
+
|
|
5
|
+
from weenspace_queue import (
|
|
6
|
+
ClassicQueueSpecification,
|
|
7
|
+
ExchangeSpecification,
|
|
8
|
+
ExchangeToQueueBindingSpecification,
|
|
9
|
+
ExchangeType,
|
|
10
|
+
Message as ProtonMessage,
|
|
11
|
+
QuorumQueueSpecification,
|
|
12
|
+
StreamSpecification,
|
|
13
|
+
)
|
|
14
|
+
from weenspace_queue.asyncio import AsyncEnvironment
|
|
15
|
+
from weenspace_queue.delivery_context import DeliveryContext
|
|
16
|
+
from weenspace_queue.amqp_consumer_handler import AMQPMessagingHandler
|
|
17
|
+
from weenspace_queue.qpid.proton._events import Event
|
|
18
|
+
|
|
19
|
+
from weenspace_queue.base import (
|
|
20
|
+
AsyncQueueEngine,
|
|
21
|
+
Message,
|
|
22
|
+
QueueSpecification,
|
|
23
|
+
TopicSpecification,
|
|
24
|
+
)
|
|
25
|
+
from weenspace_queue.constants import (
|
|
26
|
+
DEFAULT_RABBITMQ_URI,
|
|
27
|
+
ExchangeKind,
|
|
28
|
+
QueueKind,
|
|
29
|
+
)
|
|
30
|
+
from weenspace_queue.utils import (
|
|
31
|
+
encode_body,
|
|
32
|
+
rabbitmq_exchange_address,
|
|
33
|
+
rabbitmq_queue_address,
|
|
34
|
+
rabbitmq_resource_name,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
_EXCHANGE_KIND_MAP = {
|
|
38
|
+
ExchangeKind.DIRECT: ExchangeType.DIRECT,
|
|
39
|
+
ExchangeKind.TOPIC: ExchangeType.TOPIC,
|
|
40
|
+
ExchangeKind.FANOUT: ExchangeType.FANOUT,
|
|
41
|
+
ExchangeKind.HEADERS: ExchangeType.HEADERS,
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class _CallbackHandler(AMQPMessagingHandler):
|
|
46
|
+
def __init__(self, handler: Callable[[Message], None]) -> None:
|
|
47
|
+
super().__init__(auto_accept=False, auto_settle=True)
|
|
48
|
+
self._handler = handler
|
|
49
|
+
|
|
50
|
+
def on_amqp_message(self, event: Event) -> None:
|
|
51
|
+
proton_msg = event.message
|
|
52
|
+
routing_key = proton_msg.subject or ""
|
|
53
|
+
attributes = dict(proton_msg.properties or {})
|
|
54
|
+
context = DeliveryContext()
|
|
55
|
+
|
|
56
|
+
def accept(evt: Event = event) -> None:
|
|
57
|
+
context.accept(evt)
|
|
58
|
+
|
|
59
|
+
def reject(evt: Event = event) -> None:
|
|
60
|
+
context.discard(evt)
|
|
61
|
+
|
|
62
|
+
def requeue(evt: Event = event) -> None:
|
|
63
|
+
context.requeue(evt)
|
|
64
|
+
|
|
65
|
+
self._handler(
|
|
66
|
+
Message(
|
|
67
|
+
body=encode_body(proton_msg.body),
|
|
68
|
+
routing_key=routing_key,
|
|
69
|
+
attributes=attributes,
|
|
70
|
+
accept=accept,
|
|
71
|
+
reject=reject,
|
|
72
|
+
requeue=requeue,
|
|
73
|
+
)
|
|
74
|
+
)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class RabbitMqAsyncEngine(AsyncQueueEngine):
|
|
78
|
+
def __init__(self, **kwargs: Any) -> None:
|
|
79
|
+
uri = kwargs.get("uri")
|
|
80
|
+
uris = kwargs.get("uris")
|
|
81
|
+
if uri is None and uris is None:
|
|
82
|
+
uri = DEFAULT_RABBITMQ_URI
|
|
83
|
+
env_kwargs: dict[str, Any] = {"uri": uri, "uris": uris}
|
|
84
|
+
for key in ("ssl_context", "oauth2_options", "recovery_configuration"):
|
|
85
|
+
if kwargs.get(key) is not None:
|
|
86
|
+
env_kwargs[key] = kwargs[key]
|
|
87
|
+
self._env = AsyncEnvironment(**env_kwargs)
|
|
88
|
+
self._conn: Any = None
|
|
89
|
+
self._mgmt: Any = None
|
|
90
|
+
self._consumer: Any = None
|
|
91
|
+
|
|
92
|
+
async def _ensure(self) -> None:
|
|
93
|
+
if self._conn is not None:
|
|
94
|
+
return
|
|
95
|
+
self._conn = await self._env.connection()
|
|
96
|
+
await self._conn.dial()
|
|
97
|
+
self._mgmt = await self._conn.management()
|
|
98
|
+
|
|
99
|
+
async def declare_queue(self, spec: QueueSpecification) -> str:
|
|
100
|
+
await self._ensure()
|
|
101
|
+
if spec.kind == QueueKind.STREAM:
|
|
102
|
+
await self._mgmt.declare_queue(StreamSpecification(name=spec.name))
|
|
103
|
+
elif spec.kind == QueueKind.QUORUM:
|
|
104
|
+
await self._mgmt.declare_queue(
|
|
105
|
+
QuorumQueueSpecification(
|
|
106
|
+
name=spec.name,
|
|
107
|
+
dead_letter_exchange=spec.dead_letter_target,
|
|
108
|
+
dead_letter_routing_key=spec.dead_letter_routing_key,
|
|
109
|
+
deliver_limit=spec.max_receive_count,
|
|
110
|
+
)
|
|
111
|
+
)
|
|
112
|
+
else:
|
|
113
|
+
await self._mgmt.declare_queue(
|
|
114
|
+
ClassicQueueSpecification(
|
|
115
|
+
name=spec.name,
|
|
116
|
+
is_durable=spec.is_durable,
|
|
117
|
+
dead_letter_exchange=spec.dead_letter_target,
|
|
118
|
+
dead_letter_routing_key=spec.dead_letter_routing_key,
|
|
119
|
+
)
|
|
120
|
+
)
|
|
121
|
+
return rabbitmq_queue_address(spec.name)
|
|
122
|
+
|
|
123
|
+
async def declare_topic(self, spec: TopicSpecification) -> str:
|
|
124
|
+
await self._ensure()
|
|
125
|
+
await self._mgmt.declare_exchange(
|
|
126
|
+
ExchangeSpecification(
|
|
127
|
+
name=spec.name,
|
|
128
|
+
exchange_type=_EXCHANGE_KIND_MAP.get(spec.kind, ExchangeType.TOPIC),
|
|
129
|
+
is_durable=spec.is_durable,
|
|
130
|
+
)
|
|
131
|
+
)
|
|
132
|
+
return spec.name
|
|
133
|
+
|
|
134
|
+
async def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
135
|
+
await self._ensure()
|
|
136
|
+
await self._mgmt.bind(
|
|
137
|
+
ExchangeToQueueBindingSpecification(
|
|
138
|
+
source_exchange=rabbitmq_resource_name(topic_id),
|
|
139
|
+
destination_queue=rabbitmq_resource_name(queue_id),
|
|
140
|
+
binding_key=pattern,
|
|
141
|
+
)
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
async def publish(self, destination: str, message: Message) -> None:
|
|
145
|
+
await self._ensure()
|
|
146
|
+
address = self._publish_address(destination, message.routing_key)
|
|
147
|
+
publisher = await self._conn.publisher(address)
|
|
148
|
+
try:
|
|
149
|
+
proton_msg = ProtonMessage(body=encode_body(message.body))
|
|
150
|
+
proton_msg.inferred = True
|
|
151
|
+
if message.routing_key:
|
|
152
|
+
proton_msg.subject = message.routing_key
|
|
153
|
+
await publisher.publish(proton_msg)
|
|
154
|
+
finally:
|
|
155
|
+
await publisher.close()
|
|
156
|
+
|
|
157
|
+
async def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
158
|
+
await self._ensure()
|
|
159
|
+
destination = rabbitmq_queue_address(queue_id)
|
|
160
|
+
self._consumer = await self._conn.consumer(
|
|
161
|
+
destination, message_handler=_CallbackHandler(handler)
|
|
162
|
+
)
|
|
163
|
+
await self._consumer.run()
|
|
164
|
+
|
|
165
|
+
async def stop(self) -> None:
|
|
166
|
+
if self._consumer is not None:
|
|
167
|
+
await self._consumer.stop()
|
|
168
|
+
|
|
169
|
+
async def close(self) -> None:
|
|
170
|
+
await self.stop()
|
|
171
|
+
if self._conn is not None:
|
|
172
|
+
await self._conn.close()
|
|
173
|
+
await self._env.close()
|
|
174
|
+
|
|
175
|
+
def _publish_address(self, destination: str, routing_key: str) -> str:
|
|
176
|
+
if destination.startswith("/queues/"):
|
|
177
|
+
return destination
|
|
178
|
+
if destination.startswith("/exchanges/"):
|
|
179
|
+
return rabbitmq_exchange_address(destination, routing_key)
|
|
180
|
+
if routing_key:
|
|
181
|
+
return rabbitmq_exchange_address(destination, routing_key)
|
|
182
|
+
return rabbitmq_queue_address(destination)
|
weenspace_queue/base.py
ADDED
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
from abc import ABC, abstractmethod
|
|
2
|
+
from dataclasses import dataclass, field
|
|
3
|
+
from typing import Any, Callable, Dict, Optional
|
|
4
|
+
|
|
5
|
+
from .constants import (
|
|
6
|
+
DEFAULT_MAX_RECEIVE_COUNT,
|
|
7
|
+
ExchangeKind,
|
|
8
|
+
QueueKind,
|
|
9
|
+
)
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
@dataclass
|
|
13
|
+
class Message:
|
|
14
|
+
body: bytes
|
|
15
|
+
routing_key: str = ""
|
|
16
|
+
attributes: Dict[str, Any] = field(default_factory=dict)
|
|
17
|
+
accept: Optional[Callable[[], None]] = None
|
|
18
|
+
reject: Optional[Callable[[], None]] = None
|
|
19
|
+
requeue: Optional[Callable[[], None]] = None
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
@dataclass
|
|
23
|
+
class QueueSpecification:
|
|
24
|
+
name: str
|
|
25
|
+
is_durable: bool = True
|
|
26
|
+
kind: QueueKind = QueueKind.CLASSIC
|
|
27
|
+
dead_letter_target: Optional[str] = None
|
|
28
|
+
dead_letter_routing_key: Optional[str] = None
|
|
29
|
+
max_receive_count: int = DEFAULT_MAX_RECEIVE_COUNT
|
|
30
|
+
visibility_timeout_seconds: Optional[int] = None
|
|
31
|
+
extra: Dict[str, Any] = field(default_factory=dict)
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
@dataclass
|
|
35
|
+
class TopicSpecification:
|
|
36
|
+
name: str
|
|
37
|
+
is_durable: bool = True
|
|
38
|
+
kind: ExchangeKind = ExchangeKind.TOPIC
|
|
39
|
+
extra: Dict[str, Any] = field(default_factory=dict)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class QueueEngine(ABC):
|
|
43
|
+
"""Provider strategy: one method set for publish, consume, and topology."""
|
|
44
|
+
|
|
45
|
+
@abstractmethod
|
|
46
|
+
def declare_queue(self, spec: QueueSpecification) -> str:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
@abstractmethod
|
|
50
|
+
def declare_topic(self, spec: TopicSpecification) -> str:
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
@abstractmethod
|
|
54
|
+
def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
@abstractmethod
|
|
58
|
+
def publish(self, destination: str, message: Message) -> None:
|
|
59
|
+
pass
|
|
60
|
+
|
|
61
|
+
@abstractmethod
|
|
62
|
+
def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
63
|
+
pass
|
|
64
|
+
|
|
65
|
+
@abstractmethod
|
|
66
|
+
def stop(self) -> None:
|
|
67
|
+
pass
|
|
68
|
+
|
|
69
|
+
@abstractmethod
|
|
70
|
+
def close(self) -> None:
|
|
71
|
+
pass
|
|
72
|
+
|
|
73
|
+
|
|
74
|
+
class AsyncQueueEngine(ABC):
|
|
75
|
+
"""Async counterpart of QueueEngine with identical method names."""
|
|
76
|
+
|
|
77
|
+
@abstractmethod
|
|
78
|
+
async def declare_queue(self, spec: QueueSpecification) -> str:
|
|
79
|
+
pass
|
|
80
|
+
|
|
81
|
+
@abstractmethod
|
|
82
|
+
async def declare_topic(self, spec: TopicSpecification) -> str:
|
|
83
|
+
pass
|
|
84
|
+
|
|
85
|
+
@abstractmethod
|
|
86
|
+
async def bind_pattern(self, queue_id: str, topic_id: str, pattern: str) -> None:
|
|
87
|
+
pass
|
|
88
|
+
|
|
89
|
+
@abstractmethod
|
|
90
|
+
async def publish(self, destination: str, message: Message) -> None:
|
|
91
|
+
pass
|
|
92
|
+
|
|
93
|
+
@abstractmethod
|
|
94
|
+
async def consume(self, queue_id: str, handler: Callable[[Message], None]) -> None:
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
@abstractmethod
|
|
98
|
+
async def stop(self) -> None:
|
|
99
|
+
pass
|
|
100
|
+
|
|
101
|
+
@abstractmethod
|
|
102
|
+
async def close(self) -> None:
|
|
103
|
+
pass
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
"""Shared uppercase constants for the unified queue client."""
|
|
2
|
+
|
|
3
|
+
from enum import Enum
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class Provider(str, Enum):
|
|
7
|
+
AWS = "aws"
|
|
8
|
+
RABBITMQ = "rabbitmq"
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class QueueKind(str, Enum):
|
|
12
|
+
CLASSIC = "classic"
|
|
13
|
+
QUORUM = "quorum"
|
|
14
|
+
STREAM = "stream"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class ExchangeKind(str, Enum):
|
|
18
|
+
DIRECT = "direct"
|
|
19
|
+
TOPIC = "topic"
|
|
20
|
+
FANOUT = "fanout"
|
|
21
|
+
HEADERS = "headers"
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
PROVIDER_AWS = Provider.AWS.value
|
|
25
|
+
PROVIDER_RABBITMQ = Provider.RABBITMQ.value
|
|
26
|
+
|
|
27
|
+
DEFAULT_AWS_REGION = "us-east-1"
|
|
28
|
+
DEFAULT_RABBITMQ_URI = "amqp://guest:guest@localhost:5672/"
|
|
29
|
+
DEFAULT_MAX_RECEIVE_COUNT = 5
|
|
30
|
+
DEFAULT_SQS_WAIT_TIME_SECONDS = 20
|
|
31
|
+
DEFAULT_SQS_MAX_MESSAGES = 10
|
|
32
|
+
DEFAULT_SQS_RETENTION_SECONDS = "345600"
|
|
33
|
+
DEFAULT_SQS_VISIBILITY_TIMEOUT_SECONDS = 30
|
|
34
|
+
|
|
35
|
+
SNS_ARN_MARKER = "arn:aws:sns"
|
|
36
|
+
SQS_ARN_MARKER = "arn:aws:sqs"
|
|
37
|
+
SQS_URL_MARKER = "amazonaws.com"
|
|
38
|
+
SQS_FIFO_SUFFIX = ".fifo"
|
|
39
|
+
|
|
40
|
+
WILDCARD_SINGLE = "*"
|
|
41
|
+
WILDCARD_MULTI = "#"
|
|
42
|
+
ROUTING_SEPARATOR = "."
|
|
43
|
+
|
|
44
|
+
ROUTING_KEY_ATTR = "routing_key"
|
|
45
|
+
ROUTING_KEY_LENGTH_ATTR = "rk_len"
|
|
46
|
+
ROUTING_KEY_INDEX_PREFIX = "rk_idx_"
|
|
47
|
+
ROUTING_KEY_REVERSE_INDEX_PREFIX = "rk_ridx_"
|
|
48
|
+
|
|
49
|
+
AWS_STRING_DATA_TYPE = "String"
|
|
50
|
+
AWS_NUMBER_DATA_TYPE = "Number"
|
|
51
|
+
SNS_FILTER_POLICY_SCOPE_MESSAGE_ATTRIBUTES = "MessageAttributes"
|
|
52
|
+
SNS_PROTOCOL_SQS = "sqs"
|
|
53
|
+
|
|
54
|
+
RABBITMQ_QUEUE_PREFIX = "/queues/"
|
|
55
|
+
RABBITMQ_EXCHANGE_PREFIX = "/exchanges/"
|
|
56
|
+
|
|
57
|
+
SQS_RECEIPT_HANDLE_ATTR = "ReceiptHandle"
|
|
58
|
+
SQS_MESSAGE_ATTRIBUTES_KEY = "MessageAttributes"
|
|
59
|
+
SQS_BODY_KEY = "Body"
|
|
60
|
+
SNS_NOTIFICATION_TYPE = "Notification"
|
|
61
|
+
SNS_MESSAGE_KEY = "Message"
|
|
62
|
+
|
|
63
|
+
IAM_POLICY_VERSION = "2012-10-17"
|
|
64
|
+
SQS_SEND_MESSAGE_ACTION = "sqs:SendMessage"
|
|
65
|
+
SNS_SERVICE_PRINCIPAL = "sns.amazonaws.com"
|
|
66
|
+
SQS_POLICY_ATTRIBUTE = "Policy"
|
|
67
|
+
SQS_QUEUE_ARN_ATTRIBUTE = "QueueArn"
|
|
68
|
+
SQS_REDRIVE_POLICY_ATTRIBUTE = "RedrivePolicy"
|
|
69
|
+
SQS_RETENTION_ATTRIBUTE = "MessageRetentionPeriod"
|
|
70
|
+
SQS_VISIBILITY_ATTRIBUTE = "VisibilityTimeout"
|
|
71
|
+
DEAD_LETTER_TARGET_ARN_KEY = "deadLetterTargetArn"
|
|
72
|
+
MAX_RECEIVE_COUNT_KEY = "maxReceiveCount"
|
|
73
|
+
|
|
74
|
+
AWS_SOURCE_ARN_CONDITION = "aws:SourceArn"
|
|
75
|
+
ALLOW_SNS_SEND_SID_PREFIX = "AllowSnsSendMessage"
|