Files
go-sip/internal/store/rejection_identity.go
T

59 lines
1.8 KiB
Go

package store
import (
"database/sql"
"encoding/json"
"errors"
)
var ErrEventRejected = errors.New("original event identity was already rejected")
// A rejected identity is terminal, even if its producer later changes the body.
// Retransmit the original feedback, never adopt the changed command.
func (s *Store) ReplayRejectedEvent(dispatcherID, eventID, eventType string, tenantID int64) (bool, error) {
tx, err := s.db.Begin()
if err != nil {
return false, err
}
defer tx.Rollback()
rejected, err := replayRejectedEvent(tx, dispatcherID, eventID, eventType, tenantID)
if err != nil {
return false, err
}
if err := tx.Commit(); err != nil {
return false, err
}
return rejected, nil
}
func replayRejectedEvent(tx *sql.Tx, dispatcherID, eventID, eventType string, tenantID int64) (bool, error) {
var kind string
var body []byte
err := tx.QueryRow(`SELECT event_type,body FROM dispatcher_outbox WHERE dispatcher_id=? AND event_type=? AND event_id=?`, dispatcherID, eventType, eventID).Scan(&kind, &body)
if errors.Is(err, sql.ErrNoRows) {
return false, nil
}
if err != nil {
return false, err
}
if kind != eventType {
return false, errors.New("original event identity belongs to another outbound result")
}
var receipt struct {
TenantID int64 `json:"tenant_id"`
Payload struct {
Status string `json:"status"`
} `json:"payload"`
}
if err := json.Unmarshal(body, &receipt); err != nil {
return false, err
}
if receipt.TenantID != tenantID {
return false, errors.New("original rejection tenant identity conflicts with incoming data")
}
if receipt.Payload.Status != "rejected" {
return false, nil
}
_, err = tx.Exec(`UPDATE dispatcher_outbox SET confirmed=0,confirmed_at=NULL WHERE dispatcher_id=? AND event_type=? AND event_id=?`, dispatcherID, eventType, eventID)
return err == nil, err
}