pg_v_man/infrastructure/rabbitmq/publisher.go
2025-02-14 08:44:34 +01:00

89 lines
2.5 KiB
Go

package rabbitmq
import (
"context"
"encoding/json"
"errors"
"fmt"
"net"
"strconv"
"strings"
"github.com/wagslane/go-rabbitmq"
"code.icb4dc0.de/prskr/pg_v_man/core/domain"
"code.icb4dc0.de/prskr/pg_v_man/core/ports"
"code.icb4dc0.de/prskr/pg_v_man/infrastructure/config"
)
var _ ports.ReplicationEventConsumer = (*PublishingEventConsumer)(nil)
func NewPublishingEventConsumer(ctx context.Context, cfg config.RabbitMQ) (consumer *PublishingEventConsumer, err error) {
var dialer net.Dialer
conn, err := rabbitmq.NewConn(cfg.ConnectionString, rabbitmq.WithConnectionOptionsLogging, func(options *rabbitmq.ConnectionOptions) {
options.Config.Dial = func(network, addr string) (net.Conn, error) {
return dialer.DialContext(ctx, network, addr)
}
})
if err != nil {
return nil, fmt.Errorf("could not create RabbitMQ connection: %w", err)
}
defer func() {
if err != nil {
err = errors.Join(err, conn.Close())
}
}()
publisherOptions := []func(*rabbitmq.PublisherOptions){
rabbitmq.WithPublisherOptionsLogging,
rabbitmq.WithPublisherOptionsExchangeName(cfg.Exchange.Name),
rabbitmq.WithPublisherOptionsExchangeKind(cfg.Exchange.Kind),
rabbitmq.WithPublisherOptionsExchangeDeclare,
}
if cfg.Exchange.Durable {
publisherOptions = append(publisherOptions, rabbitmq.WithPublisherOptionsExchangeDurable)
}
publisher, err := rabbitmq.NewPublisher(
conn,
publisherOptions...,
)
if err != nil {
return nil, fmt.Errorf("could not create RabbitMQ publisher: %w", err)
}
return &PublishingEventConsumer{Conn: conn, Publisher: publisher, Cfg: cfg}, nil
}
type PublishingEventConsumer struct {
Conn *rabbitmq.Conn
Publisher *rabbitmq.Publisher
Cfg config.RabbitMQ
}
func (p PublishingEventConsumer) OnDataChange(ctx context.Context, ev domain.ReplicationEvent) error {
data, err := json.Marshal(ev)
if err != nil {
return fmt.Errorf("could not marshal event: %w", err)
}
return p.Publisher.PublishWithContext(
ctx, data, []string{p.Cfg.RoutingKey, strings.Join([]string{ev.DBName, ev.Namespace, ev.Relation}, ".")},
rabbitmq.WithPublishOptionsContentType("application/json"),
rabbitmq.WithPublishOptionsExchange(p.Cfg.Exchange.Name),
rabbitmq.WithPublishOptionsCorrelationID(strconv.Itoa(int(ev.TransactionId))),
rabbitmq.WithPublishOptionsType(ev.EventType.String()),
rabbitmq.WithPublishOptionsHeaders(rabbitmq.Table{
"db": ev.DBName,
"namespace": ev.Namespace,
"relation": ev.Relation,
}),
)
}
func (p PublishingEventConsumer) Close() error {
p.Publisher.Close()
return p.Conn.Close()
}