Coverage for /Users/eugene/Development/robotnikmq/robotnikmq/topic.py: 100%

1 import json

2 from typing import Optional

3

4 from pika.exceptions import AMQPError

5 from pika.exchange_type import ExchangeType

6 from tenacity import retry, retry_if_exception_type, wait_exponential

7 from typeguard import typechecked

8

9 from robotnikmq.config import RobotnikConfig

10 from robotnikmq.core import Robotnik, Message

11

12

13 class Topic(Robotnik):

14 """The Topic is one of two key elements of the Publish/Subscribe workflow with RobotnikMQ.

15 Once configured, a topic is able to broadcast messages to any Subscribers on a given exchange

16 and routing key combination.

17 """

18 @typechecked

19 def __init__(

20 self,

21 exchange: str,

22 config: Optional[RobotnikConfig] = None,

23 ):

24 super().__init__(config=config)

25 self.exchange = exchange

26 self.channel.exchange_declare(

27 exchange=self.exchange, exchange_type=ExchangeType.topic, auto_delete=True

28 )

29

30 @retry(

31 retry=retry_if_exception_type((AMQPError, OSError)),

32 wait=wait_exponential(multiplier=1, min=3, max=30),

33 reraise=True

34 )

35 @typechecked

36 def broadcast(

37 self,

38 msg: Message,

39 routing_key: Optional[str] = None

40 ) -> None:

41 """Broadcasts a message with an optional routing key.

42

43 Args:

44 msg (Message): The message to be broadcast.

45 routing_key (Optional[str], optional): Routing key used to broadcast the message.

46 Defaults to an empty string.

47 on_msg_error (AMQPErrorCallback, optional): _description_. Defaults to None.

48 """

49 msg = msg.with_routing_key(routing_key or msg.routing_key or "")

50 self.log.info("Broadcasting message (routing-key: [{}]):\n{}",

51 msg.routing_key, json.dumps(msg.to_dict(), indent=4))

52 self.channel.basic_publish(

53 exchange=self.exchange,

54 routing_key=msg.routing_key,

55 body=json.dumps(msg.to_dict()),

56 )

57 self.log.success("Broadcast:\n{}", json.dumps(msg.to_dict(), indent=4))