141 lines
4.6 KiB
Go
141 lines
4.6 KiB
Go
package redisstate
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"galaxy/notification/internal/api/intentstream"
|
|
"galaxy/notification/internal/service/acceptintent"
|
|
|
|
"github.com/redis/go-redis/v9"
|
|
)
|
|
|
|
// AcceptanceStore provides the Redis-backed durable storage used by the
|
|
// intent-acceptance use case.
|
|
type AcceptanceStore struct {
|
|
client *redis.Client
|
|
writer *AtomicWriter
|
|
keys Keyspace
|
|
cfg AcceptanceConfig
|
|
}
|
|
|
|
// NewAcceptanceStore constructs one Redis-backed acceptance store.
|
|
func NewAcceptanceStore(client *redis.Client, cfg AcceptanceConfig) (*AcceptanceStore, error) {
|
|
if client == nil {
|
|
return nil, errors.New("new notification acceptance store: nil redis client")
|
|
}
|
|
|
|
writer, err := NewAtomicWriter(client, cfg)
|
|
if err != nil {
|
|
return nil, fmt.Errorf("new notification acceptance store: %w", err)
|
|
}
|
|
|
|
return &AcceptanceStore{
|
|
client: client,
|
|
writer: writer,
|
|
keys: Keyspace{},
|
|
cfg: cfg,
|
|
}, nil
|
|
}
|
|
|
|
// CreateAcceptance stores one complete accepted notification write set in
|
|
// Redis.
|
|
func (store *AcceptanceStore) CreateAcceptance(ctx context.Context, input acceptintent.CreateAcceptanceInput) error {
|
|
if store == nil || store.client == nil || store.writer == nil {
|
|
return errors.New("create notification acceptance: nil store")
|
|
}
|
|
if ctx == nil {
|
|
return errors.New("create notification acceptance: nil context")
|
|
}
|
|
if err := input.Validate(); err != nil {
|
|
return fmt.Errorf("create notification acceptance: %w", err)
|
|
}
|
|
|
|
err := store.writer.CreateAcceptance(ctx, input)
|
|
if errors.Is(err, ErrConflict) {
|
|
return fmt.Errorf("create notification acceptance: %w", acceptintent.ErrConflict)
|
|
}
|
|
if err != nil {
|
|
return fmt.Errorf("create notification acceptance: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// GetIdempotency loads one accepted idempotency scope from Redis.
|
|
func (store *AcceptanceStore) GetIdempotency(ctx context.Context, producer intentstream.Producer, idempotencyKey string) (acceptintent.IdempotencyRecord, bool, error) {
|
|
if store == nil || store.client == nil {
|
|
return acceptintent.IdempotencyRecord{}, false, errors.New("get notification idempotency: nil store")
|
|
}
|
|
if ctx == nil {
|
|
return acceptintent.IdempotencyRecord{}, false, errors.New("get notification idempotency: nil context")
|
|
}
|
|
|
|
payload, err := store.client.Get(ctx, store.keys.Idempotency(producer, idempotencyKey)).Bytes()
|
|
switch {
|
|
case errors.Is(err, redis.Nil):
|
|
return acceptintent.IdempotencyRecord{}, false, nil
|
|
case err != nil:
|
|
return acceptintent.IdempotencyRecord{}, false, fmt.Errorf("get notification idempotency: %w", err)
|
|
}
|
|
|
|
record, err := UnmarshalIdempotency(payload)
|
|
if err != nil {
|
|
return acceptintent.IdempotencyRecord{}, false, fmt.Errorf("get notification idempotency: %w", err)
|
|
}
|
|
|
|
return record, true, nil
|
|
}
|
|
|
|
// GetNotification loads one accepted notification record from Redis.
|
|
func (store *AcceptanceStore) GetNotification(ctx context.Context, notificationID string) (acceptintent.NotificationRecord, bool, error) {
|
|
if store == nil || store.client == nil {
|
|
return acceptintent.NotificationRecord{}, false, errors.New("get notification record: nil store")
|
|
}
|
|
if ctx == nil {
|
|
return acceptintent.NotificationRecord{}, false, errors.New("get notification record: nil context")
|
|
}
|
|
|
|
payload, err := store.client.Get(ctx, store.keys.Notification(notificationID)).Bytes()
|
|
switch {
|
|
case errors.Is(err, redis.Nil):
|
|
return acceptintent.NotificationRecord{}, false, nil
|
|
case err != nil:
|
|
return acceptintent.NotificationRecord{}, false, fmt.Errorf("get notification record: %w", err)
|
|
}
|
|
|
|
record, err := UnmarshalNotification(payload)
|
|
if err != nil {
|
|
return acceptintent.NotificationRecord{}, false, fmt.Errorf("get notification record: %w", err)
|
|
}
|
|
|
|
return record, true, nil
|
|
}
|
|
|
|
// GetRoute loads one accepted notification route by NotificationID and
|
|
// RouteID.
|
|
func (store *AcceptanceStore) GetRoute(ctx context.Context, notificationID string, routeID string) (acceptintent.NotificationRoute, bool, error) {
|
|
if store == nil || store.client == nil {
|
|
return acceptintent.NotificationRoute{}, false, errors.New("get notification route: nil store")
|
|
}
|
|
if ctx == nil {
|
|
return acceptintent.NotificationRoute{}, false, errors.New("get notification route: nil context")
|
|
}
|
|
|
|
payload, err := store.client.Get(ctx, store.keys.Route(notificationID, routeID)).Bytes()
|
|
switch {
|
|
case errors.Is(err, redis.Nil):
|
|
return acceptintent.NotificationRoute{}, false, nil
|
|
case err != nil:
|
|
return acceptintent.NotificationRoute{}, false, fmt.Errorf("get notification route: %w", err)
|
|
}
|
|
|
|
record, err := UnmarshalRoute(payload)
|
|
if err != nil {
|
|
return acceptintent.NotificationRoute{}, false, fmt.Errorf("get notification route: %w", err)
|
|
}
|
|
|
|
return record, true, nil
|
|
}
|