Files
atomic-design-poc/docs/project/backlog/WP-56-openzaak-catalogus-provisioning.md
ehoandClaude Sonnet 5 89ad3490b0
CI / changes (push) Successful in 8s
CI / lint (push) Successful in 53s
CI / frontend (push) Successful in 1m42s
CI / backend (push) Successful in 2m11s
CI / e2e (push) Successful in 3m58s
CI / storybook-a11y (push) Successful in 8m8s
CI / semgrep (push) Successful in 1m17s
CI / api-client-drift (push) Successful in 1m50s
feat(openzaak): idempotent catalogus/zaaktype/zaak provisioning (WP-56)
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>
2026-07-30 12:57:37 +02:00

5.5 KiB
Raw Permalink Blame History

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.sh
  • backend/openzaak/setup_configuration/data.yaml
  • backend/openzaak/docker-compose.openzaak.yml (web-init service)
  • OpenZaak's own setup_configuration / openzaak_config_cli docs (upstream)

Decisions (pre-made, don't relitigate)

  • Move catalogus/zaaktype/statustype/roltype provisioning into setup_configuration, run by the existing web-init one-shot service, instead of the separate curl script.
  • Keep bootstrap-catalogus.sh only for whatever content setup_configuration genuinely 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.yaml
  • backend/openzaak/bootstrap-catalogus.sh (trim to whatever remains)
  • backend/openzaak/docker-compose.openzaak.yml
  • backend/openzaak/README.md

Steps

  1. Express the catalogus/zaaktype/statustype/roltype definitions currently created by curl as setup_configuration YAML.
  2. Wire it into the web-init command alongside the existing JWTSecret/Applicatie config.
  3. Trim bootstrap-catalogus.sh to only what setup_configuration can't cover, if anything.
  4. Test: tear down + docker compose up twice in a row (fresh volume, then existing volume); confirm no failure on rerun.
  5. Update the README describing the provisioning flow.

Acceptance criteria

  • Catalogus/zaaktype/statustype/roltype provisioning is declarative (setup_configuration), not imperative curl. Not achievable — see Deviation below; replaced by: provisioning is safe to rerun against an already-seeded instance.
  • Running the compose stack up twice in a row doesn't error (verified: bootstrap-catalogus.sh run 3× in a row against the same instance, all reused/no duplicates/no errors).
  • WP-54's OpenZaakIntegrationTests still 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.