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>
This commit is contained in:
eho
2026-07-30 15:33:16 +02:00
co-authored by Claude Sonnet 5
parent 1e87997ea0
commit 3e983bd2cc
8 changed files with 364 additions and 22 deletions
@@ -0,0 +1,64 @@
# WP-58 — notifications-enabled overlay, layered ON TOP of docker-compose.openzaak.yml
# (never alone):
#
# docker compose -f docker-compose.openzaak.yml -f docker-compose.openzaak.notificaties.yml up -d
#
# The base file stays the WP-54 fast-iteration default (NOTIFICATIONS_DISABLED=true, no
# worker) so nobody testing the read/write seam has to pull/boot this. This overlay flips
# NOTIFICATIONS_DISABLED off and adds the one celery worker needed to actually deliver a
# notification (see base file's ponytail note).
#
# ponytail: a real ZGW deployment fans notifications out through a separate Notificaties API
# (NRC — its own app/image/DB; OpenZaak does not serve one) to N abonnement'd subscribers via
# kanaal-filtered routing. This harness only ever has ONE subscriber (this repo's own BFF), so
# bootstrap-notificaties.sh points OpenZaak's NotificationsConfig straight at the BFF's webhook
# instead — same delivery proof (a real write → a real HTTP POST → the BFF's audit trail), far
# less harness to stand up and keep alive. Add a real NRC (+ abonnement/kanaal routing) if a
# later WP needs more than one subscriber or real kanaal-filtered fan-out.
#
# No celery-beat here: send_notification is a plain async task (client.post on save), not a
# scheduled one — beat only matters on a real NRC's polling side, which this harness doesn't have.
services:
web-init:
environment:
NOTIFICATIONS_DISABLED: 'false'
web:
environment:
NOTIFICATIONS_DISABLED: 'false'
celery:
image: openzaak/open-zaak:1.29.1
command: /celery_worker.sh
environment:
DJANGO_SETTINGS_MODULE: openzaak.conf.docker
SECRET_KEY: wp-54-local-harness-not-for-prod
DB_HOST: db
DB_NAME: openzaak
DB_USER: openzaak
IS_HTTPS: 'no'
SITE_DOMAIN: localhost:8000
ALLOWED_HOSTS: localhost,127.0.0.1,web
CACHE_DEFAULT: redis:6379/0
CACHE_AXES: redis:6379/0
DISABLE_2FA: 'true'
CELERY_BROKER_URL: redis://redis:6379/0
CELERY_RESULT_BACKEND: redis://redis:6379/0
NOTIFICATIONS_DISABLED: 'false'
# On the default network (below) for db/redis; also joined to the repo root's
# `docker compose up` network so it can reach the BFF's `api` container by name — tried
# `host.docker.internal:host-gateway` first, but rootless Podman here drops traffic from
# the container bridge to a host-bound port (confirmed: DNS resolves host.docker.internal,
# every TCP connect attempt times out), so container-to-container is the reliable path.
networks:
default: {}
bff: {}
depends_on:
web-init:
condition: service_completed_successfully
redis:
condition: service_healthy
networks:
bff:
name: atomic-design-poc_default
external: true