Files
telecom-platform/apps/api-server/internal/mq/mq.go
T
nutcas3 6cf546712b refactor: Replace interface{} with any type alias across codebase
- Replace interface{} with any in api_integration_test.go makeRequest method and test response maps
- Replace interface{} with any in logging.go WithField method and all logging function signatures
- Replace interface{} with any in middleware etag.go sync.Map.Range callback
- Replace interface{} with any in middleware redis_ratelimit.go RedisClientInterface and result parsing
- Replace interface{} with any in middleware redis_ratelimit_
2026-04-25 06:31:02 +03:00

214 lines
4.5 KiB
Go

package mq
import (
"context"
"encoding/json"
"fmt"
"log"
"os"
"time"
amqp "github.com/rabbitmq/amqp091-go"
)
// MessageQueue provides a simple interface for message queue operations
type MessageQueue struct {
connection *amqp.Connection
channel *amqp.Channel
url string
}
// Message represents a message to be sent/received
type Message struct {
Type string `json:"type"`
Payload map[string]any `json:"payload"`
Timestamp time.Time `json:"timestamp"`
}
// NewMessageQueue creates a new message queue connection
func NewMessageQueue(url string) (*MessageQueue, error) {
if url == "" {
url = os.Getenv("RABBITMQ_URL")
if url == "" {
url = "amqp://guest:guest@localhost:5672/"
}
}
conn, err := amqp.Dial(url)
if err != nil {
return nil, fmt.Errorf("failed to connect to RabbitMQ: %w", err)
}
ch, err := conn.Channel()
if err != nil {
conn.Close()
return nil, fmt.Errorf("failed to open channel: %w", err)
}
mq := &MessageQueue{
connection: conn,
channel: ch,
url: url,
}
return mq, nil
}
// Close closes the connection to the message queue
func (mq *MessageQueue) Close() error {
if mq.channel != nil {
mq.channel.Close()
}
if mq.connection != nil {
return mq.connection.Close()
}
return nil
}
// DeclareQueue declares a queue
func (mq *MessageQueue) DeclareQueue(name string) error {
_, err := mq.channel.QueueDeclare(
name,
true, // durable
false, // autoDelete
false, // exclusive
false, // noWait
nil, // args
)
if err != nil {
return fmt.Errorf("failed to declare queue %s: %w", name, err)
}
return nil
}
// Publish publishes a message to a queue
func (mq *MessageQueue) Publish(queue string, msg Message) error {
msg.Timestamp = time.Now()
body, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("failed to marshal message: %w", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err = mq.channel.PublishWithContext(
ctx,
"", // exchange
queue, // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "application/json",
DeliveryMode: amqp.Persistent,
Body: body,
Timestamp: time.Now(),
},
)
if err != nil {
return fmt.Errorf("failed to publish message: %w", err)
}
return nil
}
// Consume starts consuming messages from a queue
func (mq *MessageQueue) Consume(queue string, handler func(Message) error) error {
msgs, err := mq.channel.Consume(
queue,
"", // consumerTag
false, // autoAck
false, // exclusive
false, // noLocal
false, // noWait
nil, // args
)
if err != nil {
return fmt.Errorf("failed to register consumer: %w", err)
}
for msg := range msgs {
var mqMsg Message
if err := json.Unmarshal(msg.Body, &mqMsg); err != nil {
log.Printf("Failed to unmarshal message: %v", err)
msg.Nack(false, false)
continue
}
if err := handler(mqMsg); err != nil {
log.Printf("Handler error: %v", err)
msg.Nack(false, true) // requeue on error
continue
}
msg.Ack(false)
}
return nil
}
// PublishToExchange publishes a message to an exchange
func (mq *MessageQueue) PublishToExchange(exchange, routingKey string, msg Message) error {
msg.Timestamp = time.Now()
body, err := json.Marshal(msg)
if err != nil {
return fmt.Errorf("failed to marshal message: %w", err)
}
ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()
err = mq.channel.PublishWithContext(
ctx,
exchange, // exchange
routingKey, // routing key
false, // mandatory
false, // immediate
amqp.Publishing{
ContentType: "application/json",
DeliveryMode: amqp.Persistent,
Body: body,
Timestamp: time.Now(),
},
)
if err != nil {
return fmt.Errorf("failed to publish message: %w", err)
}
return nil
}
// DeclareExchange declares an exchange
func (mq *MessageQueue) DeclareExchange(name, kind string) error {
err := mq.channel.ExchangeDeclare(
name,
kind,
true, // durable
false, // autoDelete
false, // internal
false, // noWait
nil, // args
)
if err != nil {
return fmt.Errorf("failed to declare exchange %s: %w", name, err)
}
return nil
}
// BindQueue binds a queue to an exchange
func (mq *MessageQueue) BindQueue(queue, exchange, routingKey string) error {
err := mq.channel.QueueBind(
queue,
routingKey,
exchange,
false, // noWait
nil, // args
)
if err != nil {
return fmt.Errorf("failed to bind queue %s to exchange %s: %w", queue, exchange, err)
}
return nil
}