Files
atomic-design-poc/docs/project/archive/refactor-backlog-setup/refactor-backlog/implementation/rb-16.md
T
ehoandClaude Opus 5 12f17d9d73 docs: archive the finished backlogs (RD-30)
Two backlog trees are complete: `docs/project/backlog/` (75 files, every
WP done) and `docs/project/refactor-backlog-setup/` (the arc before it).
Move both under `docs/project/archive/` with `git mv`, so history stays
intact through `git log --follow`. `SHOWCASE-ROADMAP.md` moves with them,
because it points at the now-archived backlog README.

Add `docs/project/archive/README.md`. It states that these trees are
historical and names the two directories that are still live.

Repoint every inbound reference named in RD-30's Files table: CLAUDE.md,
the root README, both backend READMEs, `LetterHtml.cs`, `a11y.mdx`, the
`document-feature` and `new-ssp` skills, and the readable-codebase PLAN,
README, and RD-19 ticket. Fix two upward-relative links inside the moved
WP files (WP-68, WP-69) that gained a directory level and would otherwise
break. Repoint `.prettierignore`'s two agent-prompt exclusions to their
new path, so prettier keeps leaving those files' exact wording alone.

Mark RD-30 done and check off its acceptance criteria; flip its README
row to done.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-09-08 23:00:38 +02:00

5.5 KiB

RB-16 — DateOnly.TryParse on ?peildatum=

Status: implemented · 2026-08-27 · Source findings: 07-bio2-compliance.md BIO-019 · 99-backlog.md RB-16

What was wrong

Program.cs:215 (pre-change) — var rows = peildatum is { Length: > 0 } p ? t.RowsOn(DateOnly.Parse(p)) : t.Rows();. DateOnly.Parse throws FormatException on anything unparseable; there was no TryParse, no 400 path, and .Produces on the endpoint declared only 200/403/404 — so an unparseable ?peildatum= value 500'd, and in Development the exception detail was returned to the caller. §3c's baseline named backend/Stamdata 96.8% line but 71.7% branch (BL-005) — this was one of the unentered branches.

What changed

File Change
Program.cs — GET /stamdata/{table} DateOnly.Parse replaced with DateOnly.TryParse; an unparseable value now returns Results.Problem(detail: …, statusCode: 400) instead of throwing; endpoint doc gained .ProducesProblem(StatusCodes.Status400BadRequest)
tests/BigRegister.Tests/StamdataEndpointTests.cs new Unparseable_peildatum_is_400_not_500
backend/swagger.json, libs/shared/src/infrastructure/api-client.ts regenerated (npm run gen:api) — the new 400 response is now part of the documented contract

What the fix looks like

DateOnly? peildatumWaarde = null;
if (peildatum is { Length: > 0 } p)
{
  if (!DateOnly.TryParse(p, out var parsed))
    return Results.Problem(detail: $"Ongeldige peildatum '{p}'.", statusCode: StatusCodes.Status400BadRequest);
  peildatumWaarde = parsed;
}
var rows = peildatumWaarde is { } d ? t.RowsOn(d) : t.Rows();

Matches the shape every other bad-input check in this file already uses (e.g. the upload endpoint's Results.Problem(detail: …, statusCode: 400) for a malformed multipart request) — a Results.Problem with a Dutch detail message, not a bespoke response shape.

Judgement calls

  • No FE change needed, and none made. libs/beheer/src/infrastructure/ stamdata.adapter.ts's load() already routes every call through runSubmit (libs/shared/src/application/submit.ts), which try/catches any thrown ApiException — including the client's new 400 branch — into a generic Result error string via problemDetail. There is no status-code-specific branching to extend; ADR-0001's "the FE renders the decision, it does not recompute the rule" already covers "the server rejected this input" as a case the generic error path handles, same as the existing 403.
  • Regenerated the API client and committed it in this ticket's diff, rather than leaving it to drift. RB-09's implementation note records a real prior incident where a response-shape change (RB-08's 403 → ProducesProblem) landed without a regeneration and the drift went unnoticed until the next ticket's gen:api run. This ticket's .ProducesProblem(400) is exactly that same category of change, so npm run gen:api was run immediately as part of implementing it, not deferred.
  • The Dutch detail message follows the file's own convention ($"Ongeldige peildatum '{p}'.") rather than English — every other Results.Problem(detail: …) call in Program.cs (change-request rejection, upload validation, submit rejection) is Dutch; this is server-internal wire text, not $localize-wrapped UI copy (the FE never renders it verbatim — CLAUDE.md's $localize rule is about user-facing copy the FE owns, not backend ProblemDetails.detail strings), so no locale entry was needed.

Verification

  • Reverted the fix only (DateOnly.Parse restored, ternary un-nested, via Edit — test left in place) and ran StamdataEndpointTests: Unparseable_peildatum_is_400_not_500 failed red (Expected: BadRequest, Actual: InternalServerError — confirming the endpoint really did 500, not some other status). Restored the fix (via Edit) and re-ran: all 6 tests in the class green.
  • dotnet build: clean, 0 warnings.
  • dotnet format BigRegister.slnx --verify-no-changes: clean.
  • dotnet test --filter "Category!=Integration": 260 passed, 0 failed (259 + 1 new).
  • npm run gen:api: exit 0; backend/swagger.json gained the 400 response shape for this one endpoint; libs/shared/src/infrastructure/api-client.ts gained the matching status === 400 branch. Both regenerated files committed alongside the code change.
  • npm test (all four Vitest projects — ssp/behandelportal/shared/beheer): 445 passed, 0 failed, confirming the regenerated client doesn't break any existing FE consumer of stamdataTable(...).