fix(infra): let Objecten actually accept the register record (refs #149)

Replaying the gateway's calls against a live Objecten + Objecttypen pair turned
up two blockers CI would only have found after the fact:

- Objecten rejects an objecttype it has not been configured with, and it
  identifies one by uuid — assigned at seed time by a one-shot that runs after
  Objecten's static setup_configuration. Pin the uuid on both sides instead.
- Objecten notifies on every write and notifications_api_common *raises* when
  that config is absent, so every POST 500'd after rolling the object back.
  Objecten → NRC has no broker, worker, kanaal or abonnement yet, so disable
  notifications rather than wire a client that drops every message; S-19b turns
  them on for real.

With both in place the full exchange verifies end to end: lookup → version →
search → create → update (still one object), and a record carrying a bsn is
rejected by the schema. ADR-0028 records both.
This commit is contained in:
not
2026-08-14 09:41:00 +02:00
parent 43b45ad756
commit 4a047c618c
5 changed files with 69 additions and 1 deletions
@@ -77,6 +77,38 @@ is not cached, so it is retried on the next approval.
- Upgrade path: lift it into a singleton cache (as `CachedZaaktypeCatalog` does for ZGW)
if approvals ever get hot enough for that GET to matter.
### The objecttype's UUID is pinned, not server-assigned
Objecten refuses to store an object whose objecttype it has not been configured with
(`ObjectType with url=… is not configured`), and its configuration identifies an
objecttype **by UUID** — supplied through a static `setup_configuration` file applied
when the container starts, before the `registerrecord-init` one-shot has run.
Rather than thread a seed-time UUID from one container into another's config, the UUID is
**pinned**: `infra/objecttypen-registerrecord/register.py` creates the objecttype with a
fixed UUID (the Objecttypen API accepts a client-supplied one), and
`infra/objecten/setup_configuration/data.yaml` declares that same UUID. Both sides are
declared up front, both stay idempotent, and neither has to wait for the other.
The cost is a constant duplicated across two files that must be kept in step; each carries
a comment pointing at the other.
### Objecten's notifications are off for this slice
Objecten publishes to a Notificaties API on every write, and `notifications_api_common`
**raises** rather than skipping when that configuration is absent — so with no NRC wiring,
every `POST /api/v2/objects` returns 500 after creating and rolling back the object.
Objecten → NRC is not wired: there is no broker, no Celery worker, no `objecten` kanaal and
no abonnement for it. Configuring only the client side would make writes succeed while
every message was dropped on the floor — a delivery path that looks wired and isn't. So
`NOTIFICATIONS_DISABLED` is set for Objecten in both compose files instead.
- ponytail ceiling: Objecten emits no notifications, so nothing downstream can react to a
register write yet.
- Upgrade path: S-19b (#150) needs those notifications to source the projection from
Objecten, and turns them on together with the broker, worker, kanaal and abonnement.
## Consequences
**Positive**
@@ -95,6 +127,8 @@ is not cached, so it is retried on the next approval.
Nothing repairs that automatically yet.
- One more upstream module on the approval path, and one more dev credential
(`Acl__Objecten__Token`) in compose.
- Two new hand-kept constants: the pinned objecttype UUID (two files) and the objecttype
name (compose + `register.py`).
- Until S-19b lands, the public register is still read from the NRC-derived projection, so
the register record is written but not yet read — the two must agree.
@@ -110,3 +144,8 @@ and no service reaches Objecten's database.
asserts, via `infra/register-record-check.py`, that Objecten holds exactly one
`RegisterRecord` for that registration, with status `INGESCHREVEN` and no field outside
the public-safe schema.
Every HTTP exchange the gateway performs was additionally replayed by hand against a live
Objecten + Objecttypen pair while writing this slice — objecttype lookup by name, version
status, `data_attrs` search, create, update, and a rejected write carrying a `bsn`. Both
findings above came out of that replay rather than out of CI.