Implement notifications service with shared messaging and email drivers (#36)

This commit is contained in:
Stephan D committed 2026-09-12 14:41:55 +02:00
1 parent 36277b6c45
commit 5c2d36281b
54 files changed
+3746 -6

No files matched your search

@@ -0,0 +1,114 @@
package natscap
import (
"context"
"strings"
"git.sendico.io/tech/sxchnge/shared/messaging/capability"
basenats "git.sendico.io/tech/sxchnge/shared/messaging/internal/natsb"
"git.sendico.io/tech/sxchnge/shared/messaging/reliable"
"github.com/nats-io/nats.go"
)
type DurableAdapter struct {
broker *basenats.NatsBroker
}
type durableAdminContext interface {
AddStream(cfg *nats.StreamConfig, opts ...nats.JSOpt) (*nats.StreamInfo, error)
UpdateStream(cfg *nats.StreamConfig, opts ...nats.JSOpt) (*nats.StreamInfo, error)
StreamInfo(stream string, opts ...nats.JSOpt) (*nats.StreamInfo, error)
AddConsumer(stream string, cfg *nats.ConsumerConfig, opts ...nats.JSOpt) (*nats.ConsumerInfo, error)
UpdateConsumer(stream string, cfg *nats.ConsumerConfig, opts ...nats.JSOpt) (*nats.ConsumerInfo, error)
ConsumerInfo(stream, consumer string, opts ...nats.JSOpt) (*nats.ConsumerInfo, error)
}
func NewDurableAdapter(broker *basenats.NatsBroker) *DurableAdapter {
if broker == nil || broker.JetStream() == nil {
return nil
}
return &DurableAdapter{broker: broker}
}
func (a *DurableAdapter) Publish(ctx context.Context, record reliable.Record) error {
if err := reliable.ValidatePublicRecord(record); err != nil {
return err
}
normalized, err := reliable.NormalizePublicRecord(record)
if err != nil {
return err
}
return a.publishRecord(ctx, normalized)
}
func (a *DurableAdapter) PublishRuntime(ctx context.Context, record reliable.Record) error {
normalized, err := reliable.NormalizeRuntimeRecord(record)
if err != nil {
return err
}
return a.publishRecord(ctx, normalized)
}
func (a *DurableAdapter) publishRecord(ctx context.Context, record reliable.Record) error {
js := a.jetStream()
if js == nil {
return capability.Unavailable("durable transport")
}
msg := &nats.Msg{
Subject: strings.TrimSpace(record.Subject),
Data: append([]byte(nil), record.Payload...),
Header: nats.Header{},
}
for key, values := range record.Headers {
for _, value := range values {
msg.Header.Add(key, value)
}
}
if record.MessageID != "" {
msg.Header.Set(nats.MsgIdHdr, record.MessageID)
}
_, err := js.PublishMsg(msg, nats.Context(ctx))
return err
}
func (a *DurableAdapter) OpenConsumer(ctx context.Context, settings reliable.DurableSettings, consumer reliable.DurableConsumerSettings) (reliable.Subscription, error) {
js := a.jetStream()
if js == nil {
return nil, capability.Unavailable("durable transport")
}
sub, err := js.PullSubscribe(
consumer.Subject,
consumer.Durable,
nats.Bind(settings.Stream.Name, consumer.Durable),
nats.Context(ctx),
)
if err != nil {
return nil, err
}
return &natsSubscription{sub: sub}, nil
}
func (a *DurableAdapter) EnsureProducerResources(_ context.Context, settings reliable.DurableSettings) error {
if a == nil || a.broker == nil {
return capability.Unavailable("durable admin")
}
return ensureDurableStream(a.broker.Logger(), a.jetStream(), settings)
}
func (a *DurableAdapter) EnsureConsumerResources(_ context.Context, settings reliable.DurableSettings, consumer reliable.DurableConsumerSettings) error {
if a == nil || a.broker == nil {
return capability.Unavailable("durable admin")
}
js := a.jetStream()
if err := ensureDurableStream(a.broker.Logger(), js, settings); err != nil {
return err
}
return ensureDurableConsumerConfig(a.broker.Logger(), js, settings, consumer)
}
func (a *DurableAdapter) jetStream() nats.JetStreamContext {
if a == nil || a.broker == nil {
return nil
}
return a.broker.JetStream()
}