feat: read projection sourced from the register in Objecten (closes #153) #155
@@ -90,6 +90,16 @@ app.MapPost("/zaken/reference", async (ZaakReferenceRequest body, AclService acl
|
||||
return Results.Ok(new { reference });
|
||||
});
|
||||
|
||||
// Read the register record an object in Objecten holds. The Event Subscriber projects a register
|
||||
// write from the notification NRC delivers, which carries only the object URL, and may not talk to
|
||||
// Objecten itself (§8.1, ADR-0028/ADR-0030). 404 when the object holds no record — the subscriber
|
||||
// treats that as "nothing to project" rather than an error (§8.6).
|
||||
app.MapPost("/register-records/read", async (RegisterRecordReadRequest body, AclService acl, CancellationToken ct) =>
|
||||
{
|
||||
var record = await acl.GetRegisterRecordAsync(new Uri(body.ObjectUrl), ct);
|
||||
return record is null ? Results.NotFound() : Results.Ok(record);
|
||||
});
|
||||
|
||||
// Store an uploaded diploma against a zaak (S-10b): the domain sends the file as base64; the ACL
|
||||
// creates the ZGW enkelvoudiginformatieobject and relates it to the zaak (§8.1). Returns its URL.
|
||||
app.MapPost("/documenten", async (StoreDocumentRequest body, AclService acl, CancellationToken ct) =>
|
||||
@@ -131,6 +141,9 @@ public sealed record CancelZaakRequest(string ZaakUrl);
|
||||
|
||||
public sealed record ZaakReferenceRequest(string ZaakUrl);
|
||||
|
||||
/// <summary>The object whose register record the Event Subscriber wants read back (S-19b-2).</summary>
|
||||
public sealed record RegisterRecordReadRequest(string ObjectUrl);
|
||||
|
||||
public sealed record StoreDocumentRequest(string ZaakUrl, string ContentBase64, string FileName, string ContentType);
|
||||
|
||||
public partial class Program;
|
||||
|
||||
@@ -24,7 +24,16 @@ public sealed class AclService(
|
||||
clock.Today,
|
||||
registration.Reference);
|
||||
|
||||
return await gateway.OpenZaakAsync(request, ct);
|
||||
var zaakUrl = await gateway.OpenZaakAsync(request, ct);
|
||||
|
||||
// The register — not ZGW — is what the read projection is sourced from (ADR-0028/ADR-0030),
|
||||
// so the record exists from submission, not only from approval. Same two-writes-converging
|
||||
// posture as ApproveZaakAsync: the upsert is keyed on the zaak id, so a retried submit
|
||||
// updates the record rather than adding a second one (§8.6).
|
||||
await register.UpsertAsync(
|
||||
new RegisterRecord(ZaakId(zaakUrl), RegisterRecordStatus.Ingediend, registration.Reference), ct);
|
||||
|
||||
return zaakUrl;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
using System.Net;
|
||||
using System.Net.Http.Headers;
|
||||
using System.Net.Http.Json;
|
||||
using System.Text.Json.Serialization;
|
||||
@@ -38,8 +39,29 @@ public sealed class ObjectenGateway(HttpClient http, ObjectenOptions options, IC
|
||||
"Updating the register record", ct);
|
||||
}
|
||||
|
||||
public Task<RegisterRecord?> GetAsync(Uri objectUrl, CancellationToken ct = default)
|
||||
=> throw new NotImplementedException();
|
||||
public async Task<RegisterRecord?> GetAsync(Uri objectUrl, CancellationToken ct = default)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(objectUrl);
|
||||
|
||||
// Fetched by the URL the notification carried, so no objecttype resolution and no search —
|
||||
// unlike a write, which has to find the object for a registration id.
|
||||
using var message = new HttpRequestMessage(HttpMethod.Get, objectUrl);
|
||||
message.Headers.Authorization = new AuthenticationHeaderValue("Token", options.Token);
|
||||
message.Headers.Add("Accept-Crs", "EPSG:4326");
|
||||
|
||||
using var response = await http.SendAsync(message, ct);
|
||||
// The object may be gone by the time a (possibly redelivered) notification is handled —
|
||||
// there is simply nothing to project, which is not a failure (§8.6).
|
||||
if (response.StatusCode == HttpStatusCode.NotFound)
|
||||
return null;
|
||||
|
||||
await EnsureSuccessAsync(response, "Reading the register record", ct);
|
||||
|
||||
var body = await response.Content.ReadFromJsonAsync<ReadObjectDto>(ct)
|
||||
?? throw new InvalidOperationException("Objecten returned an empty object response");
|
||||
var data = body.Record?.Data;
|
||||
return data is null ? null : new RegisterRecord(data.Id, data.Status, data.Reference);
|
||||
}
|
||||
|
||||
private RecordDto NewRecord(int typeVersion, RecordDataDto data) =>
|
||||
new(typeVersion, data, clock.Today.ToString("yyyy-MM-dd"));
|
||||
@@ -144,6 +166,12 @@ public sealed class ObjectenGateway(HttpClient http, ObjectenOptions options, IC
|
||||
private sealed record ObjectDto(
|
||||
[property: JsonPropertyName("url")] string Url);
|
||||
|
||||
private sealed record ReadObjectDto(
|
||||
[property: JsonPropertyName("record")] ReadRecordDto? Record);
|
||||
|
||||
private sealed record ReadRecordDto(
|
||||
[property: JsonPropertyName("data")] RecordDataDto? Data);
|
||||
|
||||
private sealed record CreateObjectDto(
|
||||
[property: JsonPropertyName("type")] string Type,
|
||||
[property: JsonPropertyName("record")] RecordDto Record);
|
||||
|
||||
Reference in New Issue
Block a user