#!/usr/bin/env bash # WP-54 (seeding) / WP-56 (idempotency) — 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` # steps inside the `openzaak/open-zaak` image itself: the only app-registered step besides the # generic sites/credentials/applicaties ones is Selectielijst API config. There is NO # declarative-YAML equivalent upstream for Catalogi/Zaken content, so this script does it the # same way the BFF itself does at runtime — plain REST calls with a hand-rolled HS256 JWT (see # ZgwTokenProvider.cs, mirrored here in bash+openssl so this script has no extra dependency # beyond curl/openssl/python3, python3 already required by the JSON bodies below). # # Idempotent: every resource is looked up by its natural key (GET with the same filter OpenZaak # enforces uniqueness/identity on) before creating it, so re-running against an # already-seeded instance reuses what's there instead of erroring or duplicating. Safe to run # repeatedly against a long-lived instance, not just once per fresh volume. Prints the seeded # 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 # 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 # the two points they become grantable: ztc scopes up front (no zaaktype dependency), zrc # scopes once `zaaktype_url` exists below. Going through the ORM instead of the # JWT-authenticated Autorisaties REST API sidesteps a real chicken-and-egg: a client with zero # scopes cannot grant itself any scope over that API. Re-running this script re-grants the same # scopes (idempotent, like everything else here). set -euo pipefail cd "$(dirname "${BASH_SOURCE[0]}")" BASE="http://localhost:8000" CLIENT_ID="bigregister-test" SECRET="bigregister-test-secret" RSIN="123443210" # elfproef-valid RSIN, already used as the fixture Bronorganisatie # in OpenZaakZaakSourceTests.cs — reused here for consistency. BSN="111222333" # elfproef-valid BSN, already used as the fixture caller BSN. ZAAK_REF="BIG-2026-000123" b64url() { openssl base64 -A | tr '+/' '-_' | tr -d '='; } jwt() { local header='{"alg":"HS256","typ":"JWT"}' local payload payload=$(printf '{"iss":"%s","iat":%d,"client_id":"%s","user_id":"%s","user_representation":"%s"}' \ "$CLIENT_ID" "$(date +%s)" "$CLIENT_ID" "$CLIENT_ID" "bootstrap") local h p signing_input sig h=$(printf '%s' "$header" | b64url) p=$(printf '%s' "$payload" | b64url) signing_input="$h.$p" sig=$(printf '%s' "$signing_input" | openssl dgst -sha256 -hmac "$SECRET" -binary | b64url) printf '%s.%s' "$signing_input" "$sig" } # $1 = method, $2 = path, $3 = JSON body (optional) oz() { local method="$1" path="$2" body="${3:-}" # Content-Crs/Accept-Crs: every ZGW write must declare a coordinate reference system even # when no geometry is involved — OpenZaak 412s without it. local args=(-sS -X "$method" -H "Authorization: Bearer $(jwt)" -H "Content-Type: application/json" \ -H "Content-Crs: EPSG:4326" -H "Accept-Crs: EPSG:4326") [ -n "$body" ] && args+=(-d "$body") local response response=$(curl "${args[@]}" -w $'\n%{http_code}' "$BASE$path") local http_code="${response##*$'\n'}" local json="${response%$'\n'*}" if [[ ! "$http_code" =~ ^2 ]]; then echo "FAILED $method $path -> $http_code: $json" >&2 exit 1 fi echo "$json" } # Grant (replace) an Autorisatie for $CLIENT_ID directly via the ORM (see the WP-57 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). grant_scopes() { local component="$1" scopes="$2" shift 2 local extra="" kv for kv in "$@"; do extra+=" $kv,"$'\n'; done docker compose -f docker-compose.openzaak.yml exec -T --workdir /app/src web python manage.py shell </dev/null echo " created" fi echo "Roltype (initiator)..." roltype_url=$(existing_url "/catalogi/api/v1/roltypen?zaaktype=$zaaktype_url&omschrijvingGeneriek=initiator") if [ -n "$roltype_url" ]; then echo " exists: $roltype_url" else roltype_url=$(oz POST /catalogi/api/v1/roltypen "$(printf '{"zaaktype":"%s","omschrijving":"Initiator","omschrijvingGeneriek":"initiator"}' "$zaaktype_url")" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])') echo " created: $roltype_url" fi echo "Publishing zaaktype..." zaaktype_uuid=$(echo "$zaaktype_url" | sed 's#.*/##') zaaktype_concept=$(oz GET "/catalogi/api/v1/zaaktypen/$zaaktype_uuid" | python3 -c 'import json,sys; print(json.load(sys.stdin)["concept"])') if [ "$zaaktype_concept" = "False" ]; then echo " already published" else oz POST "/catalogi/api/v1/zaaktypen/$zaaktype_uuid/publish" >/dev/null echo " published" fi echo "Zaak..." zaak_url=$(existing_url "/zaken/api/v1/zaken?identificatie=$ZAAK_REF") if [ -n "$zaak_url" ]; then echo " exists: $zaak_url" else zaak=$(oz POST /zaken/api/v1/zaken "$(python3 -c ' import json, sys print(json.dumps({ "zaaktype": sys.argv[1], "bronorganisatie": sys.argv[2], "verantwoordelijkeOrganisatie": sys.argv[2], "startdatum": "2026-07-28", "identificatie": sys.argv[3], })) ' "$zaaktype_url" "$RSIN" "$ZAAK_REF")") zaak_url=$(echo "$zaak" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])') echo " created: $zaak_url" fi echo "Status..." if [ "$(oz GET "/zaken/api/v1/statussen?zaak=$zaak_url" | python3 -c 'import json,sys; print(len(json.load(sys.stdin)["results"]))')" != "0" ]; then echo " exists" else oz POST /zaken/api/v1/statussen "$(python3 -c ' import json, sys print(json.dumps({"zaak": sys.argv[1], "statustype": sys.argv[2], "datumStatusGezet": "2026-07-28T12:00:00Z"})) ' "$zaak_url" "$statustype_url")" >/dev/null echo " created" fi echo "Rol (initiator, seeded BSN)..." if [ -n "$(existing_url "/zaken/api/v1/rollen?zaak=$zaak_url&omschrijvingGeneriek=initiator")" ]; then echo " exists" else oz POST /zaken/api/v1/rollen "$(python3 -c ' import json, sys print(json.dumps({ "zaak": sys.argv[1], "betrokkeneType": "natuurlijk_persoon", "roltype": sys.argv[2], "roltoelichting": "Initiator", "betrokkeneIdentificatie": {"inpBsn": sys.argv[3]}, })) ' "$zaak_url" "$roltype_url" "$BSN")" >/dev/null echo " created" fi cat > seeded.env <