fix(backend): 400 instead of 500 on an unparseable peildatum (RB-16)
BIO-019: GET /stamdata/{table}?peildatum= called DateOnly.Parse
directly, which throws FormatException on anything unparseable — an
unhandled 500 (leaking exception detail in Development) instead of
the 400-with-problem-details every other bad-input check in this file
returns. §3c named backend/Stamdata's 71.7% branch coverage (BL-005)
as the weak spot this bug lived in.
Switched to DateOnly.TryParse; an unparseable value now returns
Results.Problem(detail: ..., statusCode: 400), matching the shape the
upload/change-request endpoints already use. Endpoint doc gained
.ProducesProblem(400), so the OpenAPI doc + generated client were
regenerated and committed in this same diff (RB-09's note records a
prior incident where a response-shape change shipped without this and
the drift went unnoticed).
No FE change needed: libs/beheer's stamdata adapter already funnels
every call through runSubmit, which folds any thrown ApiException
(now including this 400) into a generic Result error — ADR-0001's
"the FE renders the decision" already covers "the server rejected
this input".
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -212,12 +212,24 @@ api.MapGet("/stamdata/{table}", (string table, string? peildatum, HttpContext ct
|
||||
{
|
||||
var t = StamdataCatalog.Find(table);
|
||||
if (t is null) return Results.NotFound();
|
||||
var rows = peildatum is { Length: > 0 } p ? t.RowsOn(DateOnly.Parse(p)) : t.Rows();
|
||||
DateOnly? peildatumWaarde = null;
|
||||
// RB-16/BIO-019: DateOnly.Parse threw FormatException on unparseable input, surfacing as
|
||||
// an unhandled 500 (and, in Development, an exception detail leaked to the caller) — an
|
||||
// admin-gated but still user-supplied string needs the same 400 path every other bad-input
|
||||
// check in this file uses, not a crash.
|
||||
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();
|
||||
return Results.Ok(new StamdataTableDto(t.Id, t.Label, t.Columns.Select(ToColumnDto).ToList(), t.Temporal, rows));
|
||||
}))
|
||||
.Gate("StamdataAdmin")
|
||||
.WithName("stamdataTable")
|
||||
.Produces<StamdataTableDto>()
|
||||
.ProducesProblem(StatusCodes.Status400BadRequest)
|
||||
.ProducesProblem(StatusCodes.Status403Forbidden)
|
||||
.Produces(StatusCodes.Status404NotFound);
|
||||
|
||||
|
||||
@@ -194,6 +194,16 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"400": {
|
||||
"description": "Bad Request",
|
||||
"content": {
|
||||
"application/problem+json": {
|
||||
"schema": {
|
||||
"$ref": "#/components/schemas/ProblemDetails"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"403": {
|
||||
"description": "Forbidden",
|
||||
"content": {
|
||||
|
||||
@@ -59,6 +59,16 @@ public class StamdataEndpointTests(TestWebApplicationFactory factory) : IClassFi
|
||||
Assert.Empty(table.Rows);
|
||||
}
|
||||
|
||||
/// RB-16/BIO-019: DateOnly.Parse used to throw FormatException on unparseable input,
|
||||
/// surfacing as an unhandled 500 instead of the 400-with-problem-details every other
|
||||
/// bad-input check in this endpoint file returns.
|
||||
[Fact]
|
||||
public async Task Unparseable_peildatum_is_400_not_500()
|
||||
{
|
||||
var res = await _client.SendAsync(Req(HttpMethod.Get, "/api/v1/stamdata/professions?peildatum=not-a-date", role: "admin"));
|
||||
Assert.Equal(HttpStatusCode.BadRequest, res.StatusCode);
|
||||
}
|
||||
|
||||
[Fact]
|
||||
public async Task Unknown_table_is_404()
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user