129 lines
3.8 KiB
Go
129 lines
3.8 KiB
Go
// Package getdelivery implements trusted operator lookup of one accepted mail
|
|
// delivery.
|
|
package getdelivery
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
|
|
"galaxy/mail/internal/domain/common"
|
|
deliverydomain "galaxy/mail/internal/domain/delivery"
|
|
)
|
|
|
|
var (
|
|
// ErrNotFound reports that the requested delivery does not exist.
|
|
ErrNotFound = errors.New("get delivery not found")
|
|
|
|
// ErrServiceUnavailable reports that trusted lookup could not load durable
|
|
// state safely.
|
|
ErrServiceUnavailable = errors.New("get delivery service unavailable")
|
|
)
|
|
|
|
// Input stores one exact trusted lookup by delivery identifier.
|
|
type Input struct {
|
|
// DeliveryID stores the exact accepted delivery identifier to resolve.
|
|
DeliveryID common.DeliveryID
|
|
}
|
|
|
|
// Validate reports whether input contains a complete lookup key.
|
|
func (input Input) Validate() error {
|
|
if err := input.DeliveryID.Validate(); err != nil {
|
|
return fmt.Errorf("delivery id: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Result stores one full delivery record and its optional dead-letter entry.
|
|
type Result struct {
|
|
// Delivery stores the resolved accepted delivery record.
|
|
Delivery deliverydomain.Delivery
|
|
|
|
// DeadLetter stores the optional dead-letter entry when Delivery is in the
|
|
// `dead_letter` terminal state.
|
|
DeadLetter *deliverydomain.DeadLetterEntry
|
|
}
|
|
|
|
// Validate reports whether result contains a consistent delivery view.
|
|
func (result Result) Validate() error {
|
|
if err := result.Delivery.Validate(); err != nil {
|
|
return fmt.Errorf("delivery: %w", err)
|
|
}
|
|
if err := deliverydomain.ValidateDeadLetterState(result.Delivery, result.DeadLetter); err != nil {
|
|
return fmt.Errorf("dead-letter state: %w", err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
// Store provides exact lookup of one accepted delivery and its dead-letter
|
|
// entry.
|
|
type Store interface {
|
|
// GetDelivery loads one accepted delivery by its identifier.
|
|
GetDelivery(context.Context, common.DeliveryID) (deliverydomain.Delivery, bool, error)
|
|
|
|
// GetDeadLetter loads the dead-letter entry associated with deliveryID when
|
|
// one exists.
|
|
GetDeadLetter(context.Context, common.DeliveryID) (deliverydomain.DeadLetterEntry, bool, error)
|
|
}
|
|
|
|
// Config stores the dependencies used by Service.
|
|
type Config struct {
|
|
// Store owns durable delivery and dead-letter state.
|
|
Store Store
|
|
}
|
|
|
|
// Service executes trusted exact delivery lookups.
|
|
type Service struct {
|
|
store Store
|
|
}
|
|
|
|
// New constructs Service from cfg.
|
|
func New(cfg Config) (*Service, error) {
|
|
if cfg.Store == nil {
|
|
return nil, errors.New("new get delivery service: nil store")
|
|
}
|
|
|
|
return &Service{store: cfg.Store}, nil
|
|
}
|
|
|
|
// Execute loads one accepted delivery and its optional dead-letter entry.
|
|
func (service *Service) Execute(ctx context.Context, input Input) (Result, error) {
|
|
if ctx == nil {
|
|
return Result{}, errors.New("execute get delivery: nil context")
|
|
}
|
|
if service == nil {
|
|
return Result{}, errors.New("execute get delivery: nil service")
|
|
}
|
|
if err := input.Validate(); err != nil {
|
|
return Result{}, fmt.Errorf("execute get delivery: %w", err)
|
|
}
|
|
|
|
record, found, err := service.store.GetDelivery(ctx, input.DeliveryID)
|
|
switch {
|
|
case err != nil:
|
|
return Result{}, fmt.Errorf("%w: load delivery: %v", ErrServiceUnavailable, err)
|
|
case !found:
|
|
return Result{}, ErrNotFound
|
|
}
|
|
|
|
result := Result{Delivery: record}
|
|
if record.Status == deliverydomain.StatusDeadLetter {
|
|
entry, found, err := service.store.GetDeadLetter(ctx, input.DeliveryID)
|
|
switch {
|
|
case err != nil:
|
|
return Result{}, fmt.Errorf("%w: load dead-letter entry: %v", ErrServiceUnavailable, err)
|
|
case !found:
|
|
return Result{}, fmt.Errorf("%w: missing dead-letter entry for delivery %q", ErrServiceUnavailable, input.DeliveryID)
|
|
default:
|
|
result.DeadLetter = &entry
|
|
}
|
|
}
|
|
if err := result.Validate(); err != nil {
|
|
return Result{}, fmt.Errorf("%w: invalid result: %v", ErrServiceUnavailable, err)
|
|
}
|
|
|
|
return result, nil
|
|
}
|