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/acceptgenericdelivery" "github.com/redis/go-redis/v9" ) // GenericAcceptanceStore provides the Redis-backed durable storage used by the // generic-delivery acceptance use case. type GenericAcceptanceStore struct { client *redis.Client writer *AtomicWriter keys Keyspace } // NewGenericAcceptanceStore constructs one Redis-backed generic acceptance // store. func NewGenericAcceptanceStore(client *redis.Client) (*GenericAcceptanceStore, error) { if client == nil { return nil, errors.New("new generic acceptance store: nil redis client") } writer, err := NewAtomicWriter(client) if err != nil { return nil, fmt.Errorf("new generic acceptance store: %w", err) } return &GenericAcceptanceStore{ client: client, writer: writer, keys: Keyspace{}, }, nil } // CreateAcceptance stores one generic-delivery acceptance write set in Redis. func (store *GenericAcceptanceStore) CreateAcceptance(ctx context.Context, input acceptgenericdelivery.CreateAcceptanceInput) error { if store == nil || store.client == nil || store.writer == nil { return errors.New("create generic acceptance: nil store") } if ctx == nil { return errors.New("create generic acceptance: nil context") } if err := input.Validate(); err != nil { return fmt.Errorf("create generic acceptance: %w", err) } writerInput := CreateAcceptanceInput{ Delivery: input.Delivery, FirstAttempt: &input.FirstAttempt, Idempotency: &input.Idempotency, } if input.DeliveryPayload != nil { writerInput.DeliveryPayload = input.DeliveryPayload } err := store.writer.CreateAcceptance(ctx, writerInput) if errors.Is(err, ErrConflict) { return fmt.Errorf("create generic acceptance: %w", acceptgenericdelivery.ErrConflict) } if err != nil { return fmt.Errorf("create generic acceptance: %w", err) } return nil } // GetIdempotency loads one accepted idempotency scope from Redis. func (store *GenericAcceptanceStore) 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 generic acceptance idempotency: nil store") } if ctx == nil { return idempotency.Record{}, false, errors.New("get generic 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 generic acceptance idempotency: %w", err) } record, err := UnmarshalIdempotency(payload) if err != nil { return idempotency.Record{}, false, fmt.Errorf("get generic acceptance idempotency: %w", err) } return record, true, nil } // GetDelivery loads one accepted delivery by its identifier. func (store *GenericAcceptanceStore) GetDelivery(ctx context.Context, deliveryID common.DeliveryID) (deliverydomain.Delivery, bool, error) { if store == nil || store.client == nil { return deliverydomain.Delivery{}, false, errors.New("get generic acceptance delivery: nil store") } if ctx == nil { return deliverydomain.Delivery{}, false, errors.New("get generic 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 generic acceptance delivery: %w", err) } record, err := UnmarshalDelivery(payload) if err != nil { return deliverydomain.Delivery{}, false, fmt.Errorf("get generic acceptance delivery: %w", err) } return record, true, nil } // GetDeliveryPayload loads one raw accepted attachment bundle by delivery id. func (store *GenericAcceptanceStore) GetDeliveryPayload(ctx context.Context, deliveryID common.DeliveryID) (acceptgenericdelivery.DeliveryPayload, bool, error) { if store == nil || store.client == nil { return acceptgenericdelivery.DeliveryPayload{}, false, errors.New("get generic acceptance delivery payload: nil store") } if ctx == nil { return acceptgenericdelivery.DeliveryPayload{}, false, errors.New("get generic acceptance delivery payload: nil context") } payload, err := store.client.Get(ctx, store.keys.DeliveryPayload(deliveryID)).Bytes() switch { case errors.Is(err, redis.Nil): return acceptgenericdelivery.DeliveryPayload{}, false, nil case err != nil: return acceptgenericdelivery.DeliveryPayload{}, false, fmt.Errorf("get generic acceptance delivery payload: %w", err) } record, err := UnmarshalDeliveryPayload(payload) if err != nil { return acceptgenericdelivery.DeliveryPayload{}, false, fmt.Errorf("get generic acceptance delivery payload: %w", err) } return record, true, nil }