package notificationintent import ( "context" "errors" "fmt" "github.com/redis/go-redis/v9" ) // RedisClient stores the minimal Redis command surface required by Publisher. type RedisClient interface { // XAdd appends one entry to a Redis Stream. XAdd(context.Context, *redis.XAddArgs) *redis.StringCmd } // PublisherConfig stores the dependencies and stream name used by Publisher. type PublisherConfig struct { // Client appends normalized intents to Redis Streams. Client RedisClient // Stream stores the Redis Stream name. When empty, DefaultIntentsStream is // used. Stream string } // Publisher publishes normalized notification intents into the Notification // Service ingress stream. type Publisher struct { client RedisClient stream string } // NewPublisher constructs a Publisher from cfg. func NewPublisher(cfg PublisherConfig) (*Publisher, error) { if cfg.Client == nil { return nil, errors.New("new notification intent publisher: nil redis client") } if cfg.Stream == "" { cfg.Stream = DefaultIntentsStream } return &Publisher{ client: cfg.Client, stream: cfg.Stream, }, nil } // Publish validates intent and appends it with plain XADD. It does not trim // the stream and does not perform hidden retries. func (publisher *Publisher) Publish(ctx context.Context, intent Intent) (string, error) { if ctx == nil { return "", errors.New("publish notification intent: nil context") } if publisher == nil || publisher.client == nil { return "", errors.New("publish notification intent: nil publisher") } values, err := intent.Values() if err != nil { return "", fmt.Errorf("publish notification intent: %w", err) } entryID, err := publisher.client.XAdd(ctx, &redis.XAddArgs{ Stream: publisher.stream, Values: values, }).Result() if err != nil { return "", fmt.Errorf("publish notification intent: xadd: %w", err) } return entryID, nil }