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() }