feat: notification service
This commit is contained in:
@@ -0,0 +1,140 @@
|
||||
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
|
||||
}
|
||||
Reference in New Issue
Block a user