CreateProtectionEvent appends a screening event. The insert is idempotent on the primary key: callers that set a DeterministicProtectionEventID get exactly-once recording across retries. If ev.ID is empty a random id is assigned. A conflicting id is a no-op (returns nil).
(ctx context.Context, ev ProtectionEvent)
| 64 | // recording across retries. If ev.ID is empty a random id is assigned. A conflicting |
| 65 | // id is a no-op (returns nil). |
| 66 | func (s *Store) CreateProtectionEvent(ctx context.Context, ev ProtectionEvent) error { |
| 67 | if ev.ID == "" { |
| 68 | ev.ID = NewProtectionEventID() |
| 69 | } |
| 70 | _, err := s.pool.Exec(ctx, |
| 71 | `INSERT INTO protection_events |
| 72 | (id, message_id, agent_id, direction, source, reason, action, |
| 73 | subject_addr, detector, score, categories, spans, raw) |
| 74 | VALUES ($1,$2,$3,$4,$5,$6,$7,$8,$9,$10,$11,$12,$13) |
| 75 | ON CONFLICT (id) DO NOTHING`, |
| 76 | ev.ID, ev.MessageID, ev.AgentID, ev.Direction, ev.Source, ev.Reason, ev.Action, |
| 77 | nullString(ev.SubjectAddr), nullString(ev.Detector), ev.Score, |
| 78 | nullJSON(ev.Categories), nullJSON(ev.Spans), nullJSON(ev.Raw), |
| 79 | ) |
| 80 | if err != nil { |
| 81 | return fmt.Errorf("create screening event: %w", err) |
| 82 | } |
| 83 | return nil |
| 84 | } |
| 85 | |
| 86 | // ListProtectionEventsByMessage returns every screening event recorded against a |
| 87 | // message, newest first — the breakdown a reviewer sees for a held item. |