From ea617025a1e43d31ae8b67eb18b53706ab3cfbe0 Mon Sep 17 00:00:00 2001 From: Festus Date: Mon, 2 Feb 2026 12:09:02 +0100 Subject: [PATCH 1/8] feat(rabbitmq): add Connection struct with connect and Close methods --- adapters/rabbitmq/connection.go | 49 +++++++++++++++++++++++++++++++++ 1 file changed, 49 insertions(+) diff --git a/adapters/rabbitmq/connection.go b/adapters/rabbitmq/connection.go index a4e247e..e3ebabb 100644 --- a/adapters/rabbitmq/connection.go +++ b/adapters/rabbitmq/connection.go @@ -1 +1,50 @@ package rabbitmq + +import ( + "log" + "time" + + "github.com/rabbitmq/amqp091-go" +) + +type Connection struct { + URL string + conn *amqp091.Connection +} + +func NewConnection(url string) (*Connection, error) { + c := &Connection{URL: url} + + err := c.connect() + if err != nil { + return nil, err + } + + return c, nil +} + +func (c *Connection) connect() error { + var err error + + // Retry connection up to 3 times + for range 3 { + c.conn, err = amqp091.Dial(c.URL) + if err == nil { + return nil + } + log.Println("RabbitMQ connection failed, retrying in 1s...") + time.Sleep(1 * time.Second) + } + return err +} + +func (c *Connection) Close() error { + if c.conn != nil { + return c.conn.Close() + } + return nil +} + +func (c *Connection) GetConnection() *amqp091.Connection { + return c.conn +} From 5792f0b0159b60645f3120ebaf60edfb9fa5551a Mon Sep 17 00:00:00 2001 From: Festus Date: Mon, 2 Feb 2026 12:11:38 +0100 Subject: [PATCH 2/8] added go mod file --- go.mod | 5 +++++ go.sum | 4 ++++ 2 files changed, 9 insertions(+) create mode 100644 go.mod create mode 100644 go.sum diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..c417916 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/festech-cloud/microkit + +go 1.24.9 + +require github.com/rabbitmq/amqp091-go v1.10.0 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..024eebe --- /dev/null +++ b/go.sum @@ -0,0 +1,4 @@ +github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= +github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= +go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= From e9d8f3b77bcaf4afdb653521f12b13b4399e6013 Mon Sep 17 00:00:00 2001 From: Festus Date: Mon, 2 Feb 2026 12:28:25 +0100 Subject: [PATCH 3/8] feat(rabbitmq): add Producer implementing messaging.Producer interface --- adapters/rabbitmq/producer.go | 47 +++++++++++++++++++++++++++++++++++ 1 file changed, 47 insertions(+) diff --git a/adapters/rabbitmq/producer.go b/adapters/rabbitmq/producer.go index a4e247e..75c534c 100644 --- a/adapters/rabbitmq/producer.go +++ b/adapters/rabbitmq/producer.go @@ -1 +1,48 @@ package rabbitmq + +import ( + "context" + + "github.com/festech-cloud/microkit/messaging" + "github.com/rabbitmq/amqp091-go" +) + +type Producer struct { + conn *Connection + ch *amqp091.Channel + config messaging.Config +} + +func NewProducer(conn *Connection, cfg messaging.Config) (*Producer, error) { + ch, err := conn.conn.Channel() + if err != nil { + return nil, err + } + + return &Producer{ + conn: conn, + ch: ch, + config: cfg, + }, nil +} + +func (p *Producer) Publish(ctx context.Context, topic string, msg messaging.Message) error { + return p.ch.PublishWithContext( + ctx, + "amq.topic", // exchange + topic, + false, + false, + amqp091.Publishing{ + ContentType: "application/json", + Body: msg.Payload, + }, + ) +} + +func (p *Producer) Close() error { + if p.ch != nil { + return p.ch.Close() + } + return nil +} From 9b43a6b09baf5c7eec816156f453a404d6e943ea Mon Sep 17 00:00:00 2001 From: Festus Date: Mon, 2 Feb 2026 21:11:21 +0100 Subject: [PATCH 4/8] feat(rabbitmq): add minimal RabbitMQ adapter implementing messaging interfaces --- adapters/rabbitmq/consumer.go | 78 +++++++++++++++++++++++++++++++++++ 1 file changed, 78 insertions(+) diff --git a/adapters/rabbitmq/consumer.go b/adapters/rabbitmq/consumer.go index a4e247e..2bce3b4 100644 --- a/adapters/rabbitmq/consumer.go +++ b/adapters/rabbitmq/consumer.go @@ -1 +1,79 @@ package rabbitmq + +import ( + "context" + "log" + + "github.com/festech-cloud/microkit/messaging" + "github.com/rabbitmq/amqp091-go" +) + +type Consumer struct { + conn *Connection + ch *amqp091.Channel + config messaging.Config +} + +func NewConsumer(conn *Connection, cfg messaging.Config) (*Consumer, error) { + ch, err := conn.GetConnection().Channel() + if err != nil { + return nil, err + } + + return &Consumer{ + conn: conn, + ch: ch, + config: cfg, + }, nil +} + +func (c *Consumer) Subscribe(ctx context.Context, topic string, handler messaging.HandlerFunc) error { + queue, err := c.ch.QueueDeclare( + topic, + true, + false, + false, + false, + nil, + ) + + if err != nil { + return err + } + + msgs, err := c.ch.Consume( + queue.Name, + "", + false, + false, + false, + false, + nil, + ) + + if err != nil { + return err + } + + go func() { + for d := range msgs { + msg := messaging.Message{Payload: d.Body, Headers: map[string]string{}} + err := handler(ctx, msg) + if err != nil { + d.Nack(false, true) + log.Println("Message handling failed, message requeued:", err) + } else { + d.Ack(false) + } + } + }() + + return nil +} + +func (c *Consumer) Close() error { + if c.ch != nil { + return c.ch.Close() + } + return nil +} From a1dedf4135e7ddafcc3c85271f4d5e64425a0bb7 Mon Sep 17 00:00:00 2001 From: Festus Date: Mon, 2 Feb 2026 21:13:00 +0100 Subject: [PATCH 5/8] used function call on get connection --- adapters/rabbitmq/producer.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/adapters/rabbitmq/producer.go b/adapters/rabbitmq/producer.go index 75c534c..21205af 100644 --- a/adapters/rabbitmq/producer.go +++ b/adapters/rabbitmq/producer.go @@ -14,7 +14,7 @@ type Producer struct { } func NewProducer(conn *Connection, cfg messaging.Config) (*Producer, error) { - ch, err := conn.conn.Channel() + ch, err := conn.GetConnection().Channel() if err != nil { return nil, err } From 910772b3da9daaba4f83bebbcffddac685ef2e49 Mon Sep 17 00:00:00 2001 From: Festus Date: Mon, 2 Feb 2026 21:22:52 +0100 Subject: [PATCH 6/8] feat(rabbitmq): add dead letter queue (DLQ) support to consumer --- adapters/rabbitmq/consumer.go | 52 +++++++++++++++++++++++++++++++++-- 1 file changed, 49 insertions(+), 3 deletions(-) diff --git a/adapters/rabbitmq/consumer.go b/adapters/rabbitmq/consumer.go index 2bce3b4..190edba 100644 --- a/adapters/rabbitmq/consumer.go +++ b/adapters/rabbitmq/consumer.go @@ -28,13 +28,59 @@ func NewConsumer(conn *Connection, cfg messaging.Config) (*Consumer, error) { } func (c *Consumer) Subscribe(ctx context.Context, topic string, handler messaging.HandlerFunc) error { + // ---- DLQ setup ---- + dlxName := topic + ".dlx" + dlqName := topic + ".dlq" + + // Dead-letter exchange + if err := c.ch.ExchangeDeclare( + dlxName, + "direct", + true, + false, + false, + false, + nil, + ); err != nil { + return err + } + + // Dead-letter queue + if _, err := c.ch.QueueDeclare( + dlqName, + true, + false, + false, + false, + nil, + ); err != nil { + return err + } + + // Bind DLQ to DLX + if err := c.ch.QueueBind( + dlqName, + topic, + dlxName, + false, + nil, + ); err != nil { + return err + } + + // Main queue with DLQ configuration + queueArgs := amqp091.Table{ + "x-dead-letter-exchange": dlxName, + "x-dead-letter-routing-key": topic, + } + queue, err := c.ch.QueueDeclare( topic, true, false, false, false, - nil, + queueArgs, ) if err != nil { @@ -60,8 +106,8 @@ func (c *Consumer) Subscribe(ctx context.Context, topic string, handler messagin msg := messaging.Message{Payload: d.Body, Headers: map[string]string{}} err := handler(ctx, msg) if err != nil { - d.Nack(false, true) - log.Println("Message handling failed, message requeued:", err) + d.Nack(false, false) // goes to DLQ + log.Printf("Message processing failed, sent to DLQ: %v", err) } else { d.Ack(false) } From 3fb203986c79ba9a0b2432ce75f4746187d9f03a Mon Sep 17 00:00:00 2001 From: Festus Date: Mon, 2 Feb 2026 22:07:10 +0100 Subject: [PATCH 7/8] refactor(rabbitmq): modularize consumer with retry and DLQ support --- adapters/rabbitmq/consumer.go | 204 ++++++++++++++++++++++++++-------- 1 file changed, 156 insertions(+), 48 deletions(-) diff --git a/adapters/rabbitmq/consumer.go b/adapters/rabbitmq/consumer.go index 190edba..f3d2252 100644 --- a/adapters/rabbitmq/consumer.go +++ b/adapters/rabbitmq/consumer.go @@ -8,6 +8,11 @@ import ( "github.com/rabbitmq/amqp091-go" ) +const ( + maxRetries = 3 + retryDelay = 5000 // milliseconds +) + type Consumer struct { conn *Connection ch *amqp091.Channel @@ -27,99 +32,202 @@ func NewConsumer(conn *Connection, cfg messaging.Config) (*Consumer, error) { }, nil } -func (c *Consumer) Subscribe(ctx context.Context, topic string, handler messaging.HandlerFunc) error { - // ---- DLQ setup ---- - dlxName := topic + ".dlx" - dlqName := topic + ".dlq" +// Public API + +func (c *Consumer) Subscribe( + ctx context.Context, + topic string, + handler messaging.HandlerFunc, +) error { + dlx, err := c.setupDLX(topic) + if err != nil { + return err + } + + if err := c.setupDLQ(topic, dlx); err != nil { + return err + } + + if err := c.setupRetryQueue(topic); err != nil { + return err + } + + queue, err := c.setupMainQueue(topic, dlx) + if err != nil { + return err + } + + msgs, err := c.consume(queue) + if err != nil { + return err + } + + c.handleMessages(ctx, msgs, topic, handler) + return nil +} + +func (c *Consumer) Close() error { + if c.ch != nil { + return c.ch.Close() + } + return nil +} + +// Queue setup helpers + +func (c *Consumer) setupDLX(topic string) (string, error) { + name := dlxName(topic) - // Dead-letter exchange - if err := c.ch.ExchangeDeclare( - dlxName, + return name, c.ch.ExchangeDeclare( + name, "direct", true, false, false, false, nil, - ); err != nil { - return err - } + ) +} - // Dead-letter queue - if _, err := c.ch.QueueDeclare( - dlqName, +func (c *Consumer) setupDLQ(topic, dlx string) error { + _, err := c.ch.QueueDeclare( + dlqName(topic), true, false, false, false, nil, - ); err != nil { + ) + if err != nil { return err } - // Bind DLQ to DLX - if err := c.ch.QueueBind( - dlqName, + return c.ch.QueueBind( + dlqName(topic), topic, - dlxName, + dlx, false, nil, - ); err != nil { - return err - } + ) +} - // Main queue with DLQ configuration - queueArgs := amqp091.Table{ - "x-dead-letter-exchange": dlxName, - "x-dead-letter-routing-key": topic, - } +func (c *Consumer) setupRetryQueue(topic string) error { + _, err := c.ch.QueueDeclare( + retryName(topic), + true, + false, + false, + false, + amqp091.Table{ + "x-message-ttl": int32(retryDelay), + "x-dead-letter-exchange": "", + "x-dead-letter-routing-key": topic, + }, + ) + return err +} - queue, err := c.ch.QueueDeclare( +func (c *Consumer) setupMainQueue(topic, dlx string) (amqp091.Queue, error) { + return c.ch.QueueDeclare( topic, true, false, false, false, - queueArgs, + amqp091.Table{ + "x-dead-letter-exchange": dlx, + "x-dead-letter-routing-key": topic, + }, ) +} - if err != nil { - return err - } - - msgs, err := c.ch.Consume( +func (c *Consumer) consume(queue amqp091.Queue) (<-chan amqp091.Delivery, error) { + return c.ch.Consume( queue.Name, "", - false, + false, // manual ack false, false, false, nil, ) +} - if err != nil { - return err - } +// Message handling +func (c *Consumer) handleMessages( + ctx context.Context, + msgs <-chan amqp091.Delivery, + topic string, + handler messaging.HandlerFunc, +) { go func() { for d := range msgs { - msg := messaging.Message{Payload: d.Body, Headers: map[string]string{}} - err := handler(ctx, msg) - if err != nil { - d.Nack(false, false) // goes to DLQ - log.Printf("Message processing failed, sent to DLQ: %v", err) + retries := getRetryCount(d.Headers) + + msg := messaging.Message{ + Payload: d.Body, + Headers: map[string]string{}, + } + + if err := handler(ctx, msg); err != nil { + if retries >= maxRetries { + log.Println("Max retries exceeded, sending to DLQ:", err) + d.Nack(false, false) + continue + } + + headers := d.Headers + if headers == nil { + headers = amqp091.Table{} + } + headers["x-retry-count"] = retries + 1 + + _ = c.ch.Publish( + "", + retryName(topic), + false, + false, + amqp091.Publishing{ + Headers: headers, + Body: d.Body, + }, + ) + + d.Ack(false) } else { d.Ack(false) } } }() - - return nil } -func (c *Consumer) Close() error { - if c.ch != nil { - return c.ch.Close() +// Helpers + +func getRetryCount(headers amqp091.Table) int { + if headers == nil { + return 0 } - return nil + + if val, ok := headers["x-retry-count"]; ok { + switch v := val.(type) { + case int32: + return int(v) + case int: + return v + } + } + return 0 +} + +func dlxName(topic string) string { + return topic + ".dlx" +} + +func dlqName(topic string) string { + return topic + ".dlq" +} + +func retryName(topic string) string { + return topic + ".retry" } From ac9186d5cc1bef86c99dde73606ee14349acf426 Mon Sep 17 00:00:00 2001 From: Festus Date: Mon, 2 Feb 2026 23:07:48 +0100 Subject: [PATCH 8/8] Add RabbitMQ integration tests and example usage for producer/consumer --- adapters/rabbitmq/consumer.go | 21 ++- adapters/rabbitmq/rabbitmq_test.go | 211 +++++++++++++++++++++++++++++ examples/rabbitmq/consumer/main.go | 43 ++++++ examples/rabbitmq/producer/main.go | 43 ++++++ go.mod | 63 ++++++++- go.sum | 164 ++++++++++++++++++++++ 6 files changed, 542 insertions(+), 3 deletions(-) create mode 100644 adapters/rabbitmq/rabbitmq_test.go create mode 100644 examples/rabbitmq/consumer/main.go create mode 100644 examples/rabbitmq/producer/main.go diff --git a/adapters/rabbitmq/consumer.go b/adapters/rabbitmq/consumer.go index f3d2252..2c5603c 100644 --- a/adapters/rabbitmq/consumer.go +++ b/adapters/rabbitmq/consumer.go @@ -8,7 +8,7 @@ import ( "github.com/rabbitmq/amqp091-go" ) -const ( +var ( maxRetries = 3 retryDelay = 5000 // milliseconds ) @@ -128,7 +128,7 @@ func (c *Consumer) setupRetryQueue(topic string) error { } func (c *Consumer) setupMainQueue(topic, dlx string) (amqp091.Queue, error) { - return c.ch.QueueDeclare( + queue, err := c.ch.QueueDeclare( topic, true, false, @@ -139,6 +139,23 @@ func (c *Consumer) setupMainQueue(topic, dlx string) (amqp091.Queue, error) { "x-dead-letter-routing-key": topic, }, ) + + if err != nil { + return amqp091.Queue{}, err + } + + err = c.ch.QueueBind( + queue.Name, + topic, + "amq.topic", + false, + nil, + ) + if err != nil { + return amqp091.Queue{}, err + } + + return queue, nil } func (c *Consumer) consume(queue amqp091.Queue) (<-chan amqp091.Delivery, error) { diff --git a/adapters/rabbitmq/rabbitmq_test.go b/adapters/rabbitmq/rabbitmq_test.go new file mode 100644 index 0000000..7ae6bbc --- /dev/null +++ b/adapters/rabbitmq/rabbitmq_test.go @@ -0,0 +1,211 @@ +package rabbitmq + +import ( + "context" + "fmt" + "log" + "testing" + "time" + + "github.com/festech-cloud/microkit/messaging" + "github.com/testcontainers/testcontainers-go" + "github.com/testcontainers/testcontainers-go/wait" +) + +// ------------------------- +// Globals for tests +// ------------------------- +var testConn *Connection +var ctx = context.Background() + +// ------------------------- +// Start RabbitMQ container +// ------------------------- +func setupRabbitMQ(t *testing.T) string { + ctx, cancel := context.WithTimeout(context.Background(), 3*time.Minute) + defer cancel() + + req := testcontainers.ContainerRequest{ + Image: "rabbitmq:3.11-alpine", // lighter image for tests + ExposedPorts: []string{"5672/tcp"}, + WaitingFor: wait.ForListeningPort("5672/tcp").WithStartupTimeout(2 * time.Minute), + } + + rmqC, err := testcontainers.GenericContainer(ctx, testcontainers.GenericContainerRequest{ + ContainerRequest: req, + Started: true, + }) + if err != nil { + t.Fatalf("Failed to start RabbitMQ container: %v", err) + } + + // Ensure container cleanup after test + t.Cleanup(func() { + _ = rmqC.Terminate(ctx) + }) + + port, err := rmqC.MappedPort(ctx, "5672") + if err != nil { + t.Fatalf("Failed to get mapped port: %v", err) + } + + url := fmt.Sprintf("amqp://guest:guest@localhost:%s/", port.Port()) + return url +} + +// ------------------------- +// Helper to create connection with retry +// ------------------------- +func initConnection(t *testing.T) *Connection { + url := setupRabbitMQ(t) + + var conn *Connection + var err error + for i := 0; i < 5; i++ { + conn, err = NewConnection(url) + if err == nil { + break + } + log.Println("Retrying connection to RabbitMQ...") + time.Sleep(2 * time.Second) + } + if err != nil { + t.Fatalf("Failed to connect to RabbitMQ after retries: %v", err) + } + return conn +} + +// ------------------------- +// Test: Connection +// ------------------------- +func TestConnection(t *testing.T) { + conn := initConnection(t) + defer conn.Close() + + if conn.conn.IsClosed() { + t.Fatal("Connection should be open") + } + t.Log("Connection test passed") +} + +// ------------------------- +// Test: Producer + Consumer +// ------------------------- +func TestProducerConsumer(t *testing.T) { + conn := initConnection(t) + defer conn.Close() + + topic := "test-topic" + + // Create Producer + prodCfg := messaging.Config{} + producer, err := NewProducer(conn, prodCfg) + if err != nil { + t.Fatalf("Failed to create producer: %v", err) + } + defer producer.Close() + + // Create Consumer + consCfg := messaging.Config{} + consumer, err := NewConsumer(conn, consCfg) + if err != nil { + t.Fatalf("Failed to create consumer: %v", err) + } + defer consumer.Close() + + // Channel to receive message + received := make(chan []byte, 1) + + // Start consumer + err = consumer.Subscribe(ctx, topic, func(ctx context.Context, msg messaging.Message) error { + received <- msg.Payload + return nil + }) + if err != nil { + t.Fatalf("Failed to subscribe consumer: %v", err) + } + + // Publish a message + message := messaging.Message{Payload: []byte("hello world")} + err = producer.Publish(ctx, topic, message) + if err != nil { + t.Fatalf("Failed to publish message: %v", err) + } + + // Wait for message to be received + select { + case msg := <-received: + if string(msg) != "hello world" { + t.Fatalf("Expected 'hello world', got '%s'", string(msg)) + } + case <-time.After(5 * time.Second): + t.Fatal("Timeout waiting for message") + } + + t.Log("Producer + Consumer test passed") +} + +// ------------------------- +// Test: Retry / DLQ simulation +// ------------------------- +func TestRetryDLQ(t *testing.T) { + conn := initConnection(t) + defer conn.Close() + + topic := "retry-topic" + + prodCfg := messaging.Config{} + producer, err := NewProducer(conn, prodCfg) + if err != nil { + t.Fatalf("Failed to create producer: %v", err) + } + defer producer.Close() + + consCfg := messaging.Config{} + consumer, err := NewConsumer(conn, consCfg) + if err != nil { + t.Fatalf("Failed to create consumer: %v", err) + } + defer consumer.Close() + + // Reduce retry delay for testing + retryDelay = 500 // 500ms instead of 5000ms + + // Channel to track retries + retryCount := 0 + maxRetries := 2 + + err = consumer.Subscribe(ctx, topic, func(ctx context.Context, msg messaging.Message) error { + retryCount++ + if retryCount <= maxRetries { + return fmt.Errorf("simulate failure %d", retryCount) + } + return nil + }) + if err != nil { + t.Fatalf("Failed to subscribe consumer: %v", err) + } + + // Publish a message + message := messaging.Message{Payload: []byte("retry test")} + err = producer.Publish(ctx, topic, message) + if err != nil { + t.Fatalf("Failed to publish message: %v", err) + } + + // Poll until retryCount reaches expected value or timeout + timeout := time.After(5 * time.Second) + tick := time.Tick(100 * time.Millisecond) + + for { + select { + case <-timeout: + t.Fatalf("Expected %d retries, got %d", maxRetries, retryCount) + case <-tick: + if retryCount >= maxRetries+1 { + t.Logf("Retry/DLQ simulation passed with %d attempts", retryCount) + return + } + } + } +} diff --git a/examples/rabbitmq/consumer/main.go b/examples/rabbitmq/consumer/main.go new file mode 100644 index 0000000..8e6368b --- /dev/null +++ b/examples/rabbitmq/consumer/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "context" + "fmt" + "log" + + "github.com/festech-cloud/microkit/adapters/rabbitmq" + "github.com/festech-cloud/microkit/messaging" +) + +func main() { + ctx := context.Background() + topic := "example.topic" + + // 1. Connect to RabbitMQ + conn, err := rabbitmq.NewConnection("amqp://guest:guest@localhost:5672/") + if err != nil { + log.Fatalf("Failed to connect to RabbitMQ: %v", err) + } + defer conn.Close() + + // 2. Create a consumer + consCfg := messaging.Config{} + consumer, err := rabbitmq.NewConsumer(conn, consCfg) + if err != nil { + log.Fatalf("Failed to create consumer: %v", err) + } + defer consumer.Close() + + // 3. Subscribe and handle messages + err = consumer.Subscribe(ctx, topic, func(ctx context.Context, msg messaging.Message) error { + fmt.Println("Received message:", string(msg.Payload)) + return nil + }) + if err != nil { + log.Fatalf("Failed to subscribe: %v", err) + } + + // Keep consumer running + fmt.Println("Consumer is listening. Press Ctrl+C to exit...") + select {} +} diff --git a/examples/rabbitmq/producer/main.go b/examples/rabbitmq/producer/main.go new file mode 100644 index 0000000..3ed8232 --- /dev/null +++ b/examples/rabbitmq/producer/main.go @@ -0,0 +1,43 @@ +package main + +import ( + "context" + "fmt" + "log" + + "github.com/festech-cloud/microkit/adapters/rabbitmq" + "github.com/festech-cloud/microkit/messaging" +) + +func main() { + ctx := context.Background() + topic := "example.topic" + + // 1. Connect to RabbitMQ + conn, err := rabbitmq.NewConnection("amqp://guest:guest@localhost:5672/") + if err != nil { + log.Fatalf("Failed to connect to RabbitMQ: %v", err) + } + defer conn.Close() + + // 2. Create a producer + prodCfg := messaging.Config{} + producer, err := rabbitmq.NewProducer(conn, prodCfg) + if err != nil { + log.Fatalf("Failed to create producer: %v", err) + } + defer producer.Close() + + // 3. Publish some messages + for i := 1; i <= 3; i++ { + payload := fmt.Sprintf("Hello World %d", i) + msg := messaging.Message{Payload: []byte(payload)} + if err := producer.Publish(ctx, topic, msg); err != nil { + log.Printf("Failed to publish message: %v", err) + } else { + fmt.Println("Published message:", payload) + } + } + + fmt.Println("Producer finished publishing messages") +} diff --git a/go.mod b/go.mod index c417916..682f41a 100644 --- a/go.mod +++ b/go.mod @@ -2,4 +2,65 @@ module github.com/festech-cloud/microkit go 1.24.9 -require github.com/rabbitmq/amqp091-go v1.10.0 +require ( + github.com/rabbitmq/amqp091-go v1.10.0 + github.com/testcontainers/testcontainers-go v0.40.0 +) + +require ( + dario.cat/mergo v1.0.2 // indirect + github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 // indirect + github.com/Microsoft/go-winio v0.6.2 // indirect + github.com/cenkalti/backoff/v4 v4.3.0 // indirect + github.com/cespare/xxhash/v2 v2.3.0 // indirect + github.com/containerd/errdefs v1.0.0 // indirect + github.com/containerd/errdefs/pkg v0.3.0 // indirect + github.com/containerd/log v0.1.0 // indirect + github.com/containerd/platforms v0.2.1 // indirect + github.com/cpuguy83/dockercfg v0.3.2 // indirect + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/distribution/reference v0.6.0 // indirect + github.com/docker/docker v28.5.1+incompatible // indirect + github.com/docker/go-connections v0.6.0 // indirect + github.com/docker/go-units v0.5.0 // indirect + github.com/ebitengine/purego v0.8.4 // indirect + github.com/felixge/httpsnoop v1.0.4 // indirect + github.com/go-logr/logr v1.4.3 // indirect + github.com/go-logr/stdr v1.2.2 // indirect + github.com/go-ole/go-ole v1.2.6 // indirect + github.com/google/uuid v1.6.0 // indirect + github.com/klauspost/compress v1.18.0 // indirect + github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect + github.com/magiconair/properties v1.8.10 // indirect + github.com/moby/docker-image-spec v1.3.1 // indirect + github.com/moby/go-archive v0.1.0 // indirect + github.com/moby/patternmatcher v0.6.0 // indirect + github.com/moby/sys/sequential v0.6.0 // indirect + github.com/moby/sys/user v0.4.0 // indirect + github.com/moby/sys/userns v0.1.0 // indirect + github.com/moby/term v0.5.0 // indirect + github.com/morikuni/aec v1.0.0 // indirect + github.com/opencontainers/go-digest v1.0.0 // indirect + github.com/opencontainers/image-spec v1.1.1 // indirect + github.com/pkg/errors v0.9.1 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect + github.com/shirou/gopsutil/v4 v4.25.6 // indirect + github.com/sirupsen/logrus v1.9.3 // indirect + github.com/stretchr/testify v1.11.1 // indirect + github.com/tklauser/go-sysconf v0.3.12 // indirect + github.com/tklauser/numcpus v0.6.1 // indirect + github.com/yusufpapurcu/wmi v1.2.4 // indirect + go.opentelemetry.io/auto/sdk v1.2.1 // indirect + go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 // indirect + go.opentelemetry.io/otel v1.40.0 // indirect + go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 // indirect + go.opentelemetry.io/otel/metric v1.40.0 // indirect + go.opentelemetry.io/otel/sdk v1.40.0 // indirect + go.opentelemetry.io/otel/trace v1.40.0 // indirect + go.opentelemetry.io/proto/otlp v1.9.0 // indirect + golang.org/x/crypto v0.43.0 // indirect + golang.org/x/sys v0.40.0 // indirect + google.golang.org/protobuf v1.36.11 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect +) diff --git a/go.sum b/go.sum index 024eebe..f0fb13b 100644 --- a/go.sum +++ b/go.sum @@ -1,4 +1,168 @@ +dario.cat/mergo v1.0.2 h1:85+piFYR1tMbRrLcDwR18y4UKJ3aH1Tbzi24VRW1TK8= +dario.cat/mergo v1.0.2/go.mod h1:E/hbnu0NxMFBjpMIE34DRGLWqDy0g5FuKDhCb31ngxA= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6 h1:He8afgbRMd7mFxO99hRNu+6tazq8nFF9lIwo9JFroBk= +github.com/AdaLogics/go-fuzz-headers v0.0.0-20240806141605-e8a1dd7889d6/go.mod h1:8o94RPi1/7XTJvwPpRSzSUedZrtlirdB3r9Z20bi2f8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1 h1:UQHMgLO+TxOElx5B5HZ4hJQsoJ/PvUvKRhJHDQXO8P8= +github.com/Azure/go-ansiterm v0.0.0-20210617225240-d185dfc1b5a1/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E= +github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY= +github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU= +github.com/cenkalti/backoff/v4 v4.3.0 h1:MyRJ/UdXutAwSAT+s3wNd7MfTIcy71VQueUuFK343L8= +github.com/cenkalti/backoff/v4 v4.3.0/go.mod h1:Y3VNntkOUPxTVeUxJ/G5vcM//AlwfmyYozVcomhLiZE= +github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= +github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= +github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI= +github.com/containerd/errdefs v1.0.0/go.mod h1:+YBYIdtsnF4Iw6nWZhJcqGSg/dwvV7tyJ/kCkyJ2k+M= +github.com/containerd/errdefs/pkg v0.3.0 h1:9IKJ06FvyNlexW690DXuQNx2KA2cUJXx151Xdx3ZPPE= +github.com/containerd/errdefs/pkg v0.3.0/go.mod h1:NJw6s9HwNuRhnjJhM7pylWwMyAkmCQvQ4GpJHEqRLVk= +github.com/containerd/log v0.1.0 h1:TCJt7ioM2cr/tfR8GPbGf9/VRAX8D2B4PjzCpfX540I= +github.com/containerd/log v0.1.0/go.mod h1:VRRf09a7mHDIRezVKTRCrOq78v577GXq3bSa3EhrzVo= +github.com/containerd/platforms v0.2.1 h1:zvwtM3rz2YHPQsF2CHYM8+KtB5dvhISiXh5ZpSBQv6A= +github.com/containerd/platforms v0.2.1/go.mod h1:XHCb+2/hzowdiut9rkudds9bE5yJ7npe7dG/wG+uFPw= +github.com/cpuguy83/dockercfg v0.3.2 h1:DlJTyZGBDlXqUZ2Dk2Q3xHs/FtnooJJVaad2S9GKorA= +github.com/cpuguy83/dockercfg v0.3.2/go.mod h1:sugsbF4//dDlL/i+S+rtpIWp+5h0BHJHfjj5/jFyUJc= +github.com/creack/pty v1.1.18 h1:n56/Zwd5o6whRC5PMGretI4IdRLlmBXYNjScPaBgsbY= +github.com/creack/pty v1.1.18/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= +github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/distribution/reference v0.6.0 h1:0IXCQ5g4/QMHHkarYzh5l+u8T3t73zM5QvfrDyIgxBk= +github.com/distribution/reference v0.6.0/go.mod h1:BbU0aIcezP1/5jX/8MP0YiH4SdvB5Y4f/wlDRiLyi3E= +github.com/docker/docker v28.5.1+incompatible h1:Bm8DchhSD2J6PsFzxC35TZo4TLGR2PdW/E69rU45NhM= +github.com/docker/docker v28.5.1+incompatible/go.mod h1:eEKB0N0r5NX/I1kEveEz05bcu8tLC/8azJZsviup8Sk= +github.com/docker/go-connections v0.6.0 h1:LlMG9azAe1TqfR7sO+NJttz1gy6KO7VJBh+pMmjSD94= +github.com/docker/go-connections v0.6.0/go.mod h1:AahvXYshr6JgfUJGdDCs2b5EZG/vmaMAntpSFH5BFKE= +github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4= +github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= +github.com/ebitengine/purego v0.8.4 h1:CF7LEKg5FFOsASUj0+QwaXf8Ht6TlFxg09+S9wz0omw= +github.com/ebitengine/purego v0.8.4/go.mod h1:iIjxzd6CiRiOG0UyXP+V1+jWqUXVjPKLAI0mRfJZTmQ= +github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= +github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= +github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= +github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= +github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= +github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= +github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= +github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= +github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= +github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= +github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= +github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2 h1:8Tjv8EJ+pM1xP8mK6egEbD1OgnVTyacbefKhmbLhIhU= +github.com/grpc-ecosystem/grpc-gateway/v2 v2.27.2/go.mod h1:pkJQ2tZHJ0aFOVEEot6oZmaVEZcRme73eIFmhiVuRWs= +github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo= +github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ= +github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= +github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= +github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= +github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= +github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0/go.mod h1:zJYVVT2jmtg6P3p1VtQj7WsuWi/y4VnjVBn7F8KPB3I= +github.com/magiconair/properties v1.8.10 h1:s31yESBquKXCV9a/ScB3ESkOjUYYv+X0rg8SYxI99mE= +github.com/magiconair/properties v1.8.10/go.mod h1:Dhd985XPs7jluiymwWYZ0G4Z61jb3vdS329zhj2hYo0= +github.com/moby/docker-image-spec v1.3.1 h1:jMKff3w6PgbfSa69GfNg+zN/XLhfXJGnEx3Nl2EsFP0= +github.com/moby/docker-image-spec v1.3.1/go.mod h1:eKmb5VW8vQEh/BAr2yvVNvuiJuY6UIocYsFu/DxxRpo= +github.com/moby/go-archive v0.1.0 h1:Kk/5rdW/g+H8NHdJW2gsXyZ7UnzvJNOy6VKJqueWdcQ= +github.com/moby/go-archive v0.1.0/go.mod h1:G9B+YoujNohJmrIYFBpSd54GTUB4lt9S+xVQvsJyFuo= +github.com/moby/patternmatcher v0.6.0 h1:GmP9lR19aU5GqSSFko+5pRqHi+Ohk1O69aFiKkVGiPk= +github.com/moby/patternmatcher v0.6.0/go.mod h1:hDPoyOpDY7OrrMDLaYoY3hf52gNCR/YOUYxkhApJIxc= +github.com/moby/sys/atomicwriter v0.1.0 h1:kw5D/EqkBwsBFi0ss9v1VG3wIkVhzGvLklJ+w3A14Sw= +github.com/moby/sys/atomicwriter v0.1.0/go.mod h1:Ul8oqv2ZMNHOceF643P6FKPXeCmYtlQMvpizfsSoaWs= +github.com/moby/sys/sequential v0.6.0 h1:qrx7XFUd/5DxtqcoH1h438hF5TmOvzC/lspjy7zgvCU= +github.com/moby/sys/sequential v0.6.0/go.mod h1:uyv8EUTrca5PnDsdMGXhZe6CCe8U/UiTWd+lL+7b/Ko= +github.com/moby/sys/user v0.4.0 h1:jhcMKit7SA80hivmFJcbB1vqmw//wU61Zdui2eQXuMs= +github.com/moby/sys/user v0.4.0/go.mod h1:bG+tYYYJgaMtRKgEmuueC0hJEAZWwtIbZTB+85uoHjs= +github.com/moby/sys/userns v0.1.0 h1:tVLXkFOxVu9A64/yh59slHVv9ahO9UIev4JZusOLG/g= +github.com/moby/sys/userns v0.1.0/go.mod h1:IHUYgu/kao6N8YZlp9Cf444ySSvCmDlmzUcYfDHOl28= +github.com/moby/term v0.5.0 h1:xt8Q1nalod/v7BqbG21f8mQPqH+xAaC9C3N3wfWbVP0= +github.com/moby/term v0.5.0/go.mod h1:8FzsFHVUBGZdbDsJw/ot+X+d5HLUbvklYLJ9uGfcI3Y= +github.com/morikuni/aec v1.0.0 h1:nP9CBfwrvYnBRgY6qfDQkygYDmYwOilePFkwzv4dU8A= +github.com/morikuni/aec v1.0.0/go.mod h1:BbKIizmSmc5MMPqRYbxO4ZU0S0+P200+tUnFx7PXmsc= +github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8Oi/yOhh5U= +github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= +github.com/opencontainers/image-spec v1.1.1 h1:y0fUlFfIZhPF1W537XOLg0/fcx6zcHCJwooC2xJA040= +github.com/opencontainers/image-spec v1.1.1/go.mod h1:qpqAh3Dmcf36wStyyWU+kCeDgrGnAve2nCC8+7h8Q0M= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= +github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= +github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c h1:ncq/mPwQF4JjgDlrVEn3C11VoGHZN7m8qihwgMEtzYw= +github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c/go.mod h1:OmDBASR4679mdNQnz2pUhc2G8CO2JrUAVFDRBDP/hJE= github.com/rabbitmq/amqp091-go v1.10.0 h1:STpn5XsHlHGcecLmMFCtg7mqq0RnD+zFr4uzukfVhBw= github.com/rabbitmq/amqp091-go v1.10.0/go.mod h1:Hy4jKW5kQART1u+JkDTF9YYOQUHXqMuhrgxOEeS7G4o= +github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ= +github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc= +github.com/shirou/gopsutil/v4 v4.25.6 h1:kLysI2JsKorfaFPcYmcJqbzROzsBWEOAtw6A7dIfqXs= +github.com/shirou/gopsutil/v4 v4.25.6/go.mod h1:PfybzyydfZcN+JMMjkF6Zb8Mq1A/VcogFFg7hj50W9c= +github.com/sirupsen/logrus v1.9.3 h1:dueUQJ1C2q9oE3F7wvmSGAaVtTmUizReu6fjN8uqzbQ= +github.com/sirupsen/logrus v1.9.3/go.mod h1:naHLuLoDiP4jHNo9R0sCBMtWGeIprob74mVsIT4qYEQ= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= +github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/testcontainers/testcontainers-go v0.40.0 h1:pSdJYLOVgLE8YdUY2FHQ1Fxu+aMnb6JfVz1mxk7OeMU= +github.com/testcontainers/testcontainers-go v0.40.0/go.mod h1:FSXV5KQtX2HAMlm7U3APNyLkkap35zNLxukw9oBi/MY= +github.com/tklauser/go-sysconf v0.3.12 h1:0QaGUFOdQaIVdPgfITYzaTegZvdCjmYO52cSFAEVmqU= +github.com/tklauser/go-sysconf v0.3.12/go.mod h1:Ho14jnntGE1fpdOqQEEaiKRpvIavV0hSfmBq8nJbHYI= +github.com/tklauser/numcpus v0.6.1 h1:ng9scYS7az0Bk4OZLvrNXNSAO2Pxr1XXRAPyjhIx+Fk= +github.com/tklauser/numcpus v0.6.1/go.mod h1:1XfjsgE2zo8GVw7POkMbHENHzVg3GzmoZ9fESEdAacY= +github.com/yusufpapurcu/wmi v1.2.4 h1:zFUKzehAFReQwLys1b/iSMl+JQGSCSjtVqQn9bBrPo0= +github.com/yusufpapurcu/wmi v1.2.4/go.mod h1:SBZ9tNy3G9/m5Oi98Zks0QjeHVDvuK0qfxQmPyzfmi0= +go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= +go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0 h1:jq9TW8u3so/bN+JPT166wjOI6/vQPF6Xe7nMNIltagk= +go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.49.0/go.mod h1:p8pYQP+m5XfbZm9fxtSKAbM6oIllS7s2AfxrChvc7iw= +go.opentelemetry.io/otel v1.40.0 h1:oA5YeOcpRTXq6NN7frwmwFR0Cn3RhTVZvXsP4duvCms= +go.opentelemetry.io/otel v1.40.0/go.mod h1:IMb+uXZUKkMXdPddhwAHm6UfOwJyh4ct1ybIlV14J0g= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0 h1:QKdN8ly8zEMrByybbQgv8cWBcdAarwmIPZ6FThrWXJs= +go.opentelemetry.io/otel/exporters/otlp/otlptrace v1.40.0/go.mod h1:bTdK1nhqF76qiPoCCdyFIV+N/sRHYXYCTQc+3VCi3MI= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0 h1:IeMeyr1aBvBiPVYihXIaeIZba6b8E1bYp7lbdxK8CQg= +go.opentelemetry.io/otel/exporters/otlp/otlptrace/otlptracehttp v1.19.0/go.mod h1:oVdCUtjq9MK9BlS7TtucsQwUcXcymNiEDjgDD2jMtZU= +go.opentelemetry.io/otel/metric v1.40.0 h1:rcZe317KPftE2rstWIBitCdVp89A2HqjkxR3c11+p9g= +go.opentelemetry.io/otel/metric v1.40.0/go.mod h1:ib/crwQH7N3r5kfiBZQbwrTge743UDc7DTFVZrrXnqc= +go.opentelemetry.io/otel/sdk v1.40.0 h1:KHW/jUzgo6wsPh9At46+h4upjtccTmuZCFAc9OJ71f8= +go.opentelemetry.io/otel/sdk v1.40.0/go.mod h1:Ph7EFdYvxq72Y8Li9q8KebuYUr2KoeyHx0DRMKrYBUE= +go.opentelemetry.io/otel/trace v1.40.0 h1:WA4etStDttCSYuhwvEa8OP8I5EWu24lkOzp+ZYblVjw= +go.opentelemetry.io/otel/trace v1.40.0/go.mod h1:zeAhriXecNGP/s2SEG3+Y8X9ujcJOTqQ5RgdEJcawiA= +go.opentelemetry.io/proto/otlp v1.9.0 h1:l706jCMITVouPOqEnii2fIAuO3IVGBRPV5ICjceRb/A= +go.opentelemetry.io/proto/otlp v1.9.0/go.mod h1:xE+Cx5E/eEHw+ISFkwPLwCZefwVjY+pqKg1qcK03+/4= go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= +golang.org/x/net v0.45.0 h1:RLBg5JKixCy82FtLJpeNlVM0nrSqpCRYzVU1n8kj0tM= +golang.org/x/net v0.45.0/go.mod h1:ECOoLqd5U3Lhyeyo/QDCEVQ4sNgYsqvCZ722XogGieY= +golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220715151400-c0bba94af5f8/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.11.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= +golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= +golang.org/x/term v0.36.0 h1:zMPR+aF8gfksFprF/Nc/rd1wRS1EI6nDBGyWAvDzx2Q= +golang.org/x/term v0.36.0/go.mod h1:Qu394IJq6V6dCBRgwqshf3mPF85AqzYEzofzRdZkWss= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8 h1:vVKdlvoWBphwdxWKrFZEuM0kGgGLxUOYcY4U/2Vjg44= +golang.org/x/time v0.0.0-20220210224613-90d013bbcef8/go.mod h1:tRJNPiyCQ0inRvYxbN9jk5I+vvW/OXSQhTDSoE431IQ= +golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5 h1:BIRfGDEjiHRrk0QKZe3Xv2ieMhtgRGeLcZQ0mIVn4EY= +google.golang.org/genproto/googleapis/api v0.0.0-20250825161204-c5933d9347a5/go.mod h1:j3QtIyytwqGr1JUDtYXwtMXWPKsEa5LtzIFN1Wn5WvE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5 h1:eaY8u2EuxbRv7c3NiGK0/NedzVsCcV6hDuU5qPX5EGE= +google.golang.org/genproto/googleapis/rpc v0.0.0-20250825161204-c5933d9347a5/go.mod h1:M4/wBTSeyLxupu3W3tJtOgB14jILAS/XWPSSa3TAlJc= +google.golang.org/grpc v1.75.1 h1:/ODCNEuf9VghjgO3rqLcfg8fiOP0nSluljWFlDxELLI= +google.golang.org/grpc v1.75.1/go.mod h1:JtPAzKiq4v1xcAB2hydNlWI2RnF85XXcV0mhKXr2ecQ= +google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE= +google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk= +gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= +gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +gotest.tools/v3 v3.5.2 h1:7koQfIKdy+I8UTetycgUqXWSDwpgv193Ka+qRsmBY8Q= +gotest.tools/v3 v3.5.2/go.mod h1:LtdLGcnqToBH83WByAAi/wiwSFCArdFIUV/xxN4pcjA=