package redisstate import ( "context" "errors" "fmt" "galaxy/mail/internal/domain/common" deliverydomain "galaxy/mail/internal/domain/delivery" "galaxy/mail/internal/domain/idempotency" "galaxy/mail/internal/service/acceptauthdelivery" "github.com/redis/go-redis/v9" ) // AcceptanceStore provides the Redis-backed durable storage used by the // auth-delivery acceptance use case. type AcceptanceStore struct { client *redis.Client writer *AtomicWriter keys Keyspace } // NewAcceptanceStore constructs one Redis-backed auth acceptance store. func NewAcceptanceStore(client *redis.Client) (*AcceptanceStore, error) { if client == nil { return nil, errors.New("new auth acceptance store: nil redis client") } writer, err := NewAtomicWriter(client) if err != nil { return nil, fmt.Errorf("new auth acceptance store: %w", err) } return &AcceptanceStore{ client: client, writer: writer, keys: Keyspace{}, }, nil } // CreateAcceptance stores one auth-delivery acceptance write set in Redis. func (store *AcceptanceStore) CreateAcceptance(ctx context.Context, input acceptauthdelivery.CreateAcceptanceInput) error { if store == nil || store.client == nil || store.writer == nil { return errors.New("create auth acceptance: nil store") } if ctx == nil { return errors.New("create auth acceptance: nil context") } if err := input.Validate(); err != nil { return fmt.Errorf("create auth acceptance: %w", err) } err := store.writer.CreateAcceptance(ctx, CreateAcceptanceInput{ Delivery: input.Delivery, FirstAttempt: input.FirstAttempt, Idempotency: &input.Idempotency, }) if errors.Is(err, ErrConflict) { return fmt.Errorf("create auth acceptance: %w", acceptauthdelivery.ErrConflict) } if err != nil { return fmt.Errorf("create auth acceptance: %w", err) } return nil } // GetIdempotency loads one accepted idempotency scope from Redis. func (store *AcceptanceStore) GetIdempotency(ctx context.Context, source deliverydomain.Source, key common.IdempotencyKey) (idempotency.Record, bool, error) { if store == nil || store.client == nil { return idempotency.Record{}, false, errors.New("get auth acceptance idempotency: nil store") } if ctx == nil { return idempotency.Record{}, false, errors.New("get auth acceptance idempotency: nil context") } payload, err := store.client.Get(ctx, store.keys.Idempotency(source, key)).Bytes() switch { case errors.Is(err, redis.Nil): return idempotency.Record{}, false, nil case err != nil: return idempotency.Record{}, false, fmt.Errorf("get auth acceptance idempotency: %w", err) } record, err := UnmarshalIdempotency(payload) if err != nil { return idempotency.Record{}, false, fmt.Errorf("get auth acceptance idempotency: %w", err) } return record, true, nil } // GetDelivery loads one accepted delivery from Redis. func (store *AcceptanceStore) GetDelivery(ctx context.Context, deliveryID common.DeliveryID) (deliverydomain.Delivery, bool, error) { if store == nil || store.client == nil { return deliverydomain.Delivery{}, false, errors.New("get auth acceptance delivery: nil store") } if ctx == nil { return deliverydomain.Delivery{}, false, errors.New("get auth acceptance delivery: nil context") } payload, err := store.client.Get(ctx, store.keys.Delivery(deliveryID)).Bytes() switch { case errors.Is(err, redis.Nil): return deliverydomain.Delivery{}, false, nil case err != nil: return deliverydomain.Delivery{}, false, fmt.Errorf("get auth acceptance delivery: %w", err) } record, err := UnmarshalDelivery(payload) if err != nil { return deliverydomain.Delivery{}, false, fmt.Errorf("get auth acceptance delivery: %w", err) } return record, true, nil }