Files
atomic-design-poc/backend/openzaak/verify-notificatie.sh
T
ehoandClaude Sonnet 5 3e983bd2cc feat(openzaak): real notification delivery to the BFF webhook (WP-58)
OpenZaak doesn't serve the Notificaties API itself (it's a separate app,
open-notificaties) — standing one up for a real abonnement would triple
this harness for a benefit it doesn't need (exactly one subscriber, this
repo's own BFF). Instead, an opt-in compose overlay adds a celery worker
and points OpenZaak's NotificationsConfig straight at the BFF's webhook
via a zgw_consumers Service; bootstrap-notificaties.sh configures it
idempotently and verify-notificatie.sh proves a real write delivers to
the BFF's audit trail end-to-end.

Verified live: preflight proves the webhook's shared-secret gate both
ways (204/401), a zaak PATCH triggers real celery delivery, and rerunning
both scripts against an already-configured harness stays idempotent.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-07-30 15:33:16 +02:00

64 lines
2.7 KiB
Bash
Executable File

#!/usr/bin/env bash
# WP-58 — proves the "real write -> real webhook delivery" round-trip end-to-end: PATCHes the
# zaak bootstrap-catalogus.sh seeded (a notified ZRC resource), then polls the BFF's own audit
# trail (WP-41) for the resulting `zgw:notificatie` row. Requires bootstrap-catalogus.sh and
# bootstrap-notificaties.sh to have already run.
set -euo pipefail
cd "$(dirname "${BASH_SOURCE[0]}")"
[ -f seeded.env ] || { echo "seeded.env missing — run ./bootstrap-catalogus.sh first" >&2; exit 1; }
# Not `source`d: seeded.env's ZAAKTYPE_LABEL value contains an unquoted space (fine for the
# line-oriented C# reader it's written for, not valid as sourceable shell).
ZAAK_URL=$(grep '^ZAAK_URL=' seeded.env | cut -d= -f2-)
BFF_BASE="${BFF_BASE:-http://localhost:5000}"
CLIENT_ID="bigregister-test"
SECRET="bigregister-test-secret"
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" "verify")
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"
}
echo "Triggering a real write: PATCH $ZAAK_URL (bijwerken — WP-57 granted zaken.aanmaken"
echo "for exactly ONE status, so a second status create 403s; a zaak update is the write this"
echo "client's narrowed scope can repeat)..."
response=$(curl -sS -X PATCH -H "Authorization: Bearer $(jwt)" -H 'Content-Type: application/json' \
-H 'Content-Crs: EPSG:4326' -H 'Accept-Crs: EPSG:4326' \
-d "$(printf '{"toelichting":"wp-58 verify %s"}' "$(date -u +%s)")" \
-w $'\n%{http_code}' "$ZAAK_URL")
http_code="${response##*$'\n'}"
if [[ ! "$http_code" =~ ^2 ]]; then
echo "FAILED: zaak PATCH -> $http_code: ${response%$'\n'*}" >&2
exit 1
fi
echo " updated"
echo "Waiting for the BFF's audit trail to show the delivered notification..."
for _ in $(seq 1 30); do
if curl -sS -H 'X-Role: admin' "$BFF_BASE/api/v1/admin/audit" \
| python3 -c "
import json, sys
rows = json.load(sys.stdin)
found = any(r['action'] == 'zgw:notificatie' and r['resource'] == '$ZAAK_URL' and r['decision'] == 'allow' for r in rows)
sys.exit(0 if found else 1)
"; then
echo " delivered: found a zgw:notificatie/allow row for $ZAAK_URL"
exit 0
fi
sleep 2
done
echo "FAILED: no delivered notification for $ZAAK_URL after 60s. Diagnostics:" >&2
docker compose -f docker-compose.openzaak.yml -f docker-compose.openzaak.notificaties.yml logs --tail=50 celery >&2
exit 1