Files
amq-adapter-python/amqp/adapter/service_message_factory.py
T

109 lines
4.7 KiB
Python

import base64
import logging
from amqp.adapter.amq_message_factory import AMQMessageFactory
from amqp.adapter.trace_info_adapter import TraceInfoAdapter
from amqp.model.model import CleverMicroServiceMessage
from amqp.model.service_message_type import ServiceMessageType
from amqp.model.snowflake_id import SnowflakeId
from amqp.router.serializer import parse_map_string, map_as_string
RS = "|"
SPLIT_RS = r"\|"
INVALID_MESSAGE = CleverMicroServiceMessage() # default values mean invalid message
class CleverMicroServiceMessageFactory:
def __init__(self, name: str):
self.process_name = name
def to_string(self, message: CleverMicroServiceMessage) -> str:
if message is not None:
return (
f"CleverMicroServiceMessage{{id={message.id.as_string()}|type={self.format_message_type(message)}|"
f"recipient={message.recipient_name}|replyTo={message.reply_to}|"
f"traceInfo={message.trace_info}|body={base64.b64encode(message.message.encode()).decode() if message.payload_type & 0x80 else message.message}}}"
)
return "null"
def of(self, message_type: ServiceMessageType, payload: str, recipient_name: str) -> CleverMicroServiceMessage:
_id = AMQMessageFactory.get_instance(1).generate_snowflake_id()
return CleverMicroServiceMessage(
id=_id,
payload_type=0,
message_type=message_type,
recipient_name=recipient_name,
reply_to=self.process_name,
trace_info=TraceInfoAdapter.create_produce_span(_id),
message=payload
)
def as_base64(self, message_type: ServiceMessageType, payload: str, recipient_name: str) -> CleverMicroServiceMessage:
_id = AMQMessageFactory.get_instance(1).generate_snowflake_id()
return CleverMicroServiceMessage(
id=_id,
payload_type=0x80,
message_type=message_type,
recipient_name=recipient_name,
reply_to=self.process_name,
trace_info=TraceInfoAdapter.create_produce_span(_id.as_string()),
message=payload
)
def from_bytes(self, input_bytes: bytes) -> CleverMicroServiceMessage:
logging.info(f"CleverMicroServiceMessage >>> [{input_bytes.decode()}]")
input_str_array = input_bytes.decode().split(RS)
try:
i = 0
_id = input_str_array[i]
i += 1
payload_type = int(input_str_array[i], 16)
i += 1
message_type = ServiceMessageType(payload_type + 1 & 0x7F)
recipient_name = input_str_array[i] if i < len(input_str_array) else ""
i += 1
reply_to = input_str_array[i] if i < len(input_str_array) else ""
i += 1
trace_info = parse_map_string(input_str_array[i]) if i < len(input_str_array) else {}
i += 1
message = input_str_array[i] if i < len(input_str_array) else ""
return CleverMicroServiceMessage(
id=SnowflakeId.from_hex(_id),
payload_type=payload_type,
message_type=message_type,
recipient_name=recipient_name,
reply_to=reply_to,
trace_info=trace_info,
message=base64.b64decode(message).decode() if payload_type & 0x80 else message
)
except Exception as e:
logging.error(
"Unexpected ServiceMessage format. Expected 6 tokens separated by '|' in format: "
"[ID(64bit number)|MessageType(8bit number)|RecipientName(String)|"
"ReplyTo(String)|TraceInfo|Payload(String or Base64)], got: "
"[%s], reported problem: %s", input_bytes.decode(), str(e)
)
return INVALID_MESSAGE
def serialize(self, message: CleverMicroServiceMessage) -> bytes:
return (
f"{message.id.as_string()}{RS}{self.format_message_type(message)}{RS}{message.recipient_name}{RS}"
f"{message.reply_to}{RS}{map_as_string(message.trace_info)}{RS}"
f"{base64.b64encode(message.message.encode()).decode() if message.payload_type != 0 else message.message}"
).encode('utf-8')
def format_message_type(self, message: CleverMicroServiceMessage) -> str:
"""
Formats the Enum message type and ecorates it with base64 flag at highest bit.
For compatibility with 0-based enum ordinals in Java subtract 1 from ordinal value.
:param message: Service message to send
:return: formatted message type
"""
num_type = (message.message_type.value if message.message_type else ServiceMessageType.UNKNOWN.value) - 1
fmt = ((num_type & 0x7F) | (message.payload_type & 0x80))
return f"{fmt:02X}"