docs+test(e2e): ADR-0026, S-15b demo note, default-fill e2e (refs #131)
CI / build (pull_request) Successful in 1m19s
CI / lint (pull_request) Successful in 1m32s
CI / unit (pull_request) Successful in 1m49s
CI / frontend (pull_request) Successful in 3m44s
CI / mutation (pull_request) Successful in 6m29s
CI / verify-stack (pull_request) Successful in 15m53s
CI / build (pull_request) Successful in 1m19s
CI / lint (pull_request) Successful in 1m32s
CI / unit (pull_request) Successful in 1m49s
CI / frontend (pull_request) Successful in 3m44s
CI / mutation (pull_request) Successful in 6m29s
CI / verify-stack (pull_request) Successful in 15m53s
This commit is contained in:
@@ -0,0 +1,61 @@
|
||||
# ADR-0026: Runtime-mutable ACL default-fill (in-memory store, seeded from config)
|
||||
|
||||
- **Status:** Accepted
|
||||
- **Date:** 2026-07-24
|
||||
- **Deciders:** Respellion engineering
|
||||
- **Slice:** S-15b (#131), second of the S-15 (#16) split
|
||||
|
||||
## Context
|
||||
|
||||
ADR-0003 made the ACL *default-fill* the ZGW-mandatory fields it stamps on every
|
||||
zaak, supplied as static configuration (`Acl:Defaults`, read once at startup as an
|
||||
immutable singleton). S-15b lets a beheerder **edit** those values from the portal
|
||||
and have the next zaak reflect them — so the defaults must become mutable at runtime.
|
||||
|
||||
Two questions: **what** is editable, and **where** the mutable state lives.
|
||||
|
||||
## Decision
|
||||
|
||||
**Make the three ZGW default-fill fields a runtime-mutable, in-memory store
|
||||
(`IDefaultFillStore`), seeded from `Acl:Defaults` at startup. The ACL reads it per
|
||||
zaak; the beheer `PUT /default-fill` replaces it.**
|
||||
|
||||
### Only the three ZGW fill fields are editable
|
||||
|
||||
`Acl:Defaults` also carries the S-27 catalog-resolution keys (`ZaaktypeIdentificatie`,
|
||||
`InformatieobjecttypeOmschrijving`). Those feed the resolved-URL cache
|
||||
(`CachedZaaktypeCatalog`, ADR-0021); editing them at runtime would leave a stale cache
|
||||
and is catalogus *wiring*, not "default fill". So they **stay static config** and are
|
||||
out of scope for the CRUD. The editable set is exactly `Bronorganisatie`,
|
||||
`VerantwoordelijkeOrganisatie`, `Vertrouwelijkheidaanduiding` (`DefaultFillSettings`).
|
||||
|
||||
### In-memory, not persisted
|
||||
|
||||
The store is a thread-safe in-memory singleton. **An edit is lost on restart**, when it
|
||||
reverts to the configured env. That is acceptable for this reference app: the slice
|
||||
demonstrates the *pattern* (beheer edits config that the ACL honours), not durable
|
||||
config management. The ACL stays stateless — no DB, no EF, no migration, no extra
|
||||
compose service.
|
||||
|
||||
- ponytail ceiling: no persistence, no audit trail, no optimistic concurrency.
|
||||
- Upgrade path: back `IDefaultFillStore` with a DB (or an Objecten record) if durable,
|
||||
audited, multi-instance config is needed — the port stays the same.
|
||||
|
||||
## Consequences
|
||||
|
||||
**Positive**
|
||||
|
||||
- Demoable end to end (edit in portal → next zaak reflects it) with minimal moving parts.
|
||||
- The read path is per-zaak, so no restart and no cache concerns for the ZGW fields.
|
||||
|
||||
**Negative / costs**
|
||||
|
||||
- Edits don't survive a restart and aren't shared across replicas (single-instance
|
||||
assumption). Documented ceiling above.
|
||||
- Two sources of default config now (static keys on `AclDefaults`, mutable fields in the
|
||||
store) — a deliberate split by editability.
|
||||
|
||||
## Coupling rules touched (CLAUDE.md §8)
|
||||
|
||||
None new. The BFF→ACL edge already exists (ADR-0025); this adds a read/write pair on it.
|
||||
The ACL remains the owner of the ZGW-facing config.
|
||||
@@ -5,6 +5,28 @@ copy-pasteable walkthrough against a local `make up` stack.
|
||||
|
||||
---
|
||||
|
||||
## S-15b — Beheer-portal: default-fill configuration editor (#131, ADR-0026)
|
||||
|
||||
**Outcome:** a beheerder edits the ACL's ZGW **default-fill** values (bronorganisatie,
|
||||
verantwoordelijke organisatie, vertrouwelijkheidaanduiding) from the beheer portal, and the next zaak
|
||||
is stamped with the new values — no restart. Path: portal → BFF `GET/PUT /beheer/default-fill`
|
||||
(beheerder role) → ACL `GET/PUT /default-fill` → a runtime-mutable in-memory store the ACL reads per
|
||||
zaak (ADR-0026). The S-27 catalog-resolution keys stay static config (editing them would desync the
|
||||
zaaktype cache). Store is in-memory: an edit reverts to the configured env on restart.
|
||||
|
||||
```bash
|
||||
make up
|
||||
# 1. Log in as bram-beheerder / test123 → "Default-fill" tab → change a value → Opslaan.
|
||||
open http://localhost:8143/default-fill
|
||||
#
|
||||
# 2. Automated: the ACL uses the current default-fill per zaak (unit) and the endpoints are behind the
|
||||
# beheerder role (BFF unit):
|
||||
# Acl.Tests → AclServiceTests.Opening_a_zaak_reflects_a_default_fill_update
|
||||
# Bff.Tests → BeheerDefaultFillEndpointTests
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## S-15a — Beheer-portal: read-only catalogus viewer (#130, ADR-0025)
|
||||
|
||||
**Outcome:** a new **beheer** portal (medewerker realm, like behandel) shows the ZTC catalogus —
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
import { expect, test } from '@playwright/test';
|
||||
|
||||
// S-15b: a beheerder edits the ACL default-fill in the beheer portal and gets a saved confirmation.
|
||||
// Runs against the shared verify stack; it edits + saves (the ACL store is in-memory, ADR-0026) and
|
||||
// asserts the confirmation, without depending on another test's state.
|
||||
test('a beheerder edits and saves the default-fill', async ({ page }) => {
|
||||
await page.goto('http://beheer/');
|
||||
|
||||
// Keycloak medewerker-realm login (same realm as behandel).
|
||||
await page.locator('#username').fill('bram-beheerder');
|
||||
await page.locator('#password').fill('test123');
|
||||
await page.locator('#kc-login').click();
|
||||
|
||||
await expect(page.getByRole('heading', { name: /Catalogus/i })).toBeVisible();
|
||||
|
||||
// Navigate to the default-fill editor and change a value.
|
||||
await page.getByRole('link', { name: /Default-fill/i }).click();
|
||||
await expect(page.getByRole('heading', { name: /Default-fill/i })).toBeVisible();
|
||||
|
||||
const bron = page.getByLabel('Bronorganisatie');
|
||||
await expect(bron).toBeVisible();
|
||||
await bron.fill('517439943');
|
||||
await page.getByRole('button', { name: /Opslaan/i }).click();
|
||||
|
||||
await expect(page.getByText(/standaardwaarden zijn opgeslagen/i)).toBeVisible();
|
||||
});
|
||||
Reference in New Issue
Block a user