make verify-notifications brings the stack up, seeds a published BIG zaaktype, and asserts a zaak-create notification is delivered to a webhook-sink abonnement. The sink + driver run as containers inside the compose network and reach OpenZaak/NRC by container IP (the runner can't reach published ports, and a single-label host isn't URL-valid). The sink enforces a bearer token because NRC refuses an unauthenticated callback. New 'notifications' Gitea Actions job runs it (Docker-only). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
91 lines
3.4 KiB
Python
Executable File
91 lines
3.4 KiB
Python
Executable File
#!/usr/bin/env python3
|
|
"""Drive the OpenZaak → NRC notification check from *inside* the compose network.
|
|
|
|
Registers an abonnement on the `zaken` kanaal pointing at a webhook sink, then
|
|
creates a zaak against the published BIG zaaktype. OpenZaak publishes a
|
|
`zaken`/`create` notification; NRC delivers it to the sink. The host harness
|
|
(infra/verify-notifications.sh) then asserts the sink received it.
|
|
|
|
Reached by container IP, not service name: OpenZaak/NRC validate URLs with Django's
|
|
URLValidator, which rejects a single-label host like `openzaak`. Stdlib only.
|
|
Env: OZ_BASE, NRC_BASE, SINK_CALLBACK, SINK_AUTH, OZ_CLIENT_ID, OZ_SECRET.
|
|
"""
|
|
import base64
|
|
import hashlib
|
|
import hmac
|
|
import json
|
|
import os
|
|
import sys
|
|
import time
|
|
import urllib.error
|
|
import urllib.request
|
|
|
|
OZ = os.environ["OZ_BASE"].rstrip("/")
|
|
NRC = os.environ["NRC_BASE"].rstrip("/")
|
|
SINK_CALLBACK = os.environ["SINK_CALLBACK"]
|
|
SINK_AUTH = os.environ.get("SINK_AUTH", "Bearer notification-sink-token")
|
|
CID = os.environ.get("OZ_CLIENT_ID", "big-reference-seed")
|
|
SECRET = os.environ.get("OZ_SECRET", "insecure-dev-secret-change-me")
|
|
RSIN = "517439943"
|
|
|
|
|
|
def token():
|
|
b64 = lambda b: base64.urlsafe_b64encode(b).rstrip(b"=")
|
|
seg = (
|
|
b64(json.dumps({"alg": "HS256", "typ": "JWT"}, separators=(",", ":")).encode())
|
|
+ b"."
|
|
+ b64(json.dumps(
|
|
{"iss": CID, "iat": int(time.time()), "client_id": CID,
|
|
"user_id": "verify", "user_representation": "verify"},
|
|
separators=(",", ":")).encode())
|
|
)
|
|
return (seg + b"." + b64(hmac.new(SECRET.encode(), seg, hashlib.sha256).digest())).decode()
|
|
|
|
|
|
def call(method, url, body=None, crs=False):
|
|
headers = {"Authorization": "Bearer " + token(),
|
|
"Content-Type": "application/json", "Accept": "application/json"}
|
|
if crs:
|
|
headers["Accept-Crs"] = "EPSG:4326"
|
|
headers["Content-Crs"] = "EPSG:4326"
|
|
data = json.dumps(body).encode() if body is not None else None
|
|
try:
|
|
with urllib.request.urlopen(
|
|
urllib.request.Request(url, data=data, method=method, headers=headers), timeout=30
|
|
) as r:
|
|
return r.status, json.loads(r.read() or "null")
|
|
except urllib.error.HTTPError as e:
|
|
return e.code, json.loads(e.read() or "null")
|
|
|
|
|
|
def main():
|
|
status, ab = call("POST", f"{NRC}/api/v1/abonnement", {
|
|
"callbackUrl": SINK_CALLBACK,
|
|
"auth": SINK_AUTH,
|
|
"kanalen": [{"naam": "zaken", "filters": {}}],
|
|
})
|
|
if status != 201:
|
|
sys.exit(f"create abonnement -> {status}: {json.dumps(ab)}")
|
|
print(f"abonnement: {ab['url']}")
|
|
|
|
status, body = call(
|
|
"GET", f"{OZ}/catalogi/api/v1/zaaktypen?identificatie=BIG-REGISTRATIE&status=definitief")
|
|
results = body.get("results", []) if status == 200 else []
|
|
if not results:
|
|
sys.exit("no published BIG-REGISTRATIE zaaktype — seed with OZ_PUBLISH=1 first")
|
|
zaaktype = results[0]["url"]
|
|
|
|
status, zaak = call("POST", f"{OZ}/zaken/api/v1/zaken", {
|
|
"bronorganisatie": RSIN, "verantwoordelijkeOrganisatie": RSIN,
|
|
"zaaktype": zaaktype, "startdatum": time.strftime("%Y-%m-%d"),
|
|
"vertrouwelijkheidaanduiding": "openbaar",
|
|
}, crs=True)
|
|
if status != 201:
|
|
sys.exit(f"create zaak -> {status}: {json.dumps(zaak)}")
|
|
# The harness greps the sink for this exact URL.
|
|
print(f"ZAAK_CREATED {zaak['url']}")
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|