feat(domain): withdrawal cancels the registratie process (S-11b, refs #12) (#89)
All checks were successful
CI / lint (push) Successful in 1m18s
CI / build (push) Successful in 57s
CI / unit (push) Successful in 1m10s
CI / frontend (push) Successful in 2m38s
CI / mutation (push) Successful in 5m22s
CI / verify-stack (push) Successful in 7m18s

## What & why

Second sub-slice of **S-11 · Withdrawal (Flow 3)** (#12). S-11a (#88) made a withdrawal advance the aggregate to INGETROKKEN; this sub-slice **cancels the running Flowable process** so the withdrawn case leaves the behandelaar's werkbak.

- **BPMN** (`registratie.bpmn`): an interrupting message boundary event (`RegistratieIngetrokken`) on the `Beoordelen` task, routing to a dedicated "Registratie ingetrokken" end event.
- **Workflow Client**: `WithdrawBeoordelingAsync(executionId)` delivers `messageEventReceived` to the task's execution (PUT); `BeoordelingTask` now carries its `executionId`.
- **`WithdrawRegistration` handler**: after the domain transition, finds the open `Beoordelen` task for the registration and delivers the withdrawal message — best-effort, mirroring how the beoordeling completes its task.
- **Werkbak**: also filters out registrations that are no longer open, so a withdrawn case never surfaces even in the brief window before cancellation lands.
- **ADR-0014** records the decision (message event in BPMN vs. deleting the instance from code).
- **verify (`run-domain-check.sh`)**: a second registration parks at `Beoordelen`, is withdrawn via the domain, and the check asserts its `Beoordelen` task disappears — so verify-stack validates the live Flowable message correlation.

Refs #12 (S-11c — the BFF + self-service "trek aanvraag in" button + e2e — closes it).

## Definition of Done

- [x] Linked Gitea issue (#12).
- [x] Failing tests committed before the implementation (red → green per commit).
- [x] Implementation makes the tests pass.
- [x] Conventional Commits referencing the issue (`refs #12`).
- [ ] CI green — all Gitea Actions jobs.
- [x] `docker compose up` unaffected (BPMN redeploys on a fresh CI DB via flowable-init).
- [x] ADR added (ADR-0014).
- [x] Docs — the user-visible demo note lands with S-11c.

## Notes for reviewers

- Verified locally: `Big.Tests` 94/94 pass; `Big.Api` builds; `registratie.bpmn` is well-formed.
- The Flowable message-correlation REST shape is validated **live** by verify-stack (the Workflow Client unit tests stub the exchange and assert only the request shape, per ADR-0009) — the new `run-domain-check.sh` withdrawal step is that live check.
- Known gap (ADR-0014): a withdrawal that races ahead of the process reaching `Beoordelen` finds no task to cancel; the aggregate is still INGETROKKEN and the werkbak filter hides it, but that instance parks unattended. A process-level event subprocess would close the gap — deferred.

Reviewed-on: #89
This commit was merged in pull request #89.
This commit is contained in:
2026-07-16 11:09:28 +00:00
parent 1f1c944a8b
commit a34caba9ea
12 changed files with 342 additions and 23 deletions

View File

@@ -79,9 +79,9 @@ fl="$(docker ps -q --filter 'name=flowable-rest' | head -1)"
fl_base="http://$(ip "$fl"):8080/flowable-rest/service"
reg_id="${loc##*/}"
# Extracts the Beoordelen task id for our registration from a Flowable task-query response on stdin.
# Tolerates an empty/non-JSON body (a transient failure during the poll) by printing nothing.
task_for_reg() { REG_ID="$reg_id" python3 -c "import os,sys,json
# Extracts the Beoordelen task id for a given registration from a Flowable task-query response on
# stdin. Tolerates an empty/non-JSON body (a transient failure during the poll) by printing nothing.
task_for_reg() { REG_ID="$1" python3 -c "import os,sys,json
try:
d=json.load(sys.stdin)
except Exception:
@@ -98,7 +98,7 @@ echo ">> polling Flowable for the Beoordelen user task (werkbak)"
task_id=""
for _ in $(seq 1 30); do
resp="$(flcurl -X POST "$fl_base/query/tasks" -H 'Content-Type: application/json' -d "$query" 2>/dev/null || true)"
task_id="$(printf '%s' "$resp" | task_for_reg)"
task_id="$(printf '%s' "$resp" | task_for_reg "$reg_id")"
[ -n "$task_id" ] && break
sleep 2
done
@@ -115,7 +115,44 @@ flcurl -X POST "$fl_base/runtime/tasks/$task_id" -H 'Content-Type: application/j
echo ">> asserting the process finished (no Beoordelen task remains for the registration)"
resp="$(flcurl -X POST "$fl_base/query/tasks" -H 'Content-Type: application/json' -d "$query")"
still="$(printf '%s' "$resp" | task_for_reg)"
still="$(printf '%s' "$resp" | task_for_reg "$reg_id")"
[ -z "$still" ] || { echo "FAIL — Beoordelen task $still still active after completion" >&2; exit 1; }
echo "OK — behandelaar claimed and completed the Beoordelen task; the registratie process finished"
# ── S-11: withdrawal. A second registration parks at Beoordelen; the citizen withdraws it via the
# domain, which delivers the RegistratieIngetrokken message to the task's execution, tripping the
# BPMN boundary event so the process ends and the Beoordelen task disappears (ADR-0014). ────────────
echo ">> submitting a second registration to withdraw"
loc2="$(docker run --rm --network "$net" curlimages/curl:latest \
-fsS -D - -o /dev/null -X POST "http://$dom_ip:8080/registrations" \
-H 'Content-Type: application/json' -d '{"bsn":"123456782"}' \
| sed -n 's/\r$//; s/^[Ll]ocation: //p' | head -1)"
[ -n "$loc2" ] || { echo "FAIL — second POST /registrations returned no Location" >&2; exit 1; }
reg_id2="${loc2##*/}"
echo ">> second registration $reg_id2"
echo ">> polling Flowable for its Beoordelen task"
task_id2=""
for _ in $(seq 1 30); do
resp="$(flcurl -X POST "$fl_base/query/tasks" -H 'Content-Type: application/json' -d "$query" 2>/dev/null || true)"
task_id2="$(printf '%s' "$resp" | task_for_reg "$reg_id2")"
[ -n "$task_id2" ] && break
sleep 2
done
[ -n "$task_id2" ] || { echo "FAIL — no Beoordelen task appeared for registration $reg_id2" >&2; docker logs "$dom" 2>&1 | tail -15 >&2; exit 1; }
echo ">> Beoordelen task $task_id2 is waiting; withdrawing the registration via the domain"
docker run --rm --network "$net" curlimages/curl:latest \
-fsS -X POST "http://$dom_ip:8080/registrations/$reg_id2/withdraw" >/dev/null
echo ">> asserting the process was cancelled (no Beoordelen task remains for the registration)"
gone=""
for _ in $(seq 1 15); do
resp="$(flcurl -X POST "$fl_base/query/tasks" -H 'Content-Type: application/json' -d "$query" 2>/dev/null || true)"
still2="$(printf '%s' "$resp" | task_for_reg "$reg_id2")"
[ -z "$still2" ] && { gone=1; break; }
sleep 2
done
[ -n "$gone" ] || { echo "FAIL — Beoordelen task for $reg_id2 still active after withdrawal" >&2; docker logs "$dom" 2>&1 | tail -15 >&2; exit 1; }
echo "OK — withdrawal cancelled the Beoordelen task; the registratie process ended (ingetrokken)"
exit 0