~bigbes/sr-ht-spec

ref: c2dd1ef94a971405ff2ab1c048f9e74e0ef0b844 sr-ht-spec/graph/gqlgen.yml -rw-r--r-- 1.1 KiB
8374a1ef — Eugene Blikh 25 days ago
feat(graph): GraphQL-native webhook surface (Phase 5a)

The webhook types, mutations, and resolvers, adapted from the pages.sr.ht
core-go template for spec's single-owner model.

- SDL: WebhookEvent (PROPOSAL_OPENED/MERGED/REJECTED), WebhookSubscription
  interface + UserWebhookSubscription, WebhookDelivery, WebhookPayload
  interface + ProposalEvent (carries a Proposal), cursor wrappers,
  `webhook` payload root field, and a `type Mutation` with
  createUserWebhook / deleteUserWebhook. No OAuth `client` field and no
  @access/@private directives — spec has no OAuth clients or scopes, so
  the owner gate is the entire ACL.
- Models: hand-written database.Model impls (UserWebhookSubscription,
  WebhookDelivery) so gqlgen autobinds rather than generates them; events
  via pq.Array; cursor keyset pagination.
- Resolvers: all owner-gated via authn (spec's ACL), using core-go's
  webhook engine — Validate, NewAuthConfig (INTERNAL, via the coreauth
  bridge), FilterWebhooks, WebhookContext.Exec for the sample, and the
  `webhook`→Payload(ctx) root. Proposal writes deliberately stay off this
  surface (only webhook mutations; the schema test now asserts exactly
  that).
- gqlgen.yml binds Cursor to core-go's model.Cursor; generated code
  regenerated with the pinned gqlgen v0.17.36 (reproducible).

Compiles and vets clean; existing graph read tests still pass. Runtime
context wiring and event firing are the next slices.
6f4efc2b — bigbes 27 days ago
feat(graph): the read-only GraphQL schema at /query

Eight query fields over the service layer, no Mutation and no
Subscription — the design defers mutations until the proposal state
machine settles, and TestSchemaHasNoMutations stands guard on that.

Access is fail-closed and gated before parse, matching web's ACL exactly,
so introspection is treated as content too. A federating api.sr.ht must
therefore present a token or skip us, which costs one log line.

A malformed rev is reported as a GraphQL error rather than folded into
null. service/ deliberately hides malformed-versus-absent from probing,
but the caller here is already authenticated as the owner or its own
agent, and a bare null for rev=proposals/42 is indistinguishable from an
absent document — it reads as a silently dropped argument.

Proposal listing declares its port but is unwired: service/ exposes no
proposal read yet, and returning an empty list would tell a reviewer their
queue is clear when it is merely unread.