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( ¬ificationType, &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 }