108 lines
4.7 KiB
Python
108 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, 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:
|
|
print(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: "
|
|
"[{}], reported problem: {}", 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}"
|