Skip to content

Commit 2f28bd5

Browse files
committed
Add custom error handler for execute workflow middleware and tests
1 parent 076d1b8 commit 2f28bd5

File tree

3 files changed

+131
-18
lines changed

3 files changed

+131
-18
lines changed

src/WorkflowCore/Models/WorkflowDefinition.cs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ public class WorkflowDefinition
1818
public WorkflowErrorHandling DefaultErrorBehavior { get; set; }
1919

2020
public Type OnPostMiddlewareError { get; set; }
21+
public Type OnExecuteMiddlewareError { get; set; }
2122

2223
public TimeSpan? DefaultErrorRetryInterval { get; set; }
2324

src/WorkflowCore/Services/WorkflowMiddlewareRunner.cs

Lines changed: 16 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -43,11 +43,7 @@ public async Task RunPostMiddleware(WorkflowInstance workflow, WorkflowDefinitio
4343
}
4444
catch (Exception exception)
4545
{
46-
// TODO:
47-
// OnPostMiddlewareError should be IWorkflowMiddlewareErrorHandler
48-
// because we don't know to run other error handler type
49-
var errorHandlerType = def.OnPostMiddlewareError ?? typeof(IWorkflowMiddlewareErrorHandler);
50-
await HandleWorkflowMiddlewareError(exception);
46+
await HandleWorkflowMiddlewareError(def.OnPostMiddlewareError, exception);
5147
}
5248
}
5349

@@ -63,19 +59,7 @@ public async Task RunExecuteMiddleware(WorkflowInstance workflow, WorkflowDefini
6359
}
6460
catch (Exception exception)
6561
{
66-
await HandleWorkflowMiddlewareError(exception);
67-
}
68-
}
69-
70-
private async Task HandleWorkflowMiddlewareError(Exception exception)
71-
{
72-
using (var scope = _serviceProvider.CreateScope())
73-
{
74-
var handler = scope.ServiceProvider.GetService<IWorkflowMiddlewareErrorHandler>();
75-
if (handler != null)
76-
{
77-
await handler.HandleAsync(exception);
78-
}
62+
await HandleWorkflowMiddlewareError(def.OnExecuteMiddlewareError, exception);
7963
}
8064
}
8165

@@ -89,5 +73,19 @@ private static Task RunWorkflowMiddleware(
8973
(previous, middleware) =>
9074
() => middleware.HandleAsync(workflow, previous))();
9175
}
76+
77+
private async Task HandleWorkflowMiddlewareError(Type middlewareErrorType, Exception exception)
78+
{
79+
var errorHandlerType = middlewareErrorType ?? typeof(IWorkflowMiddlewareErrorHandler);
80+
81+
using (var scope = _serviceProvider.CreateScope())
82+
{
83+
var typeInstance = scope.ServiceProvider.GetService(errorHandlerType);
84+
if (typeInstance is IWorkflowMiddlewareErrorHandler handler)
85+
{
86+
await handler.HandleAsync(exception);
87+
}
88+
}
89+
}
9290
}
9391
}

test/WorkflowCore.UnitTests/Services/WorkflowMiddlewareRunnerTests.cs

Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -238,6 +238,120 @@ public async Task
238238
.MustHaveHappenedOnceExactly();
239239
}
240240

241+
[Fact(DisplayName = "RunExecuteMiddleware should run nothing when no middleware")]
242+
public void RunExecuteMiddleware_should_run_nothing_when_no_middleware()
243+
{
244+
// Act
245+
Func<Task> action = async () => await Runner.RunExecuteMiddleware(Workflow, Definition);
246+
247+
// Assert
248+
action.ShouldNotThrow();
249+
}
250+
251+
[Fact(DisplayName = "RunExecuteMiddleware should run middleware when one middleware")]
252+
public async Task RunExecuteMiddleware_should_run_middleware_when_one_middleware()
253+
{
254+
// Arrange
255+
var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow);
256+
Middleware.Add(middleware);
257+
258+
// Act
259+
await Runner.RunExecuteMiddleware(Workflow, Definition);
260+
261+
// Assert
262+
A
263+
.CallTo(HandleMethodFor(middleware))
264+
.MustHaveHappenedOnceExactly();
265+
}
266+
267+
[Fact(DisplayName = "RunExecuteMiddleware should run all middleware when multiple middleware")]
268+
public async Task RunExecuteMiddleware_should_run_all_middleware_when_multiple_middleware()
269+
{
270+
// Arrange
271+
var middleware1 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1);
272+
var middleware2 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 2);
273+
var middleware3 = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 3);
274+
Middleware.AddRange(new[] { middleware1, middleware2, middleware3 });
275+
276+
// Act
277+
await Runner.RunPostMiddleware(Workflow, Definition);
278+
279+
// Assert
280+
A
281+
.CallTo(HandleMethodFor(middleware3))
282+
.MustHaveHappenedOnceExactly()
283+
.Then(A
284+
.CallTo(HandleMethodFor(middleware2))
285+
.MustHaveHappenedOnceExactly())
286+
.Then(A
287+
.CallTo(HandleMethodFor(middleware1))
288+
.MustHaveHappenedOnceExactly());
289+
}
290+
291+
[Fact(DisplayName = "RunExecuteMiddleware should run middleware in ExecuteWorkflow and PostWorkflow phase")]
292+
public async Task RunExecuteMiddleware_should_run_middleware_in_ExecuteWorkflow_and_PostWorkflow_phase()
293+
{
294+
// Arrange
295+
var executeMiddleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1);
296+
var postMiddleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PostWorkflow, 2);
297+
var preMiddleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.PreWorkflow, 3);
298+
Middleware.AddRange(new[] { preMiddleware, postMiddleware, executeMiddleware });
299+
300+
// Act
301+
// TODO: add same test when workflow not completed
302+
await Runner.RunExecuteMiddleware(Workflow, Definition);
303+
304+
// Assert
305+
A
306+
.CallTo(HandleMethodFor(executeMiddleware))
307+
.MustHaveHappenedOnceExactly()
308+
.Then(A
309+
.CallTo(HandleMethodFor(postMiddleware))
310+
.MustHaveHappenedOnceExactly());
311+
312+
A.CallTo(HandleMethodFor(preMiddleware)).MustNotHaveHappened();
313+
}
314+
315+
[Fact(DisplayName = "RunExecuteMiddleware should call top level error handler when middleware throws")]
316+
public async Task RunExecuteMiddleware_should_call_top_level_error_handler_when_middleware_throws()
317+
{
318+
// Arrange
319+
var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1);
320+
A.CallTo(HandleMethodFor(middleware)).ThrowsAsync(new ApplicationException("Something went wrong"));
321+
Middleware.AddRange(new[] { middleware });
322+
323+
// Act
324+
await Runner.RunExecuteMiddleware(Workflow, Definition);
325+
326+
// Assert
327+
A
328+
.CallTo(HandleMethodFor(TopLevelErrorHandler))
329+
.MustHaveHappenedOnceExactly();
330+
}
331+
332+
[Fact(DisplayName =
333+
"RunExecuteMiddleware should call error handler on workflow def when middleware throws and def has handler defined")]
334+
public async Task
335+
RunExecuteMiddleware_should_call_error_handler_on_workflow_def_when_middleware_throws_and_def_has_handler()
336+
{
337+
// Arrange
338+
var middleware = BuildWorkflowMiddleware(WorkflowMiddlewarePhase.ExecuteWorkflow, 1);
339+
A.CallTo(HandleMethodFor(middleware)).ThrowsAsync(new ApplicationException("Something went wrong"));
340+
Middleware.AddRange(new[] { middleware });
341+
Definition.OnExecuteMiddlewareError = typeof(IDefLevelErrorHandler);
342+
343+
// Act
344+
await Runner.RunExecuteMiddleware(Workflow, Definition);
345+
346+
// Assert
347+
A
348+
.CallTo(HandleMethodFor(TopLevelErrorHandler))
349+
.MustNotHaveHappened();
350+
A
351+
.CallTo(HandleMethodFor(DefLevelErrorHandler))
352+
.MustHaveHappenedOnceExactly();
353+
}
354+
241355
#region Helpers
242356

243357
private IWorkflowMiddleware BuildWorkflowMiddleware(

0 commit comments

Comments
 (0)