CI / frontend (push) Successful in 2m59s
CI / backend (push) Successful in 1m27s
CI / semgrep (push) Successful in 58s
CI / e2e (push) Successful in 2m30s
CI / api-client-drift (push) Canceled after 1m14s
CI / storybook-a11y (push) Canceled after 29m8s
Stamdata: add beroepen, opleidingen (temporal), and specialismen tables to the schema-driven catalog (zero UI code). opleidingen.beroep and specialismen.beroep both reference beroepen.code — the first stamdata->stamdata references, enforced by two new StamdataRef entries in the CI gate. OpenZaak/ZGW (WP-49, slice 1 — read-only zaken): introduce IZaakSource as the cases read seam. Default LocalZaakSource reads the local SQLite store (offline); an OpenZaakZaakSource (Zgw/ client: HS256 per-call JWT, ZGW->existing-DTO mapper, paginating HTTP source) is selected behind Zgw:Enabled (default false). The FE never changes — same ApplicationSummaryDto, no api-client drift. Unit-tested with fixtures + a stub HttpMessageHandler; no live OpenZaak needed. Docs: ADR-0005, reference/openzaak-integration.md, WP-49..52 roadmap, stamdata.md update, README index rows. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
60 lines
2.5 KiB
Markdown
60 lines
2.5 KiB
Markdown
# WP-49 — OpenZaak zaken read seam (slice 1)
|
|
|
|
Status: done
|
|
Phase: 9 — OpenZaak / ZGW integration
|
|
|
|
## Why
|
|
|
|
The POC serves cases from local SQLite. To grow toward production it must be able to source
|
|
them from a real ZGW backend (OpenZaak) **without changing the frontend** (BFF-lite, ADR-0001).
|
|
The backend had no data-access abstraction to swap behind, no outbound HTTP, and no JWT. This is
|
|
the first thin vertical slice: read-only zaken.
|
|
|
|
## Read first
|
|
|
|
- [ADR-0005 — OpenZaak behind the BFF](../reference/architecture/0005-openzaak-behind-bff.md)
|
|
- [openzaak-integration.md](../reference/openzaak-integration.md)
|
|
|
|
## Decisions (pre-made, don't relitigate)
|
|
|
|
- OpenZaak's anti-corruption layer lives in the **BFF**, never the browser.
|
|
- Source selected by config (`Zgw:Enabled`, default false) → POC still runs offline.
|
|
- Each source maps into the **existing** `ApplicationSummaryDto` → no api-client drift, no FE change.
|
|
- Fresh HS256 JWT **per call** (no refresh flow). Hand-rolled (no new NuGet).
|
|
- `IZaakSource` is sync (matches the endpoint + local store); OpenZaak source does sync-over-async.
|
|
|
|
## Files
|
|
|
|
- `Data/IZaakSource.cs`, `Data/LocalZaakSource.cs`
|
|
- `Zgw/{ZgwOptions,ZgwTokenProvider,ZgwZaakMapper,OpenZaakZaakSource}.cs`
|
|
- `Program.cs` (DI + resolve `IZaakSource` in `/admin/cases`), `appsettings.json` (`Zgw` section)
|
|
- tests: `ZgwTokenProviderTests`, `ZgwZaakMapperTests`, `OpenZaakZaakSourceTests`
|
|
|
|
## Steps
|
|
|
|
1. Extract the cases read into `IZaakSource`; `LocalZaakSource` delegates to `ApplicationStore`.
|
|
2. Build the `Zgw/` client (options, JWT minter, ZGW→DTO mapper, paginating HTTP source).
|
|
3. Wire DI by config; refactor `/admin/cases` to resolve `IZaakSource`.
|
|
4. Unit-test the minter, mapper, and source (fixtures + stub `HttpMessageHandler`).
|
|
|
|
## Acceptance criteria
|
|
|
|
- [x] `/admin/cases` serves identical DTOs via `LocalZaakSource` (default, offline).
|
|
- [x] `OpenZaakZaakSource` follows pagination, resolves+caches zaaktype labels, sends a Bearer token.
|
|
- [x] JWT verifies (HS256) with the required claims.
|
|
- [x] `dotnet test` green (142); `npm run ci` green with **no api-client drift** (FE untouched).
|
|
|
|
## Verification
|
|
|
|
`cd backend && dotnet test`; `npm run ci`; manual: `/beheer/zaken` still lists cases with `Zgw:Enabled=false`.
|
|
|
|
## Out of scope
|
|
|
|
Create-zaak (WP-50), Documenten/DRC (WP-51), Notificaties (WP-52), real inbound OIDC/JWT,
|
|
OpenZaak in docker-compose.
|
|
|
|
## Risks
|
|
|
|
Sync-over-async blocks a thread under load if OpenZaak becomes the default → make the read path
|
|
async then (noted at the call site).
|