veza/veza-backend-api/internal/websocket/message.go

139 lines
4.4 KiB
Go

package websocket
import (
"encoding/json"
"time"
"github.com/google/uuid"
)
// INT-014: Standardized WebSocket message format
// MessageType represents the type of WebSocket message
type MessageType string
const (
// Connection messages
MessageTypePing MessageType = "ping"
MessageTypePong MessageType = "pong"
MessageTypeError MessageType = "error"
// Subscription messages
MessageTypeSubscribe MessageType = "subscribe"
MessageTypeUnsubscribe MessageType = "unsubscribe"
MessageTypeSubscribed MessageType = "subscribed"
MessageTypeUnsubscribed MessageType = "unsubscribed"
// Chat messages
MessageTypeChatMessage MessageType = "chat_message"
MessageTypeTyping MessageType = "typing"
MessageTypeReadReceipt MessageType = "read_receipt"
MessageTypeUserJoined MessageType = "user_joined"
MessageTypeUserLeft MessageType = "user_left"
MessageTypeConversationUpdated MessageType = "conversation_updated"
// Playback messages
MessageTypePlaybackState MessageType = "playback_state"
MessageTypePlaybackSync MessageType = "playback_sync"
MessageTypeAnalyticsUpdate MessageType = "analytics_update"
MessageTypeStatsUpdate MessageType = "stats_update"
// Notification messages
MessageTypeNotification MessageType = "notification"
)
// WebSocketMessage represents a standardized WebSocket message format
// INT-014: All WebSocket messages follow this structure
type WebSocketMessage struct {
// Message identification
ID string `json:"id,omitempty"` // Unique message ID (UUID)
Type string `json:"type"` // Message type (required)
Timestamp string `json:"timestamp"` // ISO 8601 timestamp (RFC3339)
// Message data
Data interface{} `json:"data,omitempty"` // Message payload (optional)
// Error information (for error messages)
Error *MessageError `json:"error,omitempty"` // Error details (for error type messages)
// Context information
RequestID string `json:"request_id,omitempty"` // Request ID for correlation
UserID string `json:"user_id,omitempty"` // User ID (if applicable)
TrackID string `json:"track_id,omitempty"` // Track ID (if applicable)
ConversationID string `json:"conversation_id,omitempty"` // Conversation ID (if applicable)
}
// MessageError represents error information in WebSocket messages
type MessageError struct {
Code int `json:"code"` // Error code
Message string `json:"message"` // Error message
Details map[string]interface{} `json:"details,omitempty"` // Additional error details
}
// NewWebSocketMessage creates a new WebSocket message with standardized format
func NewWebSocketMessage(msgType MessageType, data interface{}) *WebSocketMessage {
return &WebSocketMessage{
ID: uuid.New().String(),
Type: string(msgType),
Timestamp: time.Now().UTC().Format(time.RFC3339),
Data: data,
}
}
// NewErrorMessage creates a new error WebSocket message
func NewErrorMessage(code int, message string, details map[string]interface{}) *WebSocketMessage {
return &WebSocketMessage{
ID: uuid.New().String(),
Type: string(MessageTypeError),
Timestamp: time.Now().UTC().Format(time.RFC3339),
Error: &MessageError{
Code: code,
Message: message,
Details: details,
},
}
}
// WithRequestID sets the request ID for correlation
func (m *WebSocketMessage) WithRequestID(requestID string) *WebSocketMessage {
m.RequestID = requestID
return m
}
// WithUserID sets the user ID
func (m *WebSocketMessage) WithUserID(userID string) *WebSocketMessage {
m.UserID = userID
return m
}
// WithTrackID sets the track ID
func (m *WebSocketMessage) WithTrackID(trackID string) *WebSocketMessage {
m.TrackID = trackID
return m
}
// WithConversationID sets the conversation ID
func (m *WebSocketMessage) WithConversationID(conversationID string) *WebSocketMessage {
m.ConversationID = conversationID
return m
}
// ToJSON converts the message to JSON bytes
func (m *WebSocketMessage) ToJSON() ([]byte, error) {
return json.Marshal(m)
}
// ParseWebSocketMessage parses a JSON message into WebSocketMessage
func ParseWebSocketMessage(data []byte) (*WebSocketMessage, error) {
var msg WebSocketMessage
if err := json.Unmarshal(data, &msg); err != nil {
return nil, err
}
return &msg, nil
}
// IsValid checks if the message has required fields
func (m *WebSocketMessage) IsValid() bool {
return m.Type != "" && m.Timestamp != ""
}