diff --git a/.github/workflows/elsa-server-and-studio.yml b/.github/workflows/elsa-server-and-studio.yml index 8f58dbcc2..cbca2daf3 100644 --- a/.github/workflows/elsa-server-and-studio.yml +++ b/.github/workflows/elsa-server-and-studio.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: push: branches: - - patch/3.2.x + - blueberry jobs: push_to_registry: @@ -29,7 +29,7 @@ jobs: with: # list of Docker images to use as base name for tags images: | - elsaworkflows/elsa-server-and-studio-v3-2-1-preview + elsaworkflows/elsa-server-and-studio-v3-2-1-blueberry flavor: | latest=true # generate Docker tags based on the following events/attributes diff --git a/.github/workflows/elsa-server.yml b/.github/workflows/elsa-server.yml index 82a92a31b..e305d5072 100644 --- a/.github/workflows/elsa-server.yml +++ b/.github/workflows/elsa-server.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: push: branches: - - patch/3.2.x + - blueberry jobs: push_to_registry: @@ -29,7 +29,7 @@ jobs: with: # list of Docker images to use as base name for tags images: | - elsaworkflows/elsa-server-v3-2-1-preview + elsaworkflows/elsa-server-v3-2-1-blueberry flavor: | latest=true # generate Docker tags based on the following events/attributes diff --git a/.github/workflows/elsa-studio.yml b/.github/workflows/elsa-studio.yml index a8673a0dd..a275af27d 100644 --- a/.github/workflows/elsa-studio.yml +++ b/.github/workflows/elsa-studio.yml @@ -3,7 +3,7 @@ on: workflow_dispatch: push: branches: - - patch/3.2.x + - blueberry jobs: push_to_registry: @@ -29,7 +29,7 @@ jobs: with: # list of Docker images to use as base name for tags images: | - elsaworkflows/elsa-studio-v3-2-1-preview + elsaworkflows/elsa-studio-v3-2-1-blueberry flavor: | latest=true # generate Docker tags based on the following events/attributes diff --git a/.github/workflows/packages.yml b/.github/workflows/packages.yml index c4461693c..2869399e3 100644 --- a/.github/workflows/packages.yml +++ b/.github/workflows/packages.yml @@ -3,18 +3,7 @@ on: workflow_dispatch: push: branches: - - 'main' - - 'feature/*' - - 'feat/*' - - 'issue/*' - - 'bug/*' - - 'enhancement/*' - - 'enh/*' - - 'patch/*' - - 'fix/*' - - 'perf/*' - - 'hotfix/*' - - 'chore/*' + - 'blueberry' release: types: [ prereleased, published ] env: @@ -62,7 +51,7 @@ jobs: TAG_NAME=${TAG_NAME#refs/tags/} # remove the refs/tags/ prefix echo "VERSION=${TAG_NAME}" >> $GITHUB_ENV else - echo "VERSION=3.2.1-preview.${{github.run_number}}" >> $GITHUB_ENV + echo "VERSION=3.2.1-blueberry.${{github.run_number}}" >> $GITHUB_ENV fi - name: Set up JDK 17 uses: actions/setup-java@v2 diff --git a/Elsa.sln b/Elsa.sln index 3d9bca0c0..58f882779 100644 --- a/Elsa.sln +++ b/Elsa.sln @@ -355,6 +355,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Workflows.PerformanceT EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.MongoDb.UnitTests", "test\unit\Elsa.MongoDb.UnitTests\Elsa.MongoDb.UnitTests.csproj", "{56CAA9F2-1882-4EFA-BAC0-9C3D804553F1}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.OpenTelemetry", "src\modules\Elsa.OpenTelemetry\Elsa.OpenTelemetry.csproj", "{25880971-403E-4872-93A1-D33089E07C91}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -844,6 +846,10 @@ Global {56CAA9F2-1882-4EFA-BAC0-9C3D804553F1}.Debug|Any CPU.Build.0 = Debug|Any CPU {56CAA9F2-1882-4EFA-BAC0-9C3D804553F1}.Release|Any CPU.ActiveCfg = Release|Any CPU {56CAA9F2-1882-4EFA-BAC0-9C3D804553F1}.Release|Any CPU.Build.0 = Release|Any CPU + {25880971-403E-4872-93A1-D33089E07C91}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {25880971-403E-4872-93A1-D33089E07C91}.Debug|Any CPU.Build.0 = Debug|Any CPU + {25880971-403E-4872-93A1-D33089E07C91}.Release|Any CPU.ActiveCfg = Release|Any CPU + {25880971-403E-4872-93A1-D33089E07C91}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -991,6 +997,7 @@ Global {CBB515F3-A0EF-43B5-A907-FD4E652DD66E} = {90031D64-CA0F-46D0-9AF4-8DC023A5FFCD} {90CD37A9-C866-4D90-A3B1-8C87F53B845E} = {CBB515F3-A0EF-43B5-A907-FD4E652DD66E} {56CAA9F2-1882-4EFA-BAC0-9C3D804553F1} = {18453B51-25EB-4317-A4B3-B10518252E92} + {25880971-403E-4872-93A1-D33089E07C91} = {5BA4A8FA-F7F4-45B3-AEC8-8886D35AAC79} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {D4B5CEAA-7D70-4FCB-A68E-B03FBE5E0E5E} diff --git a/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj b/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj index 71cb87c96..ff8b01a9d 100644 --- a/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj +++ b/src/bundles/Elsa.Server.Web/Elsa.Server.Web.csproj @@ -12,6 +12,7 @@ + diff --git a/src/bundles/Elsa.Server.Web/Program.cs b/src/bundles/Elsa.Server.Web/Program.cs index 33328dace..d72e04aca 100644 --- a/src/bundles/Elsa.Server.Web/Program.cs +++ b/src/bundles/Elsa.Server.Web/Program.cs @@ -20,6 +20,7 @@ using Elsa.MongoDb.Modules.Alterations; using Elsa.MongoDb.Modules.Identity; using Elsa.MongoDb.Modules.Management; using Elsa.MongoDb.Modules.Runtime; +using Elsa.OpenTelemetry.Middleware; using Elsa.Server.Web; using Elsa.Workflows; using Elsa.Workflows.Management.Compression; @@ -131,6 +132,11 @@ services identity.UseConfigurationBasedRoleProvider(options => identitySection.Bind(options)); }) .UseDefaultAuthentication() + .UseWorkflows(workflows => + { + workflows.WithDefaultWorkflowExecutionPipeline(pipeline => pipeline.UseWorkflowExecutionTracing()); + workflows.WithDefaultActivityExecutionPipeline(pipeline => pipeline.UseActivityExecutionTracing()); + }) .UseWorkflowManagement(management => { if (useMongoDb) diff --git a/src/modules/Elsa.OpenTelemetry/Elsa.OpenTelemetry.csproj b/src/modules/Elsa.OpenTelemetry/Elsa.OpenTelemetry.csproj new file mode 100644 index 000000000..dc06e309b --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Elsa.OpenTelemetry.csproj @@ -0,0 +1,14 @@ + + + + + Provides OpenTelemetry sources for tracing workflow and activity execution. + + elsa module open-telemetry + + + + + + + \ No newline at end of file diff --git a/src/modules/Elsa.OpenTelemetry/Features/OpenTelemetryFeature.cs b/src/modules/Elsa.OpenTelemetry/Features/OpenTelemetryFeature.cs new file mode 100644 index 000000000..67201c44d --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Features/OpenTelemetryFeature.cs @@ -0,0 +1,11 @@ +using Elsa.Features.Abstractions; +using Elsa.Features.Services; + +namespace Elsa.OpenTelemetry.Features; + +public class OpenTelemetryFeature(IModule module) : FeatureBase(module) +{ + public override void Configure() + { + } +} \ No newline at end of file diff --git a/src/modules/Elsa.OpenTelemetry/FodyWeavers.xml b/src/modules/Elsa.OpenTelemetry/FodyWeavers.xml new file mode 100644 index 000000000..00e1d9a1c --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/FodyWeavers.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/src/modules/Elsa.OpenTelemetry/Helpers/OpenTelemetryHelpers.cs b/src/modules/Elsa.OpenTelemetry/Helpers/OpenTelemetryHelpers.cs new file mode 100644 index 000000000..302659456 --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Helpers/OpenTelemetryHelpers.cs @@ -0,0 +1,8 @@ +using System.Diagnostics; + +namespace Elsa.OpenTelemetry.Helpers; + +public class ElsaOpenTelemetry +{ + public static readonly ActivitySource ActivitySource = new("Elsa.Workflows"); +} \ No newline at end of file diff --git a/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingActivityExecutionMiddleware.cs b/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingActivityExecutionMiddleware.cs new file mode 100644 index 000000000..cd389bdb7 --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingActivityExecutionMiddleware.cs @@ -0,0 +1,42 @@ +using System.Diagnostics; +using Elsa.OpenTelemetry.Helpers; +using Elsa.Workflows; +using Elsa.Workflows.Contracts; +using Elsa.Workflows.Pipelines.ActivityExecution; +using Elsa.Workflows.Pipelines.WorkflowExecution; +using JetBrains.Annotations; +using Activity = System.Diagnostics.Activity; +using ActivityKind = System.Diagnostics.ActivityKind; + +namespace Elsa.OpenTelemetry.Middleware; + +/// +[UsedImplicitly] +public class OpenTelemetryTracingActivityExecutionMiddleware(ActivityMiddlewareDelegate next) : IActivityExecutionMiddleware +{ + /// + public async ValueTask InvokeAsync(ActivityExecutionContext context) + { + var activity = context.Activity; + using var span = ElsaOpenTelemetry.ActivitySource.StartActivity($"ActivityExecution {context.ActivityDescriptor.TypeName}", ActivityKind.Internal, Activity.Current?.Context ?? default); + span?.AddTag("activity.nodeId", activity.NodeId); + span?.AddTag("activity.type", activity.Type); + span?.AddTag("activity.name", activity.Name); + span?.AddTag("activityInstance.id", context.Id); + span?.AddTag("activityInstance.originalStatus", context.Status.ToString()); + span?.AddEvent(new ActivityEvent("Executing")); + await next(context); + span?.AddEvent(new ActivityEvent("Executed")); + span?.AddTag("activityInstance.newStatus", context.Status.ToString()); + } +} + +/// +/// Contains extension methods for . +/// +[UsedImplicitly] +public static class OpenTelemetryTracingActivityExecutionMiddlewareExtensions +{ + /// Installs the component in the workflow execution pipeline. + public static IActivityExecutionPipelineBuilder UseActivityExecutionTracing(this IActivityExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.Insert(0); +} diff --git a/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingWorkflowExecutionMiddleware.cs b/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingWorkflowExecutionMiddleware.cs new file mode 100644 index 000000000..ae6d67572 --- /dev/null +++ b/src/modules/Elsa.OpenTelemetry/Middleware/OpenTelemetryTracingWorkflowExecutionMiddleware.cs @@ -0,0 +1,49 @@ +using System.Diagnostics; +using Elsa.OpenTelemetry.Helpers; +using Elsa.Workflows; +using Elsa.Workflows.Contracts; +using Elsa.Workflows.Pipelines.WorkflowExecution; +using JetBrains.Annotations; +using Activity = System.Diagnostics.Activity; +using ActivityKind = System.Diagnostics.ActivityKind; + +namespace Elsa.OpenTelemetry.Middleware; + +/// +/// Middleware that traces workflow execution using OpenTelemetry. +/// +[UsedImplicitly] +public class OpenTelemetryTracingWorkflowExecutionMiddleware(WorkflowMiddlewareDelegate next) : WorkflowExecutionMiddleware(next) +{ + /// + public override async ValueTask InvokeAsync(WorkflowExecutionContext context) + { + var workflowInstanceId = context.Id; + var workflow = context.Workflow; + using var activity = ElsaOpenTelemetry.ActivitySource.StartActivity($"WorkflowExecution {workflow.WorkflowMetadata.Name}", ActivityKind.Internal, Activity.Current?.Context ?? default); + + if(!string.IsNullOrWhiteSpace(context.CorrelationId)) + activity?.AddTag("correlationId", context.CorrelationId); + + activity?.AddTag("workflowInstance.id", workflowInstanceId); + activity?.AddTag("workflowDefinition.definitionId", workflow.Identity.DefinitionId); + activity?.AddTag("workflowDefinition.version", workflow.Identity.Version); + activity?.AddTag("workflowInstance.originalStatus", context.Status.ToString()); + activity?.AddTag("workflowInstance.originalSubStatus", context.SubStatus.ToString()); + activity?.AddEvent(new ActivityEvent("Executing")); + await Next(context); + activity?.AddEvent(new ActivityEvent("Executed")); + activity?.AddTag("workflowInstance.newStatus", context.Status.ToString()); + activity?.AddTag("workflowInstance.newSubStatus", context.SubStatus.ToString()); + } +} + +/// +/// Contains extension methods for . +/// +[UsedImplicitly] +public static class OpenTelemetryWorkflowExecutionMiddlewareExtensions +{ + /// Installs the component in the workflow execution pipeline. + public static IWorkflowExecutionPipelineBuilder UseWorkflowExecutionTracing(this IWorkflowExecutionPipelineBuilder pipelineBuilder) => pipelineBuilder.Insert(0); +} diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IActivityExecutionPipelineBuilder.cs b/src/modules/Elsa.Workflows.Core/Contracts/IActivityExecutionPipelineBuilder.cs index 7c7968451..6cf6c926d 100644 --- a/src/modules/Elsa.Workflows.Core/Contracts/IActivityExecutionPipelineBuilder.cs +++ b/src/modules/Elsa.Workflows.Core/Contracts/IActivityExecutionPipelineBuilder.cs @@ -19,6 +19,9 @@ public interface IActivityExecutionPipelineBuilder /// The current . IActivityExecutionPipelineBuilder Use(Func middleware); + /// Inserts the middleware component at the specified index. + IActivityExecutionPipelineBuilder Insert(int index, Func middleware); + /// /// Constructs the final delegate that invokes each installed middleware component. /// diff --git a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowExecutionPipelineBuilder.cs b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowExecutionPipelineBuilder.cs index a2f37c9b0..04ce5ff62 100644 --- a/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowExecutionPipelineBuilder.cs +++ b/src/modules/Elsa.Workflows.Core/Contracts/IWorkflowExecutionPipelineBuilder.cs @@ -36,6 +36,9 @@ public interface IWorkflowExecutionPipelineBuilder /// Clears the current pipeline. /// IWorkflowExecutionPipelineBuilder Reset(); + + /// Inserts the middleware component at the specified index. + IWorkflowExecutionPipelineBuilder Insert(int index, Func middleware); /// /// Replaces the middleware component at the specified index with the specified delegate. diff --git a/src/modules/Elsa.Workflows.Core/Pipelines/ActivityExecution/ActivityExecutionMiddlewareExtensions.cs b/src/modules/Elsa.Workflows.Core/Pipelines/ActivityExecution/ActivityExecutionMiddlewareExtensions.cs index 487b41340..c42152859 100644 --- a/src/modules/Elsa.Workflows.Core/Pipelines/ActivityExecution/ActivityExecutionMiddlewareExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Pipelines/ActivityExecution/ActivityExecutionMiddlewareExtensions.cs @@ -1,3 +1,4 @@ +using System.Diagnostics.CodeAnalysis; using Elsa.Workflows.Contracts; using Microsoft.Extensions.DependencyInjection; @@ -5,16 +6,33 @@ namespace Elsa.Workflows.Pipelines.ActivityExecution; public static class ActivityExecutionMiddlewareExtensions { - public static IActivityExecutionPipelineBuilder UseMiddleware(this IActivityExecutionPipelineBuilder pipelineBuilder, params object[] args) where TMiddleware : IActivityExecutionMiddleware + public static IActivityExecutionPipelineBuilder UseMiddleware<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMiddleware>(this IActivityExecutionPipelineBuilder pipelineBuilder, params object[] args) where TMiddleware : IActivityExecutionMiddleware + { + var delegateFactory = CreateMiddlewareDelegateFactory(pipelineBuilder, args); + return pipelineBuilder.Use(delegateFactory); + } + + public static IActivityExecutionPipelineBuilder Insert<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)]TMiddleware>(this IActivityExecutionPipelineBuilder pipelineBuilder, int index, params object[] args) where TMiddleware : IActivityExecutionMiddleware + { + var delegateFactory = CreateMiddlewareDelegateFactory(pipelineBuilder, args); + return pipelineBuilder.Insert(index, delegateFactory); + } + + /// Creates a middleware delegate for the specified middleware component. + public static Func CreateMiddlewareDelegateFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMiddleware>( + this IActivityExecutionPipelineBuilder pipelineBuilder, params object[] args) where TMiddleware : IActivityExecutionMiddleware { var middleware = typeof(TMiddleware); - return pipelineBuilder.Use(next => + return next => { var invokeMethod = MiddlewareHelpers.GetInvokeMethod(middleware); - var ctorArgs = new[] { next }.Concat(args).Select(x => x!).ToArray(); + var ctorArgs = new[] + { + next + }.Concat(args).Select(x => x).ToArray(); var instance = ActivatorUtilities.CreateInstance(pipelineBuilder.ServiceProvider, middleware, ctorArgs); return (ActivityMiddlewareDelegate)invokeMethod.CreateDelegate(typeof(ActivityMiddlewareDelegate), instance); - }); + }; } } \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Core/Pipelines/ActivityExecution/ActivityExecutionPipelinePipelineBuilder.cs b/src/modules/Elsa.Workflows.Core/Pipelines/ActivityExecution/ActivityExecutionPipelinePipelineBuilder.cs index 971ebfbca..b64289f03 100644 --- a/src/modules/Elsa.Workflows.Core/Pipelines/ActivityExecution/ActivityExecutionPipelinePipelineBuilder.cs +++ b/src/modules/Elsa.Workflows.Core/Pipelines/ActivityExecution/ActivityExecutionPipelinePipelineBuilder.cs @@ -25,6 +25,13 @@ public class ActivityExecutionPipelinePipelineBuilder : IActivityExecutionPipeli return this; } + /// + public IActivityExecutionPipelineBuilder Insert(int index, Func middleware) + { + _components.Insert(index, middleware); + return this; + } + /// public ActivityMiddlewareDelegate Build() { diff --git a/src/modules/Elsa.Workflows.Core/Pipelines/WorkflowExecution/WorkflowExecutionMiddlewareExtensions.cs b/src/modules/Elsa.Workflows.Core/Pipelines/WorkflowExecution/WorkflowExecutionMiddlewareExtensions.cs index 4e7221c11..4007c8a16 100644 --- a/src/modules/Elsa.Workflows.Core/Pipelines/WorkflowExecution/WorkflowExecutionMiddlewareExtensions.cs +++ b/src/modules/Elsa.Workflows.Core/Pipelines/WorkflowExecution/WorkflowExecutionMiddlewareExtensions.cs @@ -4,44 +4,42 @@ using Microsoft.Extensions.DependencyInjection; namespace Elsa.Workflows.Pipelines.WorkflowExecution; -/// /// Provides extensions to that adds support for installing components. -/// public static class WorkflowExecutionMiddlewareExtensions { - /// /// Installs the specified middleware component into the pipeline being built. - /// public static IWorkflowExecutionPipelineBuilder UseMiddleware<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMiddleware>( this IWorkflowExecutionPipelineBuilder pipelineBuilder, params object[] args) where TMiddleware : IWorkflowExecutionMiddleware { var delegateFactory = CreateMiddlewareDelegateFactory(pipelineBuilder, args); return pipelineBuilder.Use(delegateFactory); } + + /// Installs the specified middleware component into the pipeline being built. + public static IWorkflowExecutionPipelineBuilder Insert<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMiddleware>( + this IWorkflowExecutionPipelineBuilder pipelineBuilder, int index, params object[] args) where TMiddleware : IWorkflowExecutionMiddleware + { + var delegateFactory = CreateMiddlewareDelegateFactory(pipelineBuilder, args); + return pipelineBuilder.Insert(index, delegateFactory); + } - /// /// Replaces the terminal middleware component with the specified middleware component. - /// public static IWorkflowExecutionPipelineBuilder ReplaceTerminal<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMiddleware>( this IWorkflowExecutionPipelineBuilder pipelineBuilder, params object[] args) where TMiddleware : IWorkflowExecutionMiddleware { var index = pipelineBuilder.Components.Count() - 1; return pipelineBuilder.Replace(index, args); } - - /// + /// Replaces the middleware component at the specified index with the specified middleware component. - /// public static IWorkflowExecutionPipelineBuilder Replace<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMiddleware>( this IWorkflowExecutionPipelineBuilder pipelineBuilder, int index, params object[] args) where TMiddleware : IWorkflowExecutionMiddleware { var delegateFactory = CreateMiddlewareDelegateFactory(pipelineBuilder, args); return pipelineBuilder.Replace(index, delegateFactory); } - - /// + /// Creates a middleware delegate for the specified middleware component. - /// public static Func CreateMiddlewareDelegateFactory<[DynamicallyAccessedMembers(DynamicallyAccessedMemberTypes.PublicConstructors)] TMiddleware>( this IWorkflowExecutionPipelineBuilder pipelineBuilder, params object[] args) where TMiddleware : IWorkflowExecutionMiddleware { diff --git a/src/modules/Elsa.Workflows.Core/Pipelines/WorkflowExecution/WorkflowExecutionPipelineBuilder.cs b/src/modules/Elsa.Workflows.Core/Pipelines/WorkflowExecution/WorkflowExecutionPipelineBuilder.cs index c61c6c4e5..940461e8f 100644 --- a/src/modules/Elsa.Workflows.Core/Pipelines/WorkflowExecution/WorkflowExecutionPipelineBuilder.cs +++ b/src/modules/Elsa.Workflows.Core/Pipelines/WorkflowExecution/WorkflowExecutionPipelineBuilder.cs @@ -54,6 +54,14 @@ public class WorkflowExecutionPipelineBuilder : IWorkflowExecutionPipelineBuilde return this; } + /// + public IWorkflowExecutionPipelineBuilder Insert(int index, Func middleware) + { + _components.Insert(index, middleware); + return this; + } + + /// public IWorkflowExecutionPipelineBuilder Replace(int index, Func middleware) { _components[index] = middleware; diff --git a/src/modules/Elsa.Workflows.Runtime/Extensions/PipelineWorkflowsFeatureExtensions.cs b/src/modules/Elsa.Workflows.Runtime/Extensions/PipelineWorkflowsFeatureExtensions.cs new file mode 100644 index 000000000..b68a86779 --- /dev/null +++ b/src/modules/Elsa.Workflows.Runtime/Extensions/PipelineWorkflowsFeatureExtensions.cs @@ -0,0 +1,35 @@ +using Elsa.Workflows.Contracts; +using Elsa.Workflows.Features; +using Elsa.Workflows.Middleware.Activities; + +// ReSharper disable once CheckNamespace +namespace Elsa.Extensions; + +/// Adds an extension method to the that installs a default workflow runtime execution pipeline. +public static class PipelineWorkflowsFeatureExtensions +{ + /// Installs a default workflow runtime execution pipeline. + public static WorkflowsFeature WithDefaultWorkflowExecutionPipeline(this WorkflowsFeature workflowsFeature, Action? configurePipeline = null) + { + return workflowsFeature.WithWorkflowExecutionPipeline(pipeline => + { + pipeline.UseDefaultPipeline(); + configurePipeline?.Invoke(pipeline); + }); + } + + /// Installs an activity invoker that can run activities asynchronously in the background. + public static WorkflowsFeature WithDefaultActivityExecutionPipeline(this WorkflowsFeature workflowsFeature, Action? configurePipeline = null) + { + return workflowsFeature.WithActivityExecutionPipeline(pipeline => + { + pipeline + .UseExceptionHandling() + .UseExecutionLogging() + .UseNotifications() + .UseBackgroundActivityInvoker(); + + configurePipeline?.Invoke(pipeline); + }); + } +} \ No newline at end of file diff --git a/src/modules/Elsa.Workflows.Runtime/Extensions/WorkflowsFeatureExtensions.cs b/src/modules/Elsa.Workflows.Runtime/Extensions/WorkflowsFeatureExtensions.cs index 9d8bda420..7031ca4a1 100644 --- a/src/modules/Elsa.Workflows.Runtime/Extensions/WorkflowsFeatureExtensions.cs +++ b/src/modules/Elsa.Workflows.Runtime/Extensions/WorkflowsFeatureExtensions.cs @@ -6,6 +6,7 @@ using Elsa.Workflows.Middleware.Activities; // ReSharper disable once CheckNamespace using Elsa.Workflows.Features; +// ReSharper disable once CheckNamespace namespace Elsa.Extensions; ///