feat(v1.1.2-docs): triggers framework + dispatcher + emitter extended for docs

The docs trigger kind hangs off the same Layout-E shape that v1.1.1
established for KV: a parent triggers row + a docs_trigger_details
row (collection_glob TEXT + ops TEXT[]) with the empty-array =
any-op semantic preserved.

- trigger_repo.rs adds TriggerKind::Docs + TriggerDetails::Docs +
  CreateDocsTrigger + DocsTriggerMatch + PostgresTriggerRepo
  implementations of create_docs_trigger and list_matching_docs.
  list_matching_docs mirrors KV's Rust-side filter (does NOT push
  ops membership into SQL — that would exclude empty-ops rows).
- outbox_repo.rs adds OutboxSourceKind::Docs to the enum + wire form.
- dispatcher.rs's generic Kv | DeadLetter routing arm extends to
  Kv | DeadLetter | Docs. No kind-specific logic needed — the
  resolve_trigger + build_exec_request path is already abstract.
- outbox_event_emitter.rs gains a "docs" arm in the emit match plus
  emit_docs which builds TriggerEvent::Docs (carrying data +
  prev_data) and fans out across matching triggers.
- triggers_api.rs adds CreateDocsTriggerRequest + create_docs_trigger
  + the POST /api/v1/admin/apps/{id}/triggers/docs route, all
  guarded by Capability::AppManageTriggers (same as KV).

3 new triggers_api unit tests covering happy path, empty-glob
rejection, and capability denial. All existing trigger-related
tests still pass.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
MechaCat02
2026-06-02 19:55:27 +02:00
parent 06678f4496
commit ef5930910b
5 changed files with 425 additions and 9 deletions

View File

@@ -22,6 +22,8 @@ pub enum OutboxRepoError {
pub enum OutboxSourceKind {
Http,
Kv,
/// v1.1.2.
Docs,
DeadLetter,
}
@@ -31,6 +33,7 @@ impl OutboxSourceKind {
match self {
Self::Http => "http",
Self::Kv => "kv",
Self::Docs => "docs",
Self::DeadLetter => "dead_letter",
}
}
@@ -40,6 +43,7 @@ impl OutboxSourceKind {
match s {
"http" => Some(Self::Http),
"kv" => Some(Self::Kv),
"docs" => Some(Self::Docs),
"dead_letter" => Some(Self::DeadLetter),
_ => None,
}