feat(zgw): docker OpenZaak integration-test harness (WP-54)
Opt-in docker-compose (postgres+redis+OpenZaak, no celery/nginx) + bootstrap-catalogus.sh seed a real OpenZaak instance; OpenZaakIntegrationTests (Category=Integration, excluded from default dotnet test/CI) proves the ZGW seam against it for the first time. That live run caught a real bug: ZgwHttpClient never sent Content-Crs/Accept-Crs headers, so every write would 412 against a spec-compliant OpenZaak — fixed alongside the harness. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+164
@@ -0,0 +1,164 @@
|
||||
#!/usr/bin/env bash
|
||||
# WP-54 — 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 (the JWTSecret + Applicatie); Catalogi/Zaken content has no
|
||||
# declarative-YAML equivalent upstream, 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).
|
||||
#
|
||||
# Idempotent-ish: re-running creates duplicate catalogus/zaaktype rows (OpenZaak doesn't
|
||||
# dedupe by name) — meant to be run once per fresh `docker compose up`, not repeatedly against
|
||||
# a long-lived instance. Prints the seeded zaak's `identificatie` + `url` on success; also
|
||||
# writes them to seeded.env (repo-ignored) for OpenZaakIntegrationTests.cs to assert against.
|
||||
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"
|
||||
}
|
||||
|
||||
echo "Waiting for OpenZaak..."
|
||||
until curl -sS -o /dev/null -w '%{http_code}' "$BASE/catalogi/api/v1/catalogussen" | grep -q '^2\|^401\|^403'; do
|
||||
sleep 2
|
||||
done
|
||||
|
||||
echo "Creating catalogus..."
|
||||
catalogus=$(oz POST /catalogi/api/v1/catalogussen "$(printf '{"domein":"BIGR","rsin":"%s","contactpersoonBeheerNaam":"BIG Register"}' "$RSIN")")
|
||||
catalogus_url=$(echo "$catalogus" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])')
|
||||
echo " $catalogus_url"
|
||||
|
||||
echo "Creating zaaktype (concept)..."
|
||||
zaaktype=$(oz POST /catalogi/api/v1/zaaktypen "$(python3 -c '
|
||||
import json, sys
|
||||
print(json.dumps({
|
||||
"identificatie": "ZT-HERREG",
|
||||
"omschrijving": "Herregistratie arts",
|
||||
"vertrouwelijkheidaanduiding": "openbaar",
|
||||
"doel": "Herregistratie in het BIG-register",
|
||||
"aanleiding": "Aanvraag door de zorgverlener",
|
||||
"indicatieInternOfExtern": "extern",
|
||||
"handelingInitiator": "indienen",
|
||||
"onderwerp": "Herregistratie",
|
||||
"handelingBehandelaar": "behandelen",
|
||||
"doorlooptijd": "P30D",
|
||||
"opschortingEnAanhoudingMogelijk": False,
|
||||
"verlengingMogelijk": False,
|
||||
"publicatieIndicatie": False,
|
||||
"productenOfDiensten": ["https://example.com/producten/herregistratie"],
|
||||
"referentieproces": {"naam": "Herregistratie"},
|
||||
"verantwoordelijke": "CIBG",
|
||||
"catalogus": sys.argv[1],
|
||||
"beginGeldigheid": "2026-01-01",
|
||||
"versiedatum": "2026-01-01",
|
||||
"besluittypen": [],
|
||||
"gerelateerdeZaaktypen": [],
|
||||
# Must belong to the same procestype as the resultaattype selectielijstklasse below
|
||||
# (OpenZaak cross-checks this against the public VNG selectielijst API).
|
||||
"selectielijstProcestype": "https://selectielijst.openzaak.nl/api/v1/procestypen/e1b73b12-b2f6-4c4e-8929-94f84dd2a57d",
|
||||
}))
|
||||
' "$catalogus_url")")
|
||||
zaaktype_url=$(echo "$zaaktype" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])')
|
||||
echo " $zaaktype_url"
|
||||
|
||||
echo "Creating statustypen (publish needs a begin AND an end status)..."
|
||||
statustype=$(oz POST /catalogi/api/v1/statustypen "$(printf '{"zaaktype":"%s","omschrijving":"Ontvangen","volgnummer":1}' "$zaaktype_url")")
|
||||
echo " $(echo "$statustype" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])')"
|
||||
statustype_eind=$(oz POST /catalogi/api/v1/statustypen "$(printf '{"zaaktype":"%s","omschrijving":"Afgehandeld","volgnummer":2}' "$zaaktype_url")")
|
||||
echo " $(echo "$statustype_eind" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])')"
|
||||
|
||||
echo "Creating resultaattype (publish needs at least one)..."
|
||||
# The two URLs below are real reference-list entries on the public VNG selectielijst API
|
||||
# (selectielijst.openzaak.nl) — OpenZaak validates both by fetching them, same as it does
|
||||
# for a zaaktype URL, so a made-up URL 404s here.
|
||||
resultaattype=$(oz POST /catalogi/api/v1/resultaattypen "$(printf '{"zaaktype":"%s","omschrijving":"Afgehandeld","resultaattypeomschrijving":"https://selectielijst.openzaak.nl/api/v1/resultaattypeomschrijvingen/7cb315fb-4f7b-4a43-aca1-e4522e4c73b3","selectielijstklasse":"https://selectielijst.openzaak.nl/api/v1/resultaten/cc5ae4e3-a9e6-4386-bcee-46be4986a829","archiefnominatie":"blijvend_bewaren"}' "$zaaktype_url")")
|
||||
echo " $(echo "$resultaattype" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])')"
|
||||
|
||||
echo "Creating roltype (initiator)..."
|
||||
roltype=$(oz POST /catalogi/api/v1/roltypen "$(printf '{"zaaktype":"%s","omschrijving":"Initiator","omschrijvingGeneriek":"initiator"}' "$zaaktype_url")")
|
||||
echo " $(echo "$roltype" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])')"
|
||||
|
||||
echo "Publishing zaaktype..."
|
||||
zaaktype_uuid=$(echo "$zaaktype_url" | sed 's#.*/##')
|
||||
oz POST "/catalogi/api/v1/zaaktypen/$zaaktype_uuid/publish" >/dev/null
|
||||
|
||||
echo "Creating zaak..."
|
||||
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 " $zaak_url"
|
||||
|
||||
echo "Creating status..."
|
||||
statustype_url=$(echo "$statustype" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])')
|
||||
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 "Creating rol (initiator, seeded BSN)..."
|
||||
roltype_url=$(echo "$roltype" | python3 -c 'import json,sys; print(json.load(sys.stdin)["url"])')
|
||||
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
|
||||
|
||||
cat > seeded.env <<EOF
|
||||
ZAAK_REFERENTIE=$ZAAK_REF
|
||||
ZAAK_URL=$zaak_url
|
||||
ZAAKTYPE_LABEL=Herregistratie arts
|
||||
CALLER_BSN=$BSN
|
||||
EOF
|
||||
|
||||
echo
|
||||
echo "Seed complete. $ZAAK_REF ($zaak_url) — see seeded.env"
|
||||
Reference in New Issue
Block a user