#!/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 ) <&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 <