diff --git a/services/bff/Bff.Api/DownstreamClients.cs b/services/bff/Bff.Api/DownstreamClients.cs
index 8571641..5c21c85 100644
--- a/services/bff/Bff.Api/DownstreamClients.cs
+++ b/services/bff/Bff.Api/DownstreamClients.cs
@@ -24,6 +24,9 @@ public interface IDomainClient
/// The behandelaar's werkbak — registrations awaiting beoordeling.
Task> GetWerkbakAsync(CancellationToken ct = default);
+
+ /// Apply a behandelaar's decision (goedkeuren/afwijzen) to a registration.
+ Task DecideAsync(string registrationId, string besluit, CancellationToken ct = default);
}
/// Port to the read projection.
@@ -47,6 +50,13 @@ public sealed class DomainClient(HttpClient http) : IDomainClient
public async Task> GetWerkbakAsync(CancellationToken ct = default)
=> await http.GetFromJsonAsync>("behandel/werkbak", ct) ?? [];
+ public async Task DecideAsync(string registrationId, string besluit, CancellationToken ct = default)
+ {
+ using var response = await http.PostAsJsonAsync(
+ $"registrations/{registrationId}/decide", new { besluit }, ct);
+ response.EnsureSuccessStatusCode();
+ }
+
private sealed record DomainResponse(string RegistrationId, string Status, string? ZaakUrl);
}
diff --git a/services/bff/Bff.Tests/BehandelEndpointTests.cs b/services/bff/Bff.Tests/BehandelEndpointTests.cs
index 3d39147..c3e3dfb 100644
--- a/services/bff/Bff.Tests/BehandelEndpointTests.cs
+++ b/services/bff/Bff.Tests/BehandelEndpointTests.cs
@@ -54,4 +54,61 @@ public class BehandelEndpointTests
Assert.Equal("reg-1", item.RegistrationId);
Assert.Equal("123456782", item.Bsn);
}
+
+ private static HttpRequestMessage Decide(string? bearer, string id = "reg-1", string besluit = "goedkeuren")
+ {
+ var request = new HttpRequestMessage(HttpMethod.Post, $"/behandel/registrations/{id}/decide")
+ {
+ Content = JsonContent.Create(new { besluit }),
+ };
+ if (bearer is not null)
+ request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", bearer);
+ return request;
+ }
+
+ [Fact]
+ public async Task Rejects_a_decision_without_a_token()
+ {
+ using var factory = new BffFactory();
+
+ var response = await factory.CreateClient().SendAsync(Decide(bearer: null));
+
+ Assert.Equal(HttpStatusCode.Unauthorized, response.StatusCode);
+ Assert.Null(factory.Domain.Decided);
+ }
+
+ [Fact]
+ public async Task Rejects_a_decision_from_a_medewerker_without_the_behandelaar_role()
+ {
+ using var factory = new BffFactory();
+
+ var response = await factory.CreateClient().SendAsync(Decide(TestTokens.Medewerker("teamlead")));
+
+ Assert.Equal(HttpStatusCode.Forbidden, response.StatusCode);
+ Assert.Null(factory.Domain.Decided);
+ }
+
+ [Fact]
+ public async Task Forwards_a_behandelaar_decision_to_the_domain()
+ {
+ using var factory = new BffFactory();
+
+ var response = await factory.CreateClient()
+ .SendAsync(Decide(TestTokens.Medewerker("behandelaar"), id: "reg-42", besluit: "afwijzen"));
+
+ Assert.Equal(HttpStatusCode.NoContent, response.StatusCode);
+ Assert.Equal(("reg-42", "afwijzen"), factory.Domain.Decided);
+ }
+
+ [Fact]
+ public async Task Rejects_an_unknown_besluit_without_calling_the_domain()
+ {
+ using var factory = new BffFactory();
+
+ var response = await factory.CreateClient()
+ .SendAsync(Decide(TestTokens.Medewerker("behandelaar"), besluit: "misschien"));
+
+ Assert.Equal(HttpStatusCode.BadRequest, response.StatusCode);
+ Assert.Null(factory.Domain.Decided);
+ }
}
diff --git a/services/bff/Bff.Tests/BffFactory.cs b/services/bff/Bff.Tests/BffFactory.cs
index 5e74fb9..3cf1e67 100644
--- a/services/bff/Bff.Tests/BffFactory.cs
+++ b/services/bff/Bff.Tests/BffFactory.cs
@@ -82,8 +82,16 @@ internal sealed class FakeDomainClient : IDomainClient
return Task.FromResult(Result);
}
+ public (string RegistrationId, string Besluit)? Decided { get; private set; }
+
public Task> GetWerkbakAsync(CancellationToken ct = default)
=> Task.FromResult>(Werkbak);
+
+ public Task DecideAsync(string registrationId, string besluit, CancellationToken ct = default)
+ {
+ Decided = (registrationId, besluit);
+ return Task.CompletedTask;
+ }
}
/// Serves a configurable set of projection rows.