refactor: strip WP-/RB- ticket refs from backend (RD-19)

The backend half of the sweep RD-18 did for the front end. git blame
holds the provenance and stays correct when the code moves; the
comment names a closed ticket and tells the reader nothing the
sentence around it does not.

public/letter.css and LetterHtml.golden.html change together, because
the renderer inlines the CSS and the golden file snapshots the
result.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
eho
2026-09-04 21:48:08 +02:00
co-authored by Claude Sonnet 5
parent dd11eafe50
commit 8560746d15
89 changed files with 530 additions and 380 deletions
+17 -17
View File
@@ -1,4 +1,4 @@
# OpenZaak integration harness (WP-54)
# OpenZaak integration harness
A real OpenZaak, for developing/testing the ZGW seam (`backend/src/BigRegister.Api/Zgw/`)
against something that isn't a fixture or a stub `HttpMessageHandler`. Deliberately **not**
@@ -22,7 +22,7 @@ published), and one zaak (`BIG-2026-000123`) with an initiator rol for the seede
(`111222333` — the same fixture BSN `OpenZaakZaakSourceTests.cs` uses). It writes what it
seeded to `seeded.env` (gitignored) and prints a summary.
**Idempotent (WP-56)** — every resource is looked up by its natural key (the same field(s)
**Idempotent** — every resource is looked up by its natural key (the same field(s)
OpenZaak enforces identity on: catalogus by `domein`+`rsin`, zaaktype by `catalogus`+
`identificatie`, statustype by `zaaktype`+`volgnummer`, roltype by `zaaktype`+
`omschrijvingGeneriek`, zaak by `identificatie`) before creating it, so re-running against an
@@ -81,7 +81,7 @@ and is **excluded** from the default `dotnet test` run and from CI (`ci.yml`,
`scripts/ci-local.sh` both filter `Category!=Integration`) — it only passes with this harness
up, so it never runs where the harness doesn't exist.
## Notifications-enabled profile (WP-58)
## Notifications-enabled profile
The base harness above never delivers a real notification (`NOTIFICATIONS_DISABLED: 'true'`,
no celery worker) — fine for the read/write ZGW seam, not for proving a live webhook round-trip.
@@ -137,7 +137,7 @@ already high going in; `ZGW_DEBUG_HTTP=1` on `api`, see `docker-compose.openzaak
diagnostics to help nail the cause next time it reproduces). Prints the URLs to check
afterward and the teardown commands.
Two caveats, both non-fatal (WP-60 catches and flags rather than surfacing an error):
Two caveats, both non-fatal (the BFF catches and flags rather than surfacing an error):
**only `herregistratie` has a seeded zaaktype** here, so submit that wizard to prove a real
write; and **no Documenten content is seeded**, so a document upload's ZGW half no-ops (pick
"per post" in the wizard's document step, or ignore it).
@@ -148,7 +148,7 @@ write; and **no Documenten content is seeded**, so a document upload's ZGW half
docker compose -f docker-compose.openzaak.yml down -v
```
## Production (WP-55)
## Production
This dev harness stays dev-only: hardcoded `SECRET_KEY`, `POSTGRES_HOST_AUTH_METHOD=trust`,
`IS_HTTPS: 'no'`, a client secret checked into `setup_configuration/data.yaml`. A real
@@ -188,7 +188,7 @@ app change.
Django migrations then `setup_configuration` against `setup_configuration/data.yaml`), and
`web` (the OpenZaak API on `:8000`). Pinned to `openzaak/open-zaak:1.29.1`. No
celery/celery-beat/celery-flower/nginx — trimmed for a lean, fast-booting harness; layer
`docker-compose.openzaak.notificaties.yml` (WP-58) on top for a real async notification
`docker-compose.openzaak.notificaties.yml` on top for a real async notification
delivery round-trip.
`NOTIFICATIONS_DISABLED=true` is required, not optional: without it, OpenZaak 500s (and
**rolls back the whole create**) on any notified resource — see the compose file's comment.
@@ -197,14 +197,14 @@ app change.
one `bigregister-test` client with `heeft_alle_autorisaties: false` — this YAML mechanism
(`vng_api_common`'s `ApplicatieConfigurationModel`) has no field for granular scopes at all,
so the client starts with zero Autorisaties; `bootstrap-catalogus.sh` grants the exact ones
it needs (WP-57).
it needs.
- `bootstrap-catalogus.sh` — the business content (catalogus/zaaktype/zaak/…) `setup_configuration`
has no YAML for; every field value here was checked against OpenZaak's own OpenAPI spec and a
live run of this exact script, not guessed (two OpenZaak quirks it works around: a zaaktype
needs ≥1 resultaattype and 2 statustypen before it can be published, and its
`selectielijstklasse` and the zaaktype's `selectielijstProcestype` must reference the same
`procesType` on the public VNG selectielijst API). Idempotent (WP-56) — see "Bring it up" above.
Also grants `bigregister-test`'s Autorisaties via `manage.py shell` (WP-57, see the script's
`procesType` on the public VNG selectielijst API). Idempotent — see "Bring it up" above.
Also grants `bigregister-test`'s Autorisaties via `manage.py shell` (see the script's
top comment): `ztc` scopes (`catalogi.lezen`/`catalogi.schrijven`, this script's own
content-creation needs) up front, `zrc` scopes (`zaken.aanmaken`/`zaken.bijwerken`/
`zaken.lezen`, scoped to the one zaaktype the BFF and this script both use) once that
@@ -213,26 +213,26 @@ app change.
grant (scoped to a real `informatieobjecttype`, which this script would also need to seed)
when a later WP wires DRC content into this harness.
- **Not here**: Documenten (DRC) content, or a real Notificaties API (NRC) — add DRC content if a
later WP needs to prove that round-trip against a live instance too (WP-51 is fixture-tested
later change needs to prove that round-trip against a live instance too (fixture-tested
today). A real NRC is a separate application (`open-notificaties`) this harness deliberately
doesn't stand up — WP-58's notifications-enabled profile (below) proves live delivery without
doesn't stand up — the notifications-enabled profile (below) proves live delivery without
one, since this harness only ever has one subscriber.
- `docker-compose.openzaak.notificaties.yml` (WP-58) — opt-in overlay: one celery worker for
- `docker-compose.openzaak.notificaties.yml` — opt-in overlay: one celery worker for
OpenZaak (async notification delivery needs it) + `NOTIFICATIONS_DISABLED: 'false'`, joined to
the repo root's own compose network so it can reach the `api` container by name (tried
`host.docker.internal:host-gateway` first; this environment's rootless Podman doesn't route
container→host-port traffic through it). See "Notifications-enabled profile" below.
- `bootstrap-notificaties.sh` (WP-58) — points OpenZaak's `NotificationsConfig` at the BFF's
- `bootstrap-notificaties.sh` — points OpenZaak's `NotificationsConfig` at the BFF's
webhook via a `zgw_consumers.Service` (`update_or_create`, idempotent) instead of provisioning
a real NRC `abonnement`; preflights that the BFF is reachable with the right secret first
(a misconfigured target here means every write to a notified resource 500s and rolls back).
- `verify-notificatie.sh` (WP-58) — the runnable end-to-end check: PATCHes the seeded zaak, polls
the BFF's own `/admin/audit` (WP-41) for the resulting `zgw:notificatie`/`allow` row.
- `docker-compose.openzaak.prod.yml` (WP-55) — production overrides layered on top of
- `verify-notificatie.sh` — the runnable end-to-end check: PATCHes the seeded zaak, polls
the BFF's own `/admin/audit` for the resulting `zgw:notificatie`/`allow` row.
- `docker-compose.openzaak.prod.yml` — production overrides layered on top of
`docker-compose.openzaak.yml`: real `SECRET_KEY`/DB password/site domain/allowed-hosts from
required env vars (fails fast if unset), password DB auth instead of `trust`, `IS_HTTPS: 'yes'`.
Adds no image/service of its own — see "Production" above for the full flow.
- `setup_configuration/data.prod.yaml.template` (WP-55) — the prod counterpart of `data.yaml`
- `setup_configuration/data.prod.yaml.template` — the prod counterpart of `data.yaml`
with no secret in it (`${OPENZAAK_CLIENT_SECRET}` etc. as placeholders); `render-prod-secrets.sh`
fills it in to the gitignored `data.prod.yaml`, which the prod compose override mounts over
the container's `data.yaml`.
+4 -4
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# WP-54 (seeding) / WP-56 (idempotency) — seeds business content
# Seeds business content
# (catalogus/zaaktype/statustype/roltype/zaak/status/rol) into the OpenZaak harness started
# by docker-compose.openzaak.yml. `setup_configuration/data.yaml` only covers infra config
# (JWTSecret + Applicatie) — confirmed by reading the installed `django_setup_configuration`
@@ -17,7 +17,7 @@
# zaak's `identificatie` + `url` on success; also writes them to seeded.env (repo-ignored) for
# OpenZaakIntegrationTests.cs to assert against.
#
# WP-57: `bigregister-test` starts with ZERO Autorisaties (data.yaml sets
# `bigregister-test` starts with ZERO Autorisaties (data.yaml sets
# heeft_alle_autorisaties: false) — the setup_configuration YAML has no field for granular
# scopes at all (confirmed from vng_api_common's own ApplicatieConfigurationModel), so this
# script grants them itself via `manage.py shell` (Django ORM, inside the `web` container) at
@@ -71,7 +71,7 @@ oz() {
echo "$json"
}
# Grant (replace) an Autorisatie for $CLIENT_ID directly via the ORM (see the WP-57 note up
# Grant (replace) an Autorisatie for $CLIENT_ID directly via the ORM (see the note up
# top for why this bypasses the REST Autorisaties API). $1 = component, $2 = python list
# literal of scopes, $3.. = extra `Autorisatie(...)` kwargs as `name=value` (value already a
# valid Python literal, e.g. a quoted URL).
@@ -168,7 +168,7 @@ print(json.dumps({
echo " created: $zaaktype_url"
fi
echo "Granting zrc scopes (zaken.aanmaken, zaken.bijwerken, zaken.lezen, zaken.statussen.toevoegen), scoped to $zaaktype_url — the one zaaktype this harness (and the BFF's Zgw:ZaaktypeUrls config) ever uses. zaken.statussen.toevoegen is needed for WP-66's besluit write: zaken.aanmaken only covers the ONE status set at zaak creation, a later status (the besluit's eindstatus) needs this scope or OpenZaak 403s ('mag je slechts 1 status zetten')..."
echo "Granting zrc scopes (zaken.aanmaken, zaken.bijwerken, zaken.lezen, zaken.statussen.toevoegen), scoped to $zaaktype_url — the one zaaktype this harness (and the BFF's Zgw:ZaaktypeUrls config) ever uses. zaken.statussen.toevoegen is needed for the besluit write: zaken.aanmaken only covers the ONE status set at zaak creation, a later status (the besluit's eindstatus) needs this scope or OpenZaak 403s ('mag je slechts 1 status zetten')..."
grant_scopes zrc '["zaken.aanmaken", "zaken.bijwerken", "zaken.lezen", "zaken.statussen.toevoegen"]' \
"zaaktype=\"$zaaktype_url\"" \
'max_vertrouwelijkheidaanduiding="openbaar"'
+3 -3
View File
@@ -1,6 +1,6 @@
#!/usr/bin/env bash
# WP-58 — points OpenZaak's own NotificationsConfig straight at this repo's BFF webhook
# (POST /api/v1/zgw/notificaties, WP-52) instead of standing up a real Notificaties API (NRC)
# Points OpenZaak's own NotificationsConfig straight at this repo's BFF webhook
# (POST /api/v1/zgw/notificaties) instead of standing up a real Notificaties API (NRC)
# + abonnement — see docker-compose.openzaak.notificaties.yml's ponytail note for why. Requires
# that overlay running (adds the celery worker + flips NOTIFICATIONS_DISABLED) AND the repo
# root's own `docker compose up` running (the overlay joins its `api` container's network —
@@ -41,7 +41,7 @@ from zgw_consumers.models import Service
service, _ = Service.objects.update_or_create(
slug="bff-webhook",
defaults=dict(
label="BIG-register BFF webhook (WP-58)",
label="BIG-register BFF webhook",
api_type=APITypes.orc,
api_root="$BFF_API_ROOT",
auth_type=AuthTypes.api_key,
@@ -13,7 +13,7 @@
#
# 1. Why container-to-container instead of `http://localhost:8000`: this dev environment's
# rootless Podman drops container→host-port traffic through `host.docker.internal`
# (confirmed for the WP-58 notifications overlay's celery worker — DNS resolves it, every
# (confirmed for the notifications overlay's celery worker — DNS resolves it, every
# TCP connect times out).
#
# 2. Why the ROOT project's `api` joins INTO this project's network (below), not the other way
@@ -1,9 +1,9 @@
# WP-58 — notifications-enabled overlay, layered ON TOP of docker-compose.openzaak.yml
# Notifications-enabled overlay, layered ON TOP of docker-compose.openzaak.yml
# (never alone):
#
# docker compose -f docker-compose.openzaak.yml -f docker-compose.openzaak.notificaties.yml up -d
#
# The base file stays the WP-54 fast-iteration default (NOTIFICATIONS_DISABLED=true, no
# The base file stays the fast-iteration default (NOTIFICATIONS_DISABLED=true, no
# worker) so nobody testing the read/write seam has to pull/boot this. This overlay flips
# NOTIFICATIONS_DISABLED off and adds the one celery worker needed to actually deliver a
# notification (see base file's ponytail note).
@@ -14,7 +14,7 @@
# bootstrap-notificaties.sh points OpenZaak's NotificationsConfig straight at the BFF's webhook
# instead — same delivery proof (a real write → a real HTTP POST → the BFF's audit trail), far
# less harness to stand up and keep alive. Add a real NRC (+ abonnement/kanaal routing) if a
# later WP needs more than one subscriber or real kanaal-filtered fan-out.
# later change needs more than one subscriber or real kanaal-filtered fan-out.
#
# No celery-beat here: send_notification is a plain async task (client.post on save), not a
# scheduled one — beat only matters on a real NRC's polling side, which this harness doesn't have.
@@ -1,4 +1,4 @@
# WP-55 — production overrides for docker-compose.openzaak.yml: real secrets, real DB auth,
# Production overrides for docker-compose.openzaak.yml: real secrets, real DB auth,
# HTTPS-aware settings. Use ON TOP of the base file, never alone (it has no image/ports of its
# own to add — see backend/openzaak/README.md for the required env vars and full flow):
#
+3 -3
View File
@@ -1,12 +1,12 @@
# WP-54 — a real OpenZaak to develop/test the ZGW seam against, kept OUT of the root
# A real OpenZaak to develop/test the ZGW seam against, kept OUT of the root
# docker-compose.yml on purpose (see backend/openzaak/README.md): OpenZaak is a full Django
# stack (postgres + redis), heavy compared to this repo's own FE+BFF, and nobody who isn't
# touching the ZGW slice should have to pull/boot it.
#
# ponytail: trimmed vs. open-zaak's own published compose — no celery/celery-beat/celery-flower
# (async notification delivery, never asserted by the integration test) and no nginx (the test
# hits web's port directly). Add them back only if a later WP needs an actual notification
# round-trip against this harness (NRC delivery is already covered by fixture tests, WP-52).
# hits web's port directly). Add them back only if a later change needs an actual notification
# round-trip against this harness (NRC delivery is already covered by fixture tests).
services:
db:
image: postgis/postgis:17-3.5
+1 -1
View File
@@ -1,5 +1,5 @@
#!/usr/bin/env bash
# WP-55 — renders setup_configuration/data.prod.yaml.template into the gitignored
# Renders setup_configuration/data.prod.yaml.template into the gitignored
# data.prod.yaml docker-compose.openzaak.prod.yml mounts over the container's data.yaml.
# Run this once before `docker compose ... up` in a production deploy; re-run whenever the
# secrets rotate. Fails fast (no output file) if a required env var is missing — never
@@ -1,10 +1,10 @@
# Prod counterpart of data.yaml (WP-54's dev-only version, kept as-is for local iteration —
# Prod counterpart of data.yaml (the dev-only version, kept as-is for local iteration —
# see docker-compose.openzaak.yml's own comment on why it hardcodes a client secret). This
# template has no secret in it; render-prod-secrets.sh substitutes OPENZAAK_CLIENT_SECRET
# into it to produce the gitignored data.prod.yaml that docker-compose.openzaak.prod.yml
# mounts over the container's data.yaml.
#
# Least-privilege client scopes (WP-57): heeft_alle_autorisaties is false, matching the dev
# Least-privilege client scopes: heeft_alle_autorisaties is false, matching the dev
# harness (setup_configuration has no YAML field for granular `autorisaties` — see
# data.yaml's comment). This template only covers infra config; a real deploy must grant this
# client's Autorisaties the same way bootstrap-catalogus.sh does for the dev harness — via
@@ -2,7 +2,7 @@
# documented CLI config mechanism — see docker-compose.openzaak.yml) instead of the Django
# admin. Creates the ONE application the bootstrap script + integration test authenticate as.
#
# heeft_alle_autorisaties is false (WP-57, least privilege) — but
# heeft_alle_autorisaties is false (least privilege) — but
# `ApplicatieConfigurationModel` (vng_api_common's setup_configuration step) has no field for
# granular `autorisaties` at all, only this boolean. So this client starts with ZERO scopes;
# bootstrap-catalogus.sh grants the exact ones it needs via `manage.py shell` (Django ORM,
@@ -12,7 +12,7 @@ sites_config_enable: true
sites_config:
items:
- domain: localhost:8000
name: OpenZaak (WP-54 harness)
name: OpenZaak (harness)
vng_api_common_credentials_config_enable: true
vng_api_common_credentials:
@@ -26,5 +26,5 @@ vng_api_common_applicaties:
- uuid: 5a09b3c9-6a54-4b2b-8f3c-1f9b6b6a3a01
client_ids:
- bigregister-test
label: BIG-register BFF (WP-54 test harness)
label: BIG-register BFF (test harness)
heeft_alle_autorisaties: false
+3 -3
View File
@@ -1,7 +1,7 @@
#!/usr/bin/env bash
# WP-58 — proves the "real write -> real webhook delivery" round-trip end-to-end: PATCHes the
# Proves the "real write -> real webhook delivery" round-trip end-to-end: PATCHes the
# zaak bootstrap-catalogus.sh seeded (a notified ZRC resource), then polls the BFF's own audit
# trail (WP-41) for the resulting `zgw:notificatie` row. Requires bootstrap-catalogus.sh and
# trail for the resulting `zgw:notificatie` row. Requires bootstrap-catalogus.sh and
# bootstrap-notificaties.sh to have already run.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
@@ -29,7 +29,7 @@ jwt() {
printf '%s.%s' "$signing_input" "$sig"
}
echo "Triggering a real write: PATCH $ZAAK_URL (bijwerken — WP-57 granted zaken.aanmaken"
echo "Triggering a real write: PATCH $ZAAK_URL (bijwerken — the client is granted zaken.aanmaken"
echo "for exactly ONE status, so a second status create 403s; a zaak update is the write this"
echo "client's narrowed scope can repeat)..."
response=$(curl -sS -X PATCH -H "Authorization: Bearer $(jwt)" -H 'Content-Type: application/json' \