import base64 from typing import List, Mapping, Dict from dataclasses import dataclass, field from enum import Enum, auto from amqp.model.snowflake_id import SnowflakeId from amqp.router.utils import sanitize_as_rabbitmq_name RS = ":" @dataclass(frozen=True) class AMQRoute: """ Configuration item representing one AMQRoute. Adapter uses this to declare the Exchange and/or queue and bind the key as routing expression. :param componentName: a service/component name that this route points to (owner of the route) :param exchange: RabbitMQ exchange name :param queue: RabbitMQ queue name :param key: RabbitMQ TOPIC Exchange routing key :param timeout: Timeout in milliseconds, how long to wait for reply, or not waiting if less then 1 :param validUntil: optional timestamp (millis from epoch) after which the route is ignored """ componentName: str exchange: str queue: str key: str timeout: int validUntil: int FORMAT = "Component-Name:Exchange-Name:Queue-Name:Routing-Key:Timeout:Valid-Until" def __str__(self): return ( f"{self.componentName}{RS}{self.exchange}{RS}{self.queue}{RS}{self.key}" f"{RS}{self.timeout}{RS}{self.validUntil}" ) def __eq__(self, other): if isinstance(other, AMQRoute): return (self.componentName == other.componentName and self.queue == other.queue and self.key == other.key) return False @property def primary_key(self): return f"{self.componentName}{RS}{self.queue}{RS}{self.key}" def as_string(self) -> str: return self.__str__() @dataclass class AMQMessage: magic: str id: SnowflakeId method: str domain: str path: str headers: Mapping[str, List[str]] trace_info: Mapping[str, str] base64_body: bytes def routing_key(self) -> str: return sanitize_as_rabbitmq_name(f"{self.domain}.{self.path}") def body(self) -> bytes: return base64.b64decode(self.base64_body) @dataclass class AMQResponse: id: str response_code: int content_type: str response: bytes error: str error_cause: str trace_info: Mapping[str, str] def body(self) -> bytes: return base64.b64decode(self.response) class ServiceMessageType(Enum): ROUTING_DATA_REQUEST = auto() ROUTING_DATA = auto() ROUTING_DATA_REMOVE = auto() ROUTING_DATA_REPLACE = auto() UNKNOWN = auto() @dataclass(frozen=True) class CleverMicroServiceMessage: id: SnowflakeId = None payload_type: int = 0 message_type: ServiceMessageType = ServiceMessageType.UNKNOWN recipient_name: str = None reply_to: str = None trace_info: Dict[str, str] = field(default_factory=dict) message: str = None def is_valid(self) -> bool: return self.message_type is not None