149 lines
4.7 KiB
Go
149 lines
4.7 KiB
Go
package mailstore
|
|
|
|
import (
|
|
"context"
|
|
"errors"
|
|
"fmt"
|
|
"time"
|
|
|
|
pgtable "galaxy/mail/internal/adapters/postgres/jet/mail/table"
|
|
"galaxy/mail/internal/domain/malformedcommand"
|
|
|
|
pg "github.com/go-jet/jet/v2/postgres"
|
|
)
|
|
|
|
// Record stores entry idempotently by stream entry id. The helper satisfies
|
|
// worker.MalformedCommandRecorder.
|
|
func (store *Store) Record(ctx context.Context, entry malformedcommand.Entry) error {
|
|
if store == nil {
|
|
return errors.New("record malformed command: nil store")
|
|
}
|
|
if ctx == nil {
|
|
return errors.New("record malformed command: nil context")
|
|
}
|
|
if err := entry.Validate(); err != nil {
|
|
return fmt.Errorf("record malformed command: %w", err)
|
|
}
|
|
|
|
rawFields, err := marshalRawFields(entry.RawFields)
|
|
if err != nil {
|
|
return fmt.Errorf("record malformed command: %w", err)
|
|
}
|
|
|
|
operationCtx, cancel, err := store.operationContext(ctx, "record malformed command")
|
|
if err != nil {
|
|
return err
|
|
}
|
|
defer cancel()
|
|
|
|
stmt := pgtable.MalformedCommands.INSERT(
|
|
pgtable.MalformedCommands.StreamEntryID,
|
|
pgtable.MalformedCommands.DeliveryID,
|
|
pgtable.MalformedCommands.Source,
|
|
pgtable.MalformedCommands.IdempotencyKey,
|
|
pgtable.MalformedCommands.FailureCode,
|
|
pgtable.MalformedCommands.FailureMessage,
|
|
pgtable.MalformedCommands.RawFields,
|
|
pgtable.MalformedCommands.RecordedAt,
|
|
).VALUES(
|
|
entry.StreamEntryID,
|
|
entry.DeliveryID,
|
|
entry.Source,
|
|
entry.IdempotencyKey,
|
|
string(entry.FailureCode),
|
|
entry.FailureMessage,
|
|
rawFields,
|
|
entry.RecordedAt.UTC(),
|
|
).ON_CONFLICT(pgtable.MalformedCommands.StreamEntryID).DO_NOTHING()
|
|
|
|
query, args := stmt.Sql()
|
|
if _, err := store.db.ExecContext(operationCtx, query, args...); err != nil {
|
|
return fmt.Errorf("record malformed command: %w", err)
|
|
}
|
|
return nil
|
|
}
|
|
|
|
// GetMalformedCommand loads one malformed-command entry by stream entry id.
|
|
func (store *Store) GetMalformedCommand(ctx context.Context, streamEntryID string) (malformedcommand.Entry, bool, error) {
|
|
if store == nil {
|
|
return malformedcommand.Entry{}, false, errors.New("get malformed command: nil store")
|
|
}
|
|
if ctx == nil {
|
|
return malformedcommand.Entry{}, false, errors.New("get malformed command: nil context")
|
|
}
|
|
operationCtx, cancel, err := store.operationContext(ctx, "get malformed command")
|
|
if err != nil {
|
|
return malformedcommand.Entry{}, false, err
|
|
}
|
|
defer cancel()
|
|
|
|
stmt := pg.SELECT(
|
|
pgtable.MalformedCommands.DeliveryID,
|
|
pgtable.MalformedCommands.Source,
|
|
pgtable.MalformedCommands.IdempotencyKey,
|
|
pgtable.MalformedCommands.FailureCode,
|
|
pgtable.MalformedCommands.FailureMessage,
|
|
pgtable.MalformedCommands.RawFields,
|
|
pgtable.MalformedCommands.RecordedAt,
|
|
).FROM(pgtable.MalformedCommands).
|
|
WHERE(pgtable.MalformedCommands.StreamEntryID.EQ(pg.String(streamEntryID)))
|
|
|
|
query, args := stmt.Sql()
|
|
row := store.db.QueryRowContext(operationCtx, query, args...)
|
|
var (
|
|
deliveryID string
|
|
source string
|
|
idempotencyKey string
|
|
failureCode string
|
|
failureMessage string
|
|
rawFields []byte
|
|
)
|
|
entry := malformedcommand.Entry{StreamEntryID: streamEntryID}
|
|
if err := row.Scan(&deliveryID, &source, &idempotencyKey, &failureCode, &failureMessage, &rawFields, &entry.RecordedAt); err != nil {
|
|
if isNoRows(err) {
|
|
return malformedcommand.Entry{}, false, nil
|
|
}
|
|
return malformedcommand.Entry{}, false, fmt.Errorf("get malformed command: %w", err)
|
|
}
|
|
entry.DeliveryID = deliveryID
|
|
entry.Source = source
|
|
entry.IdempotencyKey = idempotencyKey
|
|
entry.FailureCode = malformedcommand.FailureCode(failureCode)
|
|
entry.FailureMessage = failureMessage
|
|
entry.RecordedAt = entry.RecordedAt.UTC()
|
|
fields, err := unmarshalRawFields(rawFields)
|
|
if err != nil {
|
|
return malformedcommand.Entry{}, false, fmt.Errorf("get malformed command: %w", err)
|
|
}
|
|
entry.RawFields = fields
|
|
return entry, true, nil
|
|
}
|
|
|
|
// DeleteMalformedCommandsOlderThan removes malformed-command rows whose
|
|
// recorded_at predates cutoff. The helper satisfies the SQLRetentionStore
|
|
// contract used by the periodic retention worker.
|
|
func (store *Store) DeleteMalformedCommandsOlderThan(ctx context.Context, cutoff time.Time) (int64, error) {
|
|
if store == nil {
|
|
return 0, errors.New("delete malformed commands: nil store")
|
|
}
|
|
operationCtx, cancel, err := store.operationContext(ctx, "delete malformed commands")
|
|
if err != nil {
|
|
return 0, err
|
|
}
|
|
defer cancel()
|
|
|
|
stmt := pgtable.MalformedCommands.DELETE().
|
|
WHERE(pgtable.MalformedCommands.RecordedAt.LT(pg.TimestampzT(cutoff.UTC())))
|
|
|
|
query, args := stmt.Sql()
|
|
result, err := store.db.ExecContext(operationCtx, query, args...)
|
|
if err != nil {
|
|
return 0, fmt.Errorf("delete malformed commands: %w", err)
|
|
}
|
|
rows, err := result.RowsAffected()
|
|
if err != nil {
|
|
return 0, fmt.Errorf("delete malformed commands: rows affected: %w", err)
|
|
}
|
|
return rows, nil
|
|
}
|