From 83d0e14248d8ff92e33b0f06b7730c3dda664b10 Mon Sep 17 00:00:00 2001 From: Sipke Schoorstra Date: Fri, 16 Aug 2024 13:16:47 +0200 Subject: [PATCH] Add new HTTP workflow tests and enhance middleware Introduce tests for resuming specific HTTP workflow instances. Enhanced `HttpWorkflowsMiddleware` to handle workflow instance IDs and correlation IDs for more precise control over workflow activation and resumption. --- .../Middleware/HttpWorkflowsMiddleware.cs | 25 ++++++------ .../Elsa.Workflows.ComponentTests.csproj | 6 +-- ...ResumeSpecificHttpWorkflowInstanceTests.cs | 40 +++++++++++++++++++ .../Workflows/SimpleHttpApiWorkflow.cs | 30 ++++++++++++++ .../{ => Workflows}/http-hello-world.json | 0 5 files changed, 86 insertions(+), 15 deletions(-) create mode 100644 test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/ResumeSpecificHttpWorkflowInstanceTests.cs create mode 100644 test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/Workflows/SimpleHttpApiWorkflow.cs rename test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/{ => Workflows}/http-hello-world.json (100%) diff --git a/src/modules/Elsa.Http/Middleware/HttpWorkflowsMiddleware.cs b/src/modules/Elsa.Http/Middleware/HttpWorkflowsMiddleware.cs index 4df72c5db..0ec830789 100644 --- a/src/modules/Elsa.Http/Middleware/HttpWorkflowsMiddleware.cs +++ b/src/modules/Elsa.Http/Middleware/HttpWorkflowsMiddleware.cs @@ -69,6 +69,8 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions(); + var workflowInstanceId = await GetWorkflowInstanceIdAsync(serviceProvider, httpContext, cancellationToken); + var correlationId = await GetCorrelationIdAsync(serviceProvider, httpContext, cancellationToken); var bookmarkHash = ComputeBookmarkHash(serviceProvider, matchingPath, method); var lookupResult = await httpWorkflowLookupService.FindWorkflowAsync(bookmarkHash, cancellationToken); @@ -76,7 +78,7 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions 1) + if (triggers.Count > 1) { await HandleMultipleWorkflowsFoundAsync(httpContext, () => triggers.Select(x => new { @@ -85,16 +87,16 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions 1) { @@ -109,7 +111,7 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions input) + private async Task StartWorkflowAsync(HttpContext httpContext, StoredTrigger trigger, WorkflowGraph workflowGraph, string? workflowInstanceId, string? correlationId, Dictionary? input) { var serviceProvider = httpContext.RequestServices; var cancellationToken = httpContext.RequestAborted; @@ -144,8 +146,6 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions { var cancellationTokens = new CancellationTokens(ct, ct); - var workflowInstanceId = await GetWorkflowInstanceIdAsync(serviceProvider, httpContext, httpContext.RequestAborted); - var correlationId = await GetCorrelationIdAsync(serviceProvider, httpContext, httpContext.RequestAborted); var startParams = new StartWorkflowHostParams { Input = input, @@ -160,7 +160,7 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions input) + private async Task ResumeWorkflowAsync(HttpContext httpContext, StoredBookmark bookmark, string? correlationId, IDictionary input) { var serviceProvider = httpContext.RequestServices; var cancellationToken = httpContext.RequestAborted; @@ -193,7 +193,6 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions { - var correlationId = await GetCorrelationIdAsync(serviceProvider, httpContext, ct); var cancellationTokens = new CancellationTokens(ct, ct); var resumeParams = new ResumeWorkflowHostParams { @@ -219,12 +218,14 @@ public class HttpWorkflowsMiddleware(RequestDelegate next, IOptions> FindBookmarksAsync(IServiceProvider serviceProvider, string bookmarkHash, CancellationToken cancellationToken) + private async Task> FindBookmarksAsync(IServiceProvider serviceProvider, string bookmarkHash, string? workflowInstanceId, string? correlationId, CancellationToken cancellationToken) { var bookmarkStore = serviceProvider.GetRequiredService(); var bookmarkFilter = new BookmarkFilter { - Hash = bookmarkHash + Hash = bookmarkHash, + WorkflowInstanceId = workflowInstanceId, + CorrelationId = correlationId }; return await bookmarkStore.FindManyAsync(bookmarkFilter, cancellationToken); } diff --git a/test/component/Elsa.Workflows.ComponentTests/Elsa.Workflows.ComponentTests.csproj b/test/component/Elsa.Workflows.ComponentTests/Elsa.Workflows.ComponentTests.csproj index deb69e04d..eece3a52f 100644 --- a/test/component/Elsa.Workflows.ComponentTests/Elsa.Workflows.ComponentTests.csproj +++ b/test/component/Elsa.Workflows.ComponentTests/Elsa.Workflows.ComponentTests.csproj @@ -30,9 +30,6 @@ Always - - Always - Always @@ -81,6 +78,9 @@ Always + + Always + diff --git a/test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/ResumeSpecificHttpWorkflowInstanceTests.cs b/test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/ResumeSpecificHttpWorkflowInstanceTests.cs new file mode 100644 index 000000000..0f2708c68 --- /dev/null +++ b/test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/ResumeSpecificHttpWorkflowInstanceTests.cs @@ -0,0 +1,40 @@ +using System.Net; +using Elsa.Workflows.Contracts; +using Microsoft.Extensions.DependencyInjection; + +namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows; + +public class ResumeSpecificHttpWorkflowInstanceTests(App app) : AppComponentTest(app) +{ + [Theory] + [InlineData("workflowInstanceId")] + [InlineData("correlationId")] + public async Task ResumingSpecificWorkflow_ShouldResumeSpecifiedWorkflow(string identifierKey) + { + var client = WorkflowServer.CreateHttpWorkflowClient(); + + // Start 3 instances. + var workflowInstanceId1 = await StartWorkflowAsync(client, identifierKey); + var workflowInstanceId2 = await StartWorkflowAsync(client, identifierKey); + var workflowInstanceId3 = await StartWorkflowAsync(client, identifierKey); + + // Resume the 2nd instance. + var response = await ResumeWorkflowAsync(client, identifierKey, workflowInstanceId2); + + // Response should be OK. + Assert.Equal(HttpStatusCode.OK, response.StatusCode); + } + + private async Task StartWorkflowAsync(HttpClient client, string identifierKey) + { + var identityGenerator = Scope.ServiceProvider.GetRequiredService(); + var identifierValue = identityGenerator.GenerateId(); + await client.GetStringAsync($"simple-http-api/start?{identifierKey}={identifierValue}"); + return identifierValue; + } + + private async Task ResumeWorkflowAsync(HttpClient client, string identifierKey, string identifierValue) + { + return await client.GetAsync($"simple-http-api/resume?{identifierKey}={identifierValue}"); + } +} \ No newline at end of file diff --git a/test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/Workflows/SimpleHttpApiWorkflow.cs b/test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/Workflows/SimpleHttpApiWorkflow.cs new file mode 100644 index 000000000..be17a0bad --- /dev/null +++ b/test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/Workflows/SimpleHttpApiWorkflow.cs @@ -0,0 +1,30 @@ +using Elsa.Http; +using Elsa.Workflows.Activities; +using Elsa.Workflows.Contracts; +using Microsoft.AspNetCore.Http; + +namespace Elsa.Workflows.ComponentTests.Scenarios.HttpWorkflows.Workflows; + +public class SimpleHttpApiWorkflow : WorkflowBase +{ + protected override void Build(IWorkflowBuilder builder) + { + builder.Root = new Sequence + { + Activities = + [ + new HttpEndpoint + { + Path = new("simple-http-api/start"), + SupportedMethods = new([HttpMethods.Get]), + CanStartWorkflow = true + }, + new HttpEndpoint + { + Path = new("simple-http-api/resume"), + SupportedMethods = new([HttpMethods.Get]) + } + ] + }; + } +} \ No newline at end of file diff --git a/test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/http-hello-world.json b/test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/Workflows/http-hello-world.json similarity index 100% rename from test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/http-hello-world.json rename to test/component/Elsa.Workflows.ComponentTests/Scenarios/HttpWorkflows/Workflows/http-hello-world.json