refactor(acl): resolve the objecttype's highest published version (refs #149)

Counting the `versions` URLs assumed a contiguous, all-published list. Read the
objecttype's versions collection instead and take the highest one whose status
is `published`, so a draft version — whose schema is still being shaped — is
never written against.
This commit is contained in:
not
2026-08-14 09:19:50 +02:00
parent d14f379358
commit c67ee7d3f5
2 changed files with 50 additions and 20 deletions
@@ -41,7 +41,7 @@ public sealed class ObjectenGateway(HttpClient http, ObjectenOptions options, IC
private RecordDto NewRecord(int typeVersion, RecordDataDto data) =>
new(typeVersion, data, clock.Today.ToString("yyyy-MM-dd"));
/// <summary>The URL + latest version number of the configured objecttype, read from Objecttypen.</summary>
/// <summary>The URL + latest published version of the configured objecttype, read from Objecttypen.</summary>
private async Task<Objecttype> ResolveObjecttypeAsync(CancellationToken ct)
{
var page = await GetAsync<ObjecttypePage>(
@@ -52,10 +52,16 @@ public sealed class ObjectenGateway(HttpClient http, ObjectenOptions options, IC
?? throw new InvalidOperationException(
$"No objecttype '{options.ObjecttypeName}' registered in Objecttypen — is the RegisterRecord seed applied?");
// `versions` lists the objecttype's version URLs; the count is the latest version number.
var version = match.Versions?.Count
?? throw new InvalidOperationException($"Objecttype '{options.ObjecttypeName}' has no published version");
return new Objecttype(new Uri(match.Url), version);
var url = new Uri(match.Url);
// Write against the highest *published* version: a draft version's schema is still being
// shaped, and objects written against it would be validated by a moving target.
var versions = await GetAsync<IReadOnlyList<ObjecttypeVersionDto>>(
new Uri(url + "/versions"), options.ObjecttypenToken, crs: false, "objecttype versions", ct);
var latest = versions.Where(v => v.Status == "published").Select(v => v.Version).DefaultIfEmpty(0).Max();
if (latest == 0)
throw new InvalidOperationException($"Objecttype '{options.ObjecttypeName}' has no published version");
return new Objecttype(url, latest);
}
/// <summary>The URL of the object already holding this registration's record, or null if there is none.</summary>
@@ -115,8 +121,11 @@ public sealed class ObjectenGateway(HttpClient http, ObjectenOptions options, IC
private sealed record ObjecttypeDto(
[property: JsonPropertyName("url")] string Url,
[property: JsonPropertyName("name")] string? Name,
[property: JsonPropertyName("versions")] IReadOnlyList<string>? Versions);
[property: JsonPropertyName("name")] string? Name);
private sealed record ObjecttypeVersionDto(
[property: JsonPropertyName("version")] int Version,
[property: JsonPropertyName("status")] string? Status);
private sealed record ObjectPage(
[property: JsonPropertyName("results")] IReadOnlyList<ObjectDto>? Results);