Files
galaxy-game/notification/internal/adapters/postgres/notificationstore/malformed_intents.go
T
2026-04-26 20:34:39 +02:00

132 lines
3.9 KiB
Go

package notificationstore
import (
"context"
"errors"
"fmt"
pgtable "galaxy/notification/internal/adapters/postgres/jet/notification/table"
"galaxy/notification/internal/service/malformedintent"
pg "github.com/go-jet/jet/v2/postgres"
)
// Record stores entry idempotently by stream entry id. The helper satisfies
// `worker.MalformedIntentRecorder`. Re-recording an entry with the same
// `stream_entry_id` is a silent no-op via `ON CONFLICT DO NOTHING`.
func (store *Store) Record(ctx context.Context, entry malformedintent.Entry) error {
if store == nil {
return errors.New("record malformed intent: nil store")
}
if ctx == nil {
return errors.New("record malformed intent: nil context")
}
if err := entry.Validate(); err != nil {
return fmt.Errorf("record malformed intent: %w", err)
}
rawFields, err := marshalRawFields(entry.RawFields)
if err != nil {
return fmt.Errorf("record malformed intent: %w", err)
}
operationCtx, cancel, err := store.operationContext(ctx, "record malformed intent")
if err != nil {
return err
}
defer cancel()
stmt := pgtable.MalformedIntents.INSERT(
pgtable.MalformedIntents.StreamEntryID,
pgtable.MalformedIntents.NotificationType,
pgtable.MalformedIntents.Producer,
pgtable.MalformedIntents.IdempotencyKey,
pgtable.MalformedIntents.FailureCode,
pgtable.MalformedIntents.FailureMessage,
pgtable.MalformedIntents.RawFields,
pgtable.MalformedIntents.RecordedAt,
).VALUES(
entry.StreamEntryID,
entry.NotificationType,
entry.Producer,
entry.IdempotencyKey,
string(entry.FailureCode),
entry.FailureMessage,
rawFields,
entry.RecordedAt.UTC(),
).ON_CONFLICT(pgtable.MalformedIntents.StreamEntryID).DO_NOTHING()
query, args := stmt.Sql()
if _, err := store.db.ExecContext(operationCtx, query, args...); err != nil {
return fmt.Errorf("record malformed intent: %w", err)
}
return nil
}
// GetMalformedIntent loads one malformed-intent entry by stream entry id.
// Returns found=false when no such row exists.
func (store *Store) GetMalformedIntent(ctx context.Context, streamEntryID string) (malformedintent.Entry, bool, error) {
if store == nil {
return malformedintent.Entry{}, false, errors.New("get malformed intent: nil store")
}
if ctx == nil {
return malformedintent.Entry{}, false, errors.New("get malformed intent: nil context")
}
operationCtx, cancel, err := store.operationContext(ctx, "get malformed intent")
if err != nil {
return malformedintent.Entry{}, false, err
}
defer cancel()
stmt := pg.SELECT(
pgtable.MalformedIntents.NotificationType,
pgtable.MalformedIntents.Producer,
pgtable.MalformedIntents.IdempotencyKey,
pgtable.MalformedIntents.FailureCode,
pgtable.MalformedIntents.FailureMessage,
pgtable.MalformedIntents.RawFields,
pgtable.MalformedIntents.RecordedAt,
).FROM(pgtable.MalformedIntents).
WHERE(pgtable.MalformedIntents.StreamEntryID.EQ(pg.String(streamEntryID)))
query, args := stmt.Sql()
row := store.db.QueryRowContext(operationCtx, query, args...)
var (
notificationType string
producer string
idempotencyKey string
failureCode string
failureMessage string
rawFields []byte
)
entry := malformedintent.Entry{StreamEntryID: streamEntryID}
if err := row.Scan(
&notificationType,
&producer,
&idempotencyKey,
&failureCode,
&failureMessage,
&rawFields,
&entry.RecordedAt,
); err != nil {
if isNoRows(err) {
return malformedintent.Entry{}, false, nil
}
return malformedintent.Entry{}, false, fmt.Errorf("get malformed intent: %w", err)
}
entry.NotificationType = notificationType
entry.Producer = producer
entry.IdempotencyKey = idempotencyKey
entry.FailureCode = malformedintent.FailureCode(failureCode)
entry.FailureMessage = failureMessage
entry.RecordedAt = entry.RecordedAt.UTC()
fields, err := unmarshalRawFields(rawFields)
if err != nil {
return malformedintent.Entry{}, false, fmt.Errorf("get malformed intent: %w", err)
}
entry.RawFields = fields
return entry, true, nil
}