using System.Text; using RabbitMQ.Client; using StellaOps.Router.Common.Enums; using StellaOps.Router.Common.Models; namespace StellaOps.Router.Transport.RabbitMq; /// /// Handles serialization and deserialization of frames for RabbitMQ transport. /// public static class RabbitMqFrameProtocol { /// /// Parses a frame from a RabbitMQ message. /// /// The message body. /// The message properties. /// The parsed frame. public static Frame ParseFrame(ReadOnlyMemory body, IReadOnlyBasicProperties properties) { var frameType = ParseFrameType(properties.Type); var correlationId = properties.CorrelationId; return new Frame { Type = frameType, CorrelationId = correlationId, Payload = body }; } /// /// Creates BasicProperties for a frame. /// /// The frame to serialize. /// The reply queue name. /// Optional timeout for the message. /// The basic properties. public static BasicProperties CreateProperties(Frame frame, string? replyTo, TimeSpan? timeout = null) { var props = new BasicProperties { Type = frame.Type.ToString(), Timestamp = new AmqpTimestamp(DateTimeOffset.UtcNow.ToUnixTimeSeconds()), DeliveryMode = DeliveryModes.Transient // Non-persistent (1) }; if (!string.IsNullOrEmpty(frame.CorrelationId)) { props.CorrelationId = frame.CorrelationId; } if (!string.IsNullOrEmpty(replyTo)) { props.ReplyTo = replyTo; } if (timeout.HasValue) { props.Expiration = ((int)timeout.Value.TotalMilliseconds).ToString(); } return props; } /// /// Parses a FrameType from the message Type property. /// private static FrameType ParseFrameType(string? type) { if (string.IsNullOrEmpty(type)) { return FrameType.Request; } if (Enum.TryParse(type, ignoreCase: true, out var result)) { return result; } return FrameType.Request; } /// /// Extracts the connection ID from message properties. /// /// The message properties. /// The connection ID. public static string ExtractConnectionId(IReadOnlyBasicProperties properties) { // Use ReplyTo as the basis for connection ID (identifies the instance) if (!string.IsNullOrEmpty(properties.ReplyTo)) { // Extract instance ID from queue name like "stella.svc.{instanceId}" var parts = properties.ReplyTo.Split('.'); if (parts.Length >= 3) { return $"rmq-{parts[^1]}"; } return $"rmq-{properties.ReplyTo}"; } // Fallback to correlation ID if (!string.IsNullOrEmpty(properties.CorrelationId)) { return $"rmq-{properties.CorrelationId[..Math.Min(16, properties.CorrelationId.Length)]}"; } return $"rmq-{Guid.NewGuid():N}"[..32]; } }