fix(domain): correlate withdrawal by process instance to the subscribed execution (refs #12)
All checks were successful
CI / lint (pull_request) Successful in 1m15s
CI / build (pull_request) Successful in 1m1s
CI / unit (pull_request) Successful in 1m20s
CI / frontend (pull_request) Successful in 2m50s
CI / mutation (pull_request) Successful in 5m22s
CI / verify-stack (pull_request) Successful in 7m18s
All checks were successful
CI / lint (pull_request) Successful in 1m15s
CI / build (pull_request) Successful in 1m1s
CI / unit (pull_request) Successful in 1m20s
CI / frontend (pull_request) Successful in 2m50s
CI / mutation (pull_request) Successful in 5m22s
CI / verify-stack (pull_request) Successful in 7m18s
verify-stack surfaced a Flowable 500: delivering messageEventReceived to the Beoordelen task's execution is wrong — a message boundary event's subscription lives on its own execution. Correlate instead by the registration's process instance id (recorded at submit): query the execution subscribed to RegistratieIngetrokken and deliver the message there. Withdrawal moves from IUserTaskClient to IWorkflowClient.WithdrawProcessAsync; the handler no longer needs a task lookup. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -66,7 +66,7 @@ public sealed class FlowableWorkflowClient(HttpClient http, FlowableOptions opti
|
||||
"service/query/tasks", request, ct);
|
||||
|
||||
var tasks = page?.Data ?? [];
|
||||
return [.. tasks.Select(t => new BeoordelingTask(t.Id, t.ExecutionId, RegistrationId.Parse(t.RegistrationId())))];
|
||||
return [.. tasks.Select(t => new BeoordelingTask(t.Id, RegistrationId.Parse(t.RegistrationId())))];
|
||||
}
|
||||
|
||||
public async Task ClaimAsync(string taskId, string behandelaar, CancellationToken ct = default)
|
||||
@@ -86,18 +86,35 @@ public sealed class FlowableWorkflowClient(HttpClient http, FlowableOptions opti
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
public async Task WithdrawBeoordelingAsync(string executionId, CancellationToken ct = default)
|
||||
public async Task WithdrawProcessAsync(string processInstanceId, CancellationToken ct = default)
|
||||
{
|
||||
// Deliver the withdrawal message to the Beoordelen task's execution, tripping the process's
|
||||
// interrupting message boundary event so the registratie instance ends (ADR-0014). Flowable
|
||||
// takes messageEventReceived as a PUT on the subscribed execution.
|
||||
var request = new MessageEventRequest("messageEventReceived", IngetrokkenMessage);
|
||||
// Correlate the withdrawal message to the instance: find the execution subscribed to it (the
|
||||
// interrupting message event's own execution — NOT the user task's), then deliver
|
||||
// messageEventReceived to that execution so the process ends (ADR-0014). If nothing is
|
||||
// subscribed (the process is not parked at Beoordelen) this is a best-effort no-op.
|
||||
var subscribed = await GetAsync<ExecutionQueryResult>(
|
||||
$"service/runtime/executions?messageEventSubscriptionName={IngetrokkenMessage}&processInstanceId={processInstanceId}",
|
||||
ct);
|
||||
|
||||
var execution = subscribed?.Data?.FirstOrDefault();
|
||||
if (execution is null)
|
||||
return;
|
||||
|
||||
var request = new MessageEventRequest("messageEventReceived", IngetrokkenMessage);
|
||||
using var response = await SendAsync(
|
||||
$"service/runtime/executions/{executionId}", request, ct, HttpMethod.Put);
|
||||
$"service/runtime/executions/{execution.Id}", request, ct, HttpMethod.Put);
|
||||
response.EnsureSuccessStatusCode();
|
||||
}
|
||||
|
||||
private async Task<TResponse?> GetAsync<TResponse>(string path, CancellationToken ct)
|
||||
{
|
||||
var message = new HttpRequestMessage(HttpMethod.Get, new Uri(options.BaseUrl, path));
|
||||
message.Headers.Authorization = new AuthenticationHeaderValue("Basic", BasicCredentials());
|
||||
using var response = await http.SendAsync(message, ct);
|
||||
response.EnsureSuccessStatusCode();
|
||||
return await response.Content.ReadFromJsonAsync<TResponse>(ct);
|
||||
}
|
||||
|
||||
private async Task<TResponse?> PostAsync<TRequest, TResponse>(string path, TRequest body, CancellationToken ct)
|
||||
{
|
||||
using var response = await SendAsync(path, body, ct);
|
||||
@@ -154,7 +171,6 @@ public sealed class FlowableWorkflowClient(HttpClient http, FlowableOptions opti
|
||||
|
||||
private sealed record UserTaskDto(
|
||||
[property: JsonPropertyName("id")] string Id,
|
||||
[property: JsonPropertyName("executionId")] string ExecutionId,
|
||||
// Flowable's task-query returns the (included) process variables under "variables", not
|
||||
// "processVariables"; the request opts in via includeProcessVariables.
|
||||
[property: JsonPropertyName("variables")] IReadOnlyList<Variable>? Variables)
|
||||
@@ -172,6 +188,11 @@ public sealed class FlowableWorkflowClient(HttpClient http, FlowableOptions opti
|
||||
|
||||
private sealed record ProcessInstance([property: JsonPropertyName("id")] string Id);
|
||||
|
||||
private sealed record ExecutionQueryResult(
|
||||
[property: JsonPropertyName("data")] IReadOnlyList<ExecutionDto>? Data);
|
||||
|
||||
private sealed record ExecutionDto([property: JsonPropertyName("id")] string Id);
|
||||
|
||||
private sealed record AcquiredJob(
|
||||
[property: JsonPropertyName("id")] string Id,
|
||||
[property: JsonPropertyName("variables")] IReadOnlyList<Variable> Variables)
|
||||
|
||||
Reference in New Issue
Block a user