feat(openzaak): one-command UI-OpenZaak bridge, real BSN fix, flake mitigation
CI / changes (push) Successful in 8s
CI / lint (push) Successful in 55s
CI / frontend (push) Successful in 1m33s
CI / backend (push) Successful in 1m46s
CI / e2e (push) Successful in 3m14s
CI / storybook-a11y (push) Successful in 6m50s
CI / semgrep (push) Successful in 1m13s
CI / api-client-drift (push) Successful in 1m47s
CI / changes (push) Successful in 8s
CI / lint (push) Successful in 55s
CI / frontend (push) Successful in 1m33s
CI / backend (push) Successful in 1m46s
CI / e2e (push) Successful in 3m14s
CI / storybook-a11y (push) Successful in 6m50s
CI / semgrep (push) Successful in 1m13s
CI / api-client-drift (push) Successful in 1m47s
scripts/openzaak-ui-up.sh brings up the root app and the OpenZaak harness together, wires them onto one docker network, seeds the catalogus, grants the container-alias zaaktype scope, and verifies a real aanvraag submitted through the UI lands in OpenZaak. Along the way: DocumentStore.DemoOwner was reusing the seeded doctor's 11-digit BIG-nummer as a stand-in BSN, which isn't a valid 9-digit BSN shape — OpenZaak rejects it, breaking both submit's rol-creation step and the citizen's own applications list under Zgw:Enabled=true. Fixed to a real elfproef-valid BSN. Also adds mitigation for a still-unexplained per-container flake (every outbound ZGW POST fails as if the body were empty, for that container's whole lifetime) that correlates with host memory pressure: the script now warns when host swap is heavily used, and an opt-in ZgwDiagnosticHandler (ZGW_DEBUG_HTTP=1) logs Content-Length vs. actual bytes sent so the next reproduction can confirm or rule out client-side body corruption. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
This commit is contained in:
Executable
+150
@@ -0,0 +1,150 @@
|
||||
#!/usr/bin/env bash
|
||||
# One command to test the Angular UI end-to-end against a real OpenZaak instead of the local
|
||||
# SQLite store: brings up the OpenZaak harness (backend/openzaak/), seeds its catalogus, wires
|
||||
# it onto the root app's docker network (docker-compose.openzaak.bff.yml /
|
||||
# docker-compose.openzaak.yml — see those files for the full "why", it's non-obvious), grants
|
||||
# the extra authorization scope the container alias needs, and brings the FE+BFF up pointed at
|
||||
# OpenZaak.
|
||||
#
|
||||
# Safe to re-run: every step this chains is already idempotent (bootstrap-catalogus.sh,
|
||||
# `docker compose up -d`, and the grant-replace below).
|
||||
#
|
||||
# Also confirmed empirically, many repeated trials: some freshly-(re)started `api`
|
||||
# containers have EVERY outbound ZGW POST fail with what looks like an empty body reaching
|
||||
# OpenZaak ("all fields required"), for that container's entire lifetime — while a plain curl
|
||||
# to the exact same URL, from inside the exact same container, never fails, even hammered in a
|
||||
# loop. Ruled out as the cause: HttpClient connection pooling settings, Expect-100-Continue,
|
||||
# content pre-buffering — none of it made a measurable difference. Best lead so far, and
|
||||
# reproduced live on this dev host (7.5/8GB swap from long-idle unrelated containers): this
|
||||
# correlates with the HOST being under heavy memory pressure — a heavier managed runtime
|
||||
# (dotnet's JIT + GC) is plausibly far more sensitive to that than a lightweight one-shot
|
||||
# `curl` process. If you hit this, try freeing host memory (stop unrelated containers) before
|
||||
# assuming it's a code regression. The preflight check and self-check below warn about and
|
||||
# retry around it either way; set ZGW_DEBUG_HTTP=1 on the `api` container (see
|
||||
# docker-compose.openzaak.yml) next time it reproduces to log Content-Length vs. actual bytes
|
||||
# sent, which would confirm (or rule out) client-side body corruption.
|
||||
set -euo pipefail
|
||||
cd "$(dirname "$0")/.."
|
||||
|
||||
step() { printf '\n\033[1;36m▶ %s\033[0m\n' "$1"; }
|
||||
|
||||
check_memory_pressure() {
|
||||
local total used pct
|
||||
read -r total used <<< "$(free -m | awk '/^Swap:/{print $2, $3}')"
|
||||
[ "${total:-0}" -gt 0 ] || return 0
|
||||
pct=$(( used * 100 / total ))
|
||||
if [ "$pct" -ge 50 ]; then
|
||||
echo "⚠ host swap ${pct}% used (${used}MiB/${total}MiB) — this correlates with the" >&2
|
||||
echo " known per-container ZGW flake (see comment up top). Consider 'docker ps' and" >&2
|
||||
echo " stopping unrelated long-running stacks before continuing." >&2
|
||||
fi
|
||||
}
|
||||
check_memory_pressure
|
||||
|
||||
step "root app (creates the docker network the OpenZaak harness joins below)"
|
||||
docker compose up -d
|
||||
|
||||
step "OpenZaak harness + bff overlay"
|
||||
( cd backend/openzaak && docker compose -f docker-compose.openzaak.yml -f docker-compose.openzaak.bff.yml up -d )
|
||||
|
||||
step "seed catalogus/zaaktype/zaak (idempotent)"
|
||||
bootstrap_output=$(cd backend/openzaak && ./bootstrap-catalogus.sh)
|
||||
echo "$bootstrap_output"
|
||||
|
||||
zaaktype_line=$(printf '%s\n' "$bootstrap_output" | grep -A1 '^Zaaktype (concept)\.\.\.$' | tail -n1)
|
||||
zaaktype_url=$(printf '%s\n' "$zaaktype_line" | sed -E 's/^ *(exists|created): //')
|
||||
zaaktype_uuid=$(printf '%s\n' "$zaaktype_url" | sed 's#.*/##')
|
||||
if [ -z "$zaaktype_uuid" ]; then
|
||||
echo "Could not find the seeded zaaktype's URL in bootstrap-catalogus.sh's output — see above." >&2
|
||||
exit 1
|
||||
fi
|
||||
# bootstrap-catalogus.sh queried (and granted scope) via http://localhost:8000 (run from the
|
||||
# host); the containerized BFF reaches the same resource via the `openzaak.local` alias
|
||||
# (docker-compose.openzaak.bff.yml) instead — only the UUID suffix is what actually matters.
|
||||
container_zaaktype_url="http://openzaak.local:8000/catalogi/api/v1/zaaktypen/${zaaktype_uuid}"
|
||||
export OPENZAAK_ZAAKTYPE_URL="$container_zaaktype_url"
|
||||
|
||||
step "grant the container-alias zaaktype scope (REPLACES bootstrap-catalogus.sh's own localhost-scoped grant, doesn't add to it — OpenZaak's own zaken-list authorization filter 500s with a RuntimeError, 'are you sure that all paths point to the same resource?', when an Applicatie has two zrc grants for what's really the same zaaktype under two different hostnames; confirmed empirically. One consequence: dotnet test --filter Category=Integration needs bootstrap-catalogus.sh rerun afterward to restore its localhost-scoped grant — it's idempotent, so that's a plain rerun, not a reset)"
|
||||
( cd backend/openzaak && docker compose -f docker-compose.openzaak.yml exec -T --workdir /app/src web python manage.py shell ) <<PY
|
||||
from vng_api_common.authorizations.models import Applicatie
|
||||
app = Applicatie.objects.get(client_ids__contains=["bigregister-test"])
|
||||
app.autorisaties.filter(component="zrc").delete()
|
||||
app.autorisaties.create(
|
||||
component="zrc",
|
||||
scopes=["zaken.aanmaken", "zaken.bijwerken", "zaken.lezen"],
|
||||
zaaktype="$container_zaaktype_url",
|
||||
max_vertrouwelijkheidaanduiding="openbaar",
|
||||
)
|
||||
print("granted:", "$container_zaaktype_url")
|
||||
PY
|
||||
|
||||
step "root app again (now pointed at OpenZaak)"
|
||||
docker compose -f docker-compose.yml -f docker-compose.openzaak.yml up -d
|
||||
|
||||
step "verify the BFF can actually reach OpenZaak (retries api if not — see the flake note up top)"
|
||||
wait_for_api() {
|
||||
# -f: a response has to actually be 2xx — dotnet run's own build/start (and, on a retry, a
|
||||
# full restart) can leave the port accepting-but-erroring for a few seconds first, which a
|
||||
# plain curl (no -f) would misread as "ready".
|
||||
for _ in $(seq 1 60); do
|
||||
curl -sS -f -m 2 -o /dev/null http://localhost:5000/api/v1/me && return 0
|
||||
sleep 1
|
||||
done
|
||||
return 1
|
||||
}
|
||||
# A throwaway herregistratie submit: does the resulting zaak actually show up in OpenZaak
|
||||
# (via the BFF's own /admin/cases)?
|
||||
zgw_write_reaches_openzaak() {
|
||||
local create id ref cases
|
||||
create=$(curl -sS -m 10 -X POST http://localhost:5000/api/v1/applications -H "Content-Type: application/json" -d '{"type":"herregistratie"}') || return 1
|
||||
id=$(printf '%s' "$create" | python3 -c 'import json,sys; print(json.load(sys.stdin)["id"])') || return 1
|
||||
ref=$(curl -sS -m 15 -X POST "http://localhost:5000/api/v1/applications/$id/submit" -H "Content-Type: application/json" -d '{"uren":1}' | python3 -c 'import json,sys; print(json.load(sys.stdin)["referentie"])') || return 1
|
||||
cases=$(curl -sS -m 10 -H "X-Role: admin" http://localhost:5000/api/v1/admin/cases)
|
||||
printf '%s' "$cases" | python3 -c "
|
||||
import json, sys
|
||||
refs = [c['status']['referentie'] for c in json.load(sys.stdin)]
|
||||
sys.exit(0 if '$ref' in refs else 1)
|
||||
"
|
||||
}
|
||||
verified=false
|
||||
attempts=5
|
||||
for attempt in $(seq 1 "$attempts"); do
|
||||
wait_for_api || { echo "api never came up listening — see 'docker logs atomic-design-poc-api-1'." >&2; break; }
|
||||
if zgw_write_reaches_openzaak; then
|
||||
verified=true
|
||||
break
|
||||
fi
|
||||
echo " attempt $attempt/$attempts: a write didn't reach OpenZaak — restarting api and giving the network a moment to settle (known flake, see comment up top)..."
|
||||
sleep 3
|
||||
docker compose -f docker-compose.yml -f docker-compose.openzaak.yml restart api
|
||||
done
|
||||
if [ "$verified" = true ]; then
|
||||
echo " verified: a write reaches OpenZaak."
|
||||
else
|
||||
check_memory_pressure
|
||||
echo " Could not verify after $attempts tries. This is the known flake, not necessarily a real failure —" >&2
|
||||
echo " keep retrying by hand: 'docker compose -f docker-compose.yml -f docker-compose.openzaak.yml restart api'," >&2
|
||||
echo " wait for 'Application started' in 'docker logs -f atomic-design-poc-api-1', then try the UI again." >&2
|
||||
fi
|
||||
|
||||
printf '\n\033[1;32m✔ up\033[0m\n'
|
||||
cat <<EOF
|
||||
|
||||
App: http://localhost:4200
|
||||
Admin cases (proof an aanvraag landed in OpenZaak): http://localhost:4200/beheer/zaken?role=admin
|
||||
Audit trail (should show no zgw:divergence rows): http://localhost:4200/beheer/audit?role=admin
|
||||
OpenZaak directly: http://localhost:8000
|
||||
|
||||
Only "herregistratie" has a seeded zaaktype in this harness — submit that wizard to see a real
|
||||
write land in OpenZaak. registratie/intake submissions still succeed locally but their ZGW
|
||||
write is flagged (Aanvraag.ZgwError / a zgw:divergence audit row), not surfaced as a UI error.
|
||||
Document uploads also won't register in ZGW (no Documenten content seeded) — pick "per post"
|
||||
in the wizard's document step to keep the demo clean, or ignore it, it's non-fatal.
|
||||
|
||||
Tear down (plain "down", no -f overlay needed — overlay files only add env/network config to
|
||||
services docker-compose.yml already defines, and the required-var syntax in
|
||||
docker-compose.openzaak.yml would otherwise block "down" too once OPENZAAK_ZAAKTYPE_URL falls
|
||||
out of your shell):
|
||||
docker compose down
|
||||
( cd backend/openzaak && docker compose -f docker-compose.openzaak.yml down -v )
|
||||
EOF
|
||||
Reference in New Issue
Block a user