bootstrap-catalogus.sh now looks up every resource by its natural key before creating it (catalogus by domein+rsin, zaaktype by catalogus+identificatie, statustype by zaaktype+volgnummer, roltype by zaaktype+omschrijvingGeneriek, zaaktype-publish by checking `concept` first, zaak by identificatie, status/rol by existence-under-the-zaak), so rerunning against an already-seeded instance reuses what's there instead of erroring. The WP's original plan (move this into OpenZaak's `setup_configuration` mechanism) turned out not to be achievable: reading the actual django_setup_configuration steps installed inside the open-zaak image shows no step exists for Catalogi/Zaken content anywhere in this OpenZaak version — only sites/credentials/applicaties/selectielijst. Documented as a deviation; the WP's own Risks section already anticipated this and sanctioned falling back to an idempotent script. Verified live: fresh instance -> full run (all created) -> integration test green -> reran the script twice more against the same instance (all reused, identical URLs, no duplicates) -> integration test still green. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
5.5 KiB
WP-56 — Idempotent catalogus provisioning
Status: done Phase: 10 — OpenZaak production hardening
Why
backend/openzaak/bootstrap-catalogus.sh seeds catalogus/zaaktype/statustype/roltype/zaak
via hand-rolled curl+JWT and is explicitly not idempotent (fails on domein+rsin
uniqueness on rerun) — fine for a one-shot WP-54 harness, wrong for an environment that
needs to be rebuildable. OpenZaak already ships a documented, scripted alternative — the
setup_configuration mechanism (already used in the harness for the JWTSecret/Applicatie,
see setup_configuration/data.yaml) — this WP extends that same mechanism to the catalogus
content too.
Read first
backend/openzaak/bootstrap-catalogus.shbackend/openzaak/setup_configuration/data.yamlbackend/openzaak/docker-compose.openzaak.yml(web-initservice)- OpenZaak's own
setup_configuration/openzaak_config_clidocs (upstream)
Decisions (pre-made, don't relitigate)
- Move catalogus/zaaktype/statustype/roltype provisioning into
setup_configuration, run by the existingweb-initone-shot service, instead of the separate curl script. - Keep
bootstrap-catalogus.shonly for whatever contentsetup_configurationgenuinely can't express (e.g. a demo zaak instance) — confirm what's left at kickoff. - Provisioning must be safe to run against an already-provisioned instance — either genuinely idempotent, or the compose is structured to only run it once per fresh volume (document which, don't leave it ambiguous).
Files
backend/openzaak/setup_configuration/data.yamlbackend/openzaak/bootstrap-catalogus.sh(trim to whatever remains)backend/openzaak/docker-compose.openzaak.ymlbackend/openzaak/README.md
Steps
- Express the catalogus/zaaktype/statustype/roltype definitions currently created by
curl as
setup_configurationYAML. - Wire it into the
web-initcommand alongside the existing JWTSecret/Applicatie config. - Trim
bootstrap-catalogus.shto only what setup_configuration can't cover, if anything. - Test: tear down +
docker compose uptwice in a row (fresh volume, then existing volume); confirm no failure on rerun. - Update the README describing the provisioning flow.
Acceptance criteria
Catalogus/zaaktype/statustype/roltype provisioning is declarative (Not achievable — see Deviation below; replaced by: provisioning is safe to rerun against an already-seeded instance.setup_configuration), not imperative curl.- Running the compose stack up twice in a row doesn't error (verified:
bootstrap-catalogus.shrun 3× in a row against the same instance, all reused/no duplicates/no errors). - WP-54's
OpenZaakIntegrationTestsstill pass unchanged (same content, different provisioning mechanism).
Deviation from the original plan
Confirmed by reading the django_setup_configuration steps actually installed inside the
openzaak/open-zaak:1.29.1 image (/app/src/openzaak/config/setup_configuration/steps/ +
every third-party contrib/setup_configuration/ package): the ONLY app-registered
configuration steps are sites/credentials/applicaties (already used by data.yaml) and
Selectielijst API config. There is no step for catalogus/zaaktype/statustype/roltype/zaak
content anywhere in this OpenZaak version — the WP's core premise ("move provisioning into
setup_configuration") is not achievable. This was explicitly anticipated by the WP's own Risks
section ("if a piece genuinely can't be expressed declaratively, keep it in a clearly-labeled
idempotent script rather than forcing a bad fit") and Decisions block ("either genuinely
idempotent, or the compose is structured to only run it once... document which").
Chose genuinely idempotent: bootstrap-catalogus.sh now looks up every resource by its
natural key before creating it (catalogus by domein+rsin, zaaktype by catalogus+
identificatie, statustype by zaaktype+volgnummer (no server-side volgnummer filter, so
listed by zaaktype and matched client-side), resultaattype by existence-only (publish just
needs ≥1), roltype by zaaktype+omschrijvingGeneriek, zaaktype-publish by checking concept
on the zaaktype detail first, zaak by identificatie, status/rol by existence-under-the-zaak).
Real gotcha hit and fixed: OpenZaak's query-parameter names are camelCase
(omschrijvingGeneriek), not the Django model's snake_case (omschrijving_generiek) the
filter is registered under internally — the snake_case form 400s with "Onbekende query
parameters" (unknown query parameter). Verified for real: fresh instance → full run (all
"created") → dotnet test --filter Category=Integration green → reran the script twice more
against the same live instance (all "exists", identical URLs each time, no duplicates) →
integration test still green.
Not attempted: moving anything into setup_configuration/data.yaml (nothing there to move,
per the finding above).
Verification
docker compose -f backend/openzaak/docker-compose.openzaak.yml up twice in a row (fresh
volume, then existing volume); cd backend && dotnet test --filter Category=Integration
against the harness.
Out of scope
Secrets/TLS (WP-55, unrelated but sequenced first in the table only by number), client scopes (WP-57), notifications (WP-58).
Risks
OpenZaak's setup_configuration coverage for zaaktype/besluittype content may be
incomplete upstream — if a piece genuinely can't be expressed declaratively, keep it in a
clearly-labeled idempotent script rather than forcing a bad fit.