Improve trigger API and implement HTTP correlation
This commit is contained in:
parent
b51aa6d7a6
commit
6577b0cd38
|
|
@ -106,6 +106,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.WhileLoopWorke
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.IfElseConsole", "src\samples\Elsa.Samples.IfElseConsole\Elsa.Samples.IfElseConsole.csproj", "{E508C1B6-916F-40F4-8731-58279B8F45B2}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Elsa.Samples.CorrelationHttp", "src\samples\Elsa.Samples.CorrelationHttp\Elsa.Samples.CorrelationHttp.csproj", "{565E2F7D-1F5F-45EF-B130-BC09CB3465B1}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -249,6 +251,10 @@ Global
|
|||
{E508C1B6-916F-40F4-8731-58279B8F45B2}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E508C1B6-916F-40F4-8731-58279B8F45B2}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E508C1B6-916F-40F4-8731-58279B8F45B2}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{565E2F7D-1F5F-45EF-B130-BC09CB3465B1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{565E2F7D-1F5F-45EF-B130-BC09CB3465B1}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{565E2F7D-1F5F-45EF-B130-BC09CB3465B1}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{565E2F7D-1F5F-45EF-B130-BC09CB3465B1}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -298,6 +304,7 @@ Global
|
|||
{6B83CCE1-D523-4E3F-84EB-8D79AAF6F7C2} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
|
||||
{85A70E7C-A4AC-49BA-942B-635F974DE255} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
|
||||
{E508C1B6-916F-40F4-8731-58279B8F45B2} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
|
||||
{565E2F7D-1F5F-45EF-B130-BC09CB3465B1} = {5E5E1E84-DDBC-40D6-B891-0D563A15A44A}
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,6 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
|
@ -38,5 +39,23 @@ namespace Elsa.Activities.Http.Extensions
|
|||
var absoluteUrl = $"{request.Scheme}://{request.Host}{relativePath}";
|
||||
return new Uri(absoluteUrl, UriKind.Absolute);
|
||||
}
|
||||
|
||||
public static bool TryGetCorrelationId(this HttpRequest request, out string? correlationId)
|
||||
{
|
||||
if (request.Query.ContainsKey("correlation"))
|
||||
{
|
||||
correlationId = request.Query["correlation"];
|
||||
return true;
|
||||
}
|
||||
|
||||
if (request.Headers.ContainsKey("X-Correlation-Id"))
|
||||
{
|
||||
correlationId = request.Headers["X-Correlation-Id"].ToString();
|
||||
return true;
|
||||
}
|
||||
|
||||
correlationId = null;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1,9 +1,14 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Http.Extensions;
|
||||
using Elsa.Activities.Http.Triggers;
|
||||
using Elsa.Extensions;
|
||||
using Elsa.Services;
|
||||
using Elsa.Triggers;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
using Open.Linq.AsyncExtensions;
|
||||
|
||||
namespace Elsa.Activities.Http.Middleware
|
||||
{
|
||||
|
|
@ -16,22 +21,49 @@ namespace Elsa.Activities.Http.Middleware
|
|||
_next = next;
|
||||
}
|
||||
|
||||
public async Task InvokeAsync(HttpContext httpContext, IWorkflowSelector workflowSelector, IWorkflowRunner workflowRunner)
|
||||
public async Task InvokeAsync(HttpContext httpContext, IWorkflowSelector workflowSelector, IWorkflowRunner workflowRunner, IWorkflowInstanceManager workflowInstanceManager)
|
||||
{
|
||||
var path = httpContext.Request.Path;
|
||||
var method = httpContext.Request.Method;
|
||||
var cancellationToken = httpContext.RequestAborted;
|
||||
httpContext.Request.TryGetCorrelationId(out var correlationId);
|
||||
|
||||
var results = await workflowSelector.SelectWorkflowsAsync<ReceiveHttpRequestTrigger>(
|
||||
x => x.Path == path && x.Method == null || x.Method == method,
|
||||
cancellationToken);
|
||||
x => (x.Path == path) && (x.Method == null || x.Method == method) && (x.CorrelationId == null || x.CorrelationId == correlationId),
|
||||
cancellationToken)
|
||||
.ToList();
|
||||
|
||||
var result = results.FirstOrDefault();
|
||||
|
||||
if (result == null)
|
||||
if (!results.Any())
|
||||
{
|
||||
await _next(httpContext);
|
||||
return;
|
||||
}
|
||||
|
||||
if (results.Count > 1)
|
||||
{
|
||||
httpContext.Response.StatusCode = (int)HttpStatusCode.BadRequest;
|
||||
await httpContext.Response.WriteAsync("Request matches multiple workflows.", cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
var result = results.Single();
|
||||
|
||||
if (result.WorkflowInstanceId == null)
|
||||
{
|
||||
await workflowRunner.RunWorkflowAsync(result.WorkflowBlueprint, result.ActivityId, cancellationToken: cancellationToken);
|
||||
}
|
||||
else
|
||||
await workflowRunner.RunWorkflowAsync(result.WorkflowBlueprint, result.WorkflowInstance, result.ActivityId, cancellationToken: cancellationToken);
|
||||
{
|
||||
var workflowInstance = await workflowInstanceManager.GetByIdAsync(result.WorkflowInstanceId, cancellationToken);
|
||||
|
||||
if (workflowInstance == null)
|
||||
{
|
||||
httpContext.Response.StatusCode = 404;
|
||||
return;
|
||||
}
|
||||
|
||||
await workflowRunner.RunWorkflowAsync(result.WorkflowBlueprint, workflowInstance, result.ActivityId, cancellationToken: cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@ namespace Elsa.Activities.Http.Triggers
|
|||
{
|
||||
public PathString Path { get; set; }
|
||||
public string? Method { get; set; }
|
||||
public string? CorrelationId { get; set; }
|
||||
}
|
||||
|
||||
public class ReceiveHttpRequestTriggerProvider : TriggerProvider<ReceiveHttpRequestTrigger, ReceiveHttpRequest>
|
||||
|
|
@ -17,7 +18,8 @@ namespace Elsa.Activities.Http.Triggers
|
|||
new ReceiveHttpRequestTrigger
|
||||
{
|
||||
Path = await context.Activity.GetPropertyValueAsync(x => x.Path, cancellationToken),
|
||||
Method = await context.Activity.GetPropertyValueAsync(x => x.Method, cancellationToken)
|
||||
Method = await context.Activity.GetPropertyValueAsync(x => x.Method, cancellationToken),
|
||||
CorrelationId = context.ActivityExecutionContext.WorkflowExecutionContext.CorrelationId
|
||||
};
|
||||
}
|
||||
}
|
||||
15
src/core/Elsa.Abstractions/Events/WorkflowInstanceDeleted.cs
Normal file
15
src/core/Elsa.Abstractions/Events/WorkflowInstanceDeleted.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using Elsa.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace Elsa.Events
|
||||
{
|
||||
public class WorkflowInstanceDeleted : INotification
|
||||
{
|
||||
public WorkflowInstanceDeleted(WorkflowInstance workflowInstance)
|
||||
{
|
||||
WorkflowInstance = workflowInstance;
|
||||
}
|
||||
|
||||
public WorkflowInstance WorkflowInstance { get; }
|
||||
}
|
||||
}
|
||||
15
src/core/Elsa.Abstractions/Events/WorkflowInstanceSaved.cs
Normal file
15
src/core/Elsa.Abstractions/Events/WorkflowInstanceSaved.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using Elsa.Models;
|
||||
using MediatR;
|
||||
|
||||
namespace Elsa.Events
|
||||
{
|
||||
public class WorkflowInstanceSaved : INotification
|
||||
{
|
||||
public WorkflowInstanceSaved(WorkflowInstance workflowInstance)
|
||||
{
|
||||
WorkflowInstance = workflowInstance;
|
||||
}
|
||||
|
||||
public WorkflowInstance WorkflowInstance { get; }
|
||||
}
|
||||
}
|
||||
|
|
@ -22,7 +22,19 @@ namespace Elsa.Models
|
|||
[JsonExtensionData] public JObject Data { get; set; }
|
||||
|
||||
public JToken? Get(string name) => Has(name) ? Data[name] : default;
|
||||
public T Get<T>(string name) => Has(name) ? Data.Value<T>(name) : default!;
|
||||
|
||||
public T Get<T>(string name)
|
||||
{
|
||||
if (!Has(name))
|
||||
return default!;
|
||||
|
||||
var value = Get(name);
|
||||
|
||||
if (value == null)
|
||||
return default!;
|
||||
|
||||
return value.ToObject<T>()!;
|
||||
}
|
||||
|
||||
public Variables Set(string name, JToken value)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -40,6 +40,7 @@ namespace Elsa.Services.Models
|
|||
public void SetVariable(string name, object? value) => WorkflowExecutionContext.SetVariable(name, value);
|
||||
public object? GetVariable(string name) => WorkflowExecutionContext.GetVariable(name);
|
||||
public T GetVariable<T>(string name) => WorkflowExecutionContext.GetVariable<T>(name);
|
||||
public T GetVariable<T>() => GetVariable<T>(typeof(T).Name);
|
||||
public T GetService<T>() => WorkflowExecutionContext.ServiceProvider.GetService<T>();
|
||||
|
||||
public async ValueTask SetActivityPropertiesAsync(
|
||||
|
|
|
|||
|
|
@ -56,7 +56,13 @@ namespace Elsa.Services.Models
|
|||
public void PostScheduleActivity(ScheduledActivity activity) => WorkflowInstance.PostScheduledActivities.Push(activity);
|
||||
public ScheduledActivity PopScheduledActivity() => WorkflowInstance.ScheduledActivities.Pop();
|
||||
public ScheduledActivity PeekScheduledActivity() => WorkflowInstance.ScheduledActivities.Peek();
|
||||
public string? CorrelationId { get; set; }
|
||||
|
||||
public string? CorrelationId
|
||||
{
|
||||
get => WorkflowInstance.CorrelationId;
|
||||
set => WorkflowInstance.CorrelationId = value;
|
||||
}
|
||||
|
||||
public bool DeleteCompletedInstances { get; set; }
|
||||
public ICollection<ExecutionLogEntry> ExecutionLog => WorkflowInstance.ExecutionLog;
|
||||
public WorkflowStatus Status => WorkflowInstance.Status;
|
||||
|
|
|
|||
|
|
@ -1,12 +1,11 @@
|
|||
using Elsa.Models;
|
||||
using Elsa.Services.Models;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
namespace Elsa.Triggers
|
||||
{
|
||||
public class TriggerDescriptor
|
||||
{
|
||||
public IWorkflowBlueprint WorkflowBlueprint { get; set; } = default!;
|
||||
public WorkflowInstance WorkflowInstance { get; set; } = default!;
|
||||
public string? WorkflowInstanceId { get; set; }
|
||||
public string ActivityId { get; set; } = default!;
|
||||
public string ActivityType { get; set; } = default!;
|
||||
public ITrigger Trigger { get; set; } = default!;
|
||||
|
|
|
|||
|
|
@ -5,16 +5,16 @@ namespace Elsa.Triggers
|
|||
{
|
||||
public class WorkflowSelectorResult
|
||||
{
|
||||
public WorkflowSelectorResult(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityId, ITrigger trigger)
|
||||
public WorkflowSelectorResult(IWorkflowBlueprint workflowBlueprint, string? workflowInstanceId, string activityId, ITrigger trigger)
|
||||
{
|
||||
WorkflowBlueprint = workflowBlueprint;
|
||||
WorkflowInstance = workflowInstance;
|
||||
WorkflowInstanceId = workflowInstanceId;
|
||||
ActivityId = activityId;
|
||||
Trigger = trigger;
|
||||
}
|
||||
|
||||
public IWorkflowBlueprint WorkflowBlueprint { get; }
|
||||
public WorkflowInstance WorkflowInstance { get; }
|
||||
public string? WorkflowInstanceId { get; }
|
||||
public string ActivityId { get; }
|
||||
public ITrigger Trigger { get; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -18,6 +18,6 @@ namespace Elsa.Activities.Primitives
|
|||
[ActivityProperty(Hint = "An expression that evaluates to the value to store as the correlation ID.")]
|
||||
public string Value { get; set; } = default!;
|
||||
|
||||
protected override IActivityExecutionResult OnExecute() => new CorrelateResult(Value);
|
||||
protected override IActivityExecutionResult OnExecute() => Combine(Done(Value), new CorrelateResult(Value));
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Primitives;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
|
||||
// ReSharper disable once CheckNamespace
|
||||
namespace Elsa.Activities.ControlFlow
|
||||
{
|
||||
public static class CorrelateBuilderExtensions
|
||||
{
|
||||
public static IOutcomeBuilder Correlate(this IBuilder builder, Action<ISetupActivity<Correlate>> setup) => builder.Then(setup).When(OutcomeNames.Done);
|
||||
public static IOutcomeBuilder Correlate(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<string>> value) => builder.Correlate(activity => activity.Set(x => x.Value, value));
|
||||
public static IOutcomeBuilder Correlate(this IBuilder builder, Func<ActivityExecutionContext, string> value) => builder.Correlate(activity => activity.Set(x => x.Value, value));
|
||||
public static IOutcomeBuilder Correlate(this IBuilder builder, Func<ValueTask<string>> value) => builder.Correlate(activity => activity.Set(x => x.Value, value));
|
||||
public static IOutcomeBuilder Correlate(this IBuilder builder, Func<string> value) => builder.Correlate(activity => activity.Set(x => x.Value, value));
|
||||
public static IOutcomeBuilder Correlate(this IBuilder builder, string value) => builder.Correlate(activity => activity.Set(x => x.Value, value));
|
||||
}
|
||||
}
|
||||
|
|
@ -10,8 +10,7 @@ namespace Elsa.Activities.Primitives
|
|||
DisplayName = "Set Variable",
|
||||
Description = "Set variable on the workflow.",
|
||||
Category = "Primitives",
|
||||
RuntimeDescription =
|
||||
"x => !!x.state.variableName ? `<strong>${x.state.variableName}</strong> = <strong>${x.state.valueExpression.expression}</strong><br/>${x.state.valueExpression.syntax}` : x.definition.description",
|
||||
RuntimeDescription = "x => !!x.state.variableName ? `<strong>${x.state.variableName}</strong> = <strong>${x.state.valueExpression.expression}</strong><br/>${x.state.valueExpression.syntax}` : x.definition.description",
|
||||
Outcomes = new[] { OutcomeNames.Done }
|
||||
)]
|
||||
public class SetVariable : Activity
|
||||
|
|
|
|||
|
|
@ -1,4 +1,5 @@
|
|||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Activities.Primitives;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Services.Models;
|
||||
|
|
@ -10,12 +11,24 @@ namespace Elsa.Activities.ControlFlow
|
|||
{
|
||||
public static IOutcomeBuilder SetVariable(this IBuilder builder, Action<ISetupActivity<SetVariable>> setup) => builder.Then(setup).When(OutcomeNames.Done);
|
||||
|
||||
public static IOutcomeBuilder SetVariable(this IBuilder builder, string name, Func<ActivityExecutionContext, ValueTask<object?>> value) =>
|
||||
builder.SetVariable(
|
||||
activity => activity
|
||||
.Set(x => x.VariableName, name)
|
||||
.Set(x => x.Value, value));
|
||||
|
||||
public static IOutcomeBuilder SetVariable(this IBuilder builder, string name, Func<ActivityExecutionContext, object?> value) =>
|
||||
builder.SetVariable(
|
||||
activity => activity
|
||||
.Set(x => x.VariableName, name)
|
||||
.Set(x => x.Value, value));
|
||||
|
||||
public static IOutcomeBuilder SetVariable(this IBuilder builder, string name, Func<ValueTask<object?>> value) =>
|
||||
builder.SetVariable(
|
||||
activity => activity
|
||||
.Set(x => x.VariableName, name)
|
||||
.Set(x => x.Value, value));
|
||||
|
||||
public static IOutcomeBuilder SetVariable(this IBuilder builder, string name, Func<object?> value) =>
|
||||
builder.SetVariable(
|
||||
activity => activity
|
||||
|
|
@ -27,5 +40,11 @@ namespace Elsa.Activities.ControlFlow
|
|||
activity => activity
|
||||
.Set(x => x.VariableName, name)
|
||||
.Set(x => x.Value, value));
|
||||
|
||||
public static IOutcomeBuilder SetVariable<T>(this IBuilder builder, Func<ActivityExecutionContext, ValueTask<T>> value) => builder.SetVariable(typeof(T).Name, async context => await value(context));
|
||||
public static IOutcomeBuilder SetVariable<T>(this IBuilder builder, Func<ActivityExecutionContext, T> value) => builder.SetVariable(typeof(T).Name, context => value(context));
|
||||
public static IOutcomeBuilder SetVariable<T>(this IBuilder builder, Func<ValueTask<T>> value) => builder.SetVariable(typeof(T).Name, async () => await value());
|
||||
public static IOutcomeBuilder SetVariable<T>(this IBuilder builder, Func<T> value) => builder.SetVariable(typeof(T).Name, () => value());
|
||||
public static IOutcomeBuilder SetVariable<T>(this IBuilder builder, T value) => builder.SetVariable(typeof(T).Name, value);
|
||||
}
|
||||
}
|
||||
|
|
@ -7,7 +7,6 @@ namespace Elsa.ActivityResults
|
|||
public CorrelateResult(string correlationId) => CorrelationId = correlationId;
|
||||
public string CorrelationId { get; }
|
||||
|
||||
protected override void Execute(ActivityExecutionContext activityExecutionContext) =>
|
||||
activityExecutionContext.WorkflowExecutionContext.CorrelationId = CorrelationId;
|
||||
protected override void Execute(ActivityExecutionContext activityExecutionContext) => activityExecutionContext.WorkflowExecutionContext.CorrelationId = CorrelationId;
|
||||
}
|
||||
}
|
||||
|
|
@ -47,9 +47,9 @@ namespace Elsa.Builders
|
|||
return this;
|
||||
}
|
||||
|
||||
public IActivityBuilder WithId(string id)
|
||||
public IActivityBuilder WithId(string? id)
|
||||
{
|
||||
ActivityId = id;
|
||||
ActivityId = id!;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,15 +1,29 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Events;
|
||||
using Elsa.Models;
|
||||
using Elsa.Services;
|
||||
using Elsa.Triggers;
|
||||
using MediatR;
|
||||
|
||||
namespace Elsa.Handlers
|
||||
{
|
||||
public class UpdateTriggers : INotificationHandler<WorkflowExecuted>
|
||||
public class UpdateTriggers : INotificationHandler<WorkflowInstanceSaved>
|
||||
{
|
||||
private readonly IWorkflowSelector _workflowSelector;
|
||||
public UpdateTriggers(IWorkflowSelector workflowSelector) => _workflowSelector = workflowSelector;
|
||||
public async Task Handle(WorkflowExecuted notification, CancellationToken cancellationToken) => await _workflowSelector.UpdateTriggersAsync(notification.WorkflowExecutionContext.WorkflowBlueprint, cancellationToken);
|
||||
private readonly IWorkflowRegistry _workflowRegistry;
|
||||
|
||||
public UpdateTriggers(IWorkflowSelector workflowSelector, IWorkflowRegistry workflowRegistry)
|
||||
{
|
||||
_workflowSelector = workflowSelector;
|
||||
_workflowRegistry = workflowRegistry;
|
||||
}
|
||||
|
||||
public async Task Handle(WorkflowInstanceSaved notification, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflowInstance = notification.WorkflowInstance;
|
||||
var workflowBlueprint = await _workflowRegistry.GetWorkflowAsync(workflowInstance.WorkflowDefinitionId, VersionOptions.SpecificVersion(workflowInstance.Version), cancellationToken);
|
||||
await _workflowSelector.UpdateTriggersAsync(workflowBlueprint!, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -3,7 +3,9 @@ using System.Linq.Expressions;
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Elsa.Data;
|
||||
using Elsa.Events;
|
||||
using Elsa.Models;
|
||||
using MediatR;
|
||||
using YesSql;
|
||||
using YesSql.Indexes;
|
||||
|
||||
|
|
@ -12,22 +14,26 @@ namespace Elsa.Services
|
|||
public class WorkflowInstanceManager : IWorkflowInstanceManager
|
||||
{
|
||||
private readonly ISession _session;
|
||||
private readonly IMediator _mediator;
|
||||
|
||||
public WorkflowInstanceManager(ISession session)
|
||||
public WorkflowInstanceManager(ISession session, IMediator mediator)
|
||||
{
|
||||
_session = session;
|
||||
_mediator = mediator;
|
||||
}
|
||||
|
||||
public async ValueTask SaveAsync(WorkflowInstance workflowInstance, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_session.Save(workflowInstance, CollectionNames.WorkflowInstances);
|
||||
await _session.CommitAsync();
|
||||
await _mediator.Publish(new WorkflowInstanceSaved(workflowInstance), cancellationToken);
|
||||
}
|
||||
|
||||
public async ValueTask DeleteAsync(WorkflowInstance workflowInstance, CancellationToken cancellationToken = default)
|
||||
{
|
||||
_session.Delete(workflowInstance, CollectionNames.WorkflowInstances);
|
||||
await _session.CommitAsync();
|
||||
await _mediator.Publish(new WorkflowInstanceDeleted(workflowInstance), cancellationToken);
|
||||
}
|
||||
|
||||
public IQuery<WorkflowInstance> Query() => _session.Query<WorkflowInstance>(CollectionNames.WorkflowInstances);
|
||||
|
|
|
|||
|
|
@ -211,12 +211,13 @@ namespace Elsa.Services
|
|||
private async ValueTask RunAsync(WorkflowExecutionContext workflowExecutionContext, ActivityOperation activityOperation, CancellationToken cancellationToken = default)
|
||||
{
|
||||
using var scope = _serviceProvider.CreateScope();
|
||||
var serviceProvider = scope.ServiceProvider;
|
||||
while (workflowExecutionContext.HasScheduledActivities)
|
||||
{
|
||||
var scheduledActivity = workflowExecutionContext.PopScheduledActivity();
|
||||
var currentActivityId = scheduledActivity.ActivityId;
|
||||
var activityBlueprint = workflowExecutionContext.WorkflowBlueprint.GetActivity(currentActivityId)!;
|
||||
var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, scope.ServiceProvider, activityBlueprint, scheduledActivity.Input);
|
||||
var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, serviceProvider, activityBlueprint, scheduledActivity.Input);
|
||||
var activity = await activityBlueprint.CreateActivityAsync(activityExecutionContext, cancellationToken);
|
||||
var result = await activityOperation(activityExecutionContext, activity, cancellationToken);
|
||||
await _mediator.Publish(new ActivityExecuting(activityExecutionContext), cancellationToken);
|
||||
|
|
|
|||
|
|
@ -95,8 +95,8 @@ namespace Elsa.Services
|
|||
|
||||
foreach (var result in results)
|
||||
{
|
||||
if (result.WorkflowInstance.Id > 0)
|
||||
await ScheduleWorkflowInstanceAsync(result.WorkflowInstance.WorkflowInstanceId, result.ActivityId, input, cancellationToken);
|
||||
if (result.WorkflowInstanceId != null)
|
||||
await ScheduleWorkflowInstanceAsync(result.WorkflowInstanceId, result.ActivityId, input, cancellationToken);
|
||||
else
|
||||
await ScheduleWorkflowAsync(result.WorkflowBlueprint, result.WorkflowBlueprint.GetActivity(result.ActivityId)!, input, correlationId, cancellationToken);
|
||||
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ namespace Elsa.Triggers
|
|||
where descriptor.Trigger == trigger
|
||||
select (entry.Key, descriptor);
|
||||
|
||||
foreach (var (key, descriptor) in query.ToList())
|
||||
foreach (var (key, descriptor) in query.ToList())
|
||||
_descriptors[key].Remove(descriptor);
|
||||
|
||||
_memoryCache.Set(CacheKey, _descriptors);
|
||||
|
|
@ -94,10 +94,10 @@ namespace Elsa.Triggers
|
|||
return from descriptor in descriptors
|
||||
let workflow = descriptor.WorkflowBlueprint
|
||||
where workflow.IsPublished && workflow.IsEnabled
|
||||
let workflowInstance = descriptor.WorkflowInstance
|
||||
let workflowInstanceId = descriptor.WorkflowInstanceId
|
||||
let trigger = descriptor.Trigger
|
||||
where trigger.GetType() == triggerType && evaluate(trigger)
|
||||
select new WorkflowSelectorResult(workflow, workflowInstance, descriptor.ActivityId, trigger);
|
||||
select new WorkflowSelectorResult(workflow, workflowInstanceId, descriptor.ActivityId, trigger);
|
||||
}
|
||||
|
||||
private async Task<IDictionary<string, ICollection<TriggerDescriptor>>> BuildDescriptorsAsync(CancellationToken cancellationToken)
|
||||
|
|
@ -171,7 +171,7 @@ namespace Elsa.Triggers
|
|||
ActivityId = blockingActivity.Id,
|
||||
ActivityType = blockingActivity.Type,
|
||||
WorkflowBlueprint = workflowBlueprint,
|
||||
WorkflowInstance = workflowInstance,
|
||||
WorkflowInstanceId = workflowInstance.Id == 0 ? default : workflowInstance.WorkflowInstanceId,
|
||||
Trigger = trigger,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk.Web">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netcoreapp3.1</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\core\Elsa\Elsa.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
namespace Elsa.Samples.CorrelationHttp.Models
|
||||
{
|
||||
public class Registration
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public string Email { get; set; }
|
||||
}
|
||||
}
|
||||
14
src/samples/Elsa.Samples.CorrelationHttp/Program.cs
Normal file
14
src/samples/Elsa.Samples.CorrelationHttp/Program.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
using Microsoft.AspNetCore.Hosting;
|
||||
using Microsoft.Extensions.Hosting;
|
||||
|
||||
namespace Elsa.Samples.CorrelationHttp
|
||||
{
|
||||
public class Program
|
||||
{
|
||||
public static void Main(string[] args) => CreateHostBuilder(args).Build().Run();
|
||||
|
||||
public static IHostBuilder CreateHostBuilder(string[] args) =>
|
||||
Host.CreateDefaultBuilder(args)
|
||||
.ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup<Startup>(); });
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"profiles": {
|
||||
"Elsa.Samples.LongRunningHttp": {
|
||||
"commandName": "Project",
|
||||
"launchBrowser": true,
|
||||
"environmentVariables": {
|
||||
"ASPNETCORE_ENVIRONMENT": "Development"
|
||||
},
|
||||
"applicationUrl": "http://localhost:8201"
|
||||
}
|
||||
}
|
||||
}
|
||||
29
src/samples/Elsa.Samples.CorrelationHttp/Startup.cs
Normal file
29
src/samples/Elsa.Samples.CorrelationHttp/Startup.cs
Normal file
|
|
@ -0,0 +1,29 @@
|
|||
using System.Data;
|
||||
using Elsa.Samples.CorrelationHttp.Workflows;
|
||||
using Microsoft.AspNetCore.Builder;
|
||||
using Microsoft.Extensions.DependencyInjection;
|
||||
using YesSql.Provider.Sqlite;
|
||||
|
||||
namespace Elsa.Samples.CorrelationHttp
|
||||
{
|
||||
public class Startup
|
||||
{
|
||||
public void ConfigureServices(IServiceCollection services)
|
||||
{
|
||||
services
|
||||
.AddElsa(option => option.UsePersistence(db => db.UseSqLite("Data Source=elsa.db;Cache=Shared", IsolationLevel.ReadUncommitted)))
|
||||
.AddHttpActivities()
|
||||
.AddConsoleActivities()
|
||||
.AddWorkflow<RegistrationWorkflow>();
|
||||
}
|
||||
|
||||
public void Configure(IApplicationBuilder app)
|
||||
{
|
||||
// Add HTTP activities middleware.
|
||||
app.UseHttpActivities();
|
||||
|
||||
// Show welcome page if no routes matched any endpoints.
|
||||
app.UseWelcomePage();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
using System.Net;
|
||||
using Elsa.Activities.ControlFlow;
|
||||
using Elsa.Activities.Http;
|
||||
using Elsa.Activities.Http.Models;
|
||||
using Elsa.Builders;
|
||||
using Elsa.Samples.CorrelationHttp.Models;
|
||||
using Microsoft.AspNetCore.Http;
|
||||
|
||||
namespace Elsa.Samples.CorrelationHttp.Workflows
|
||||
{
|
||||
/// <summary>
|
||||
/// Triggered when POST HTTP requests are made to /register and continued when a subsequent GET HTTP request is made to /confirm to confirm the registration (see workflows.http).
|
||||
/// </summary>
|
||||
public class RegistrationWorkflow : IWorkflow
|
||||
{
|
||||
public void Build(IWorkflowBuilder workflow)
|
||||
{
|
||||
workflow
|
||||
// Configure a Receive HTTP Request trigger that executes on incoming HTTP POST requests.
|
||||
.ReceiveHttpRequest(activity => activity.WithPath("/register").WithMethod(HttpMethods.Post).WithTargetType<Registration>())
|
||||
|
||||
// Store the registration as a workflow variable for easier access.
|
||||
.SetVariable(context => (Registration)((HttpRequestModel)(context.Input))?.Body)
|
||||
|
||||
// Correlate the workflow by email address.
|
||||
.Correlate(context => context.GetVariable<Registration>().Email)
|
||||
|
||||
// Write an HTTP response with a hyperlink to continue the workflow (notice the presence of the "correlation" query string parameter).
|
||||
.WriteHttpResponse(activity => activity
|
||||
.WithStatusCode(HttpStatusCode.OK)
|
||||
.WithContentType("text/html")
|
||||
.WithContent(context =>
|
||||
{
|
||||
var registration = context.GetVariable<Registration>();
|
||||
return $"Welcome onboard, {registration.Name}! Please <a href=\"http://localhost:8201/confirm?correlation={registration.Email}\">confirm your registration</a>";
|
||||
}))
|
||||
|
||||
// Configure another Receive HTTP Request trigger that executes on incoming HTTP GET requests.
|
||||
// This will cause the workflow to become suspended and executed once the request comes in.
|
||||
.ReceiveHttpRequest(activity => activity.WithPath("/confirm"))
|
||||
|
||||
// Write an HTTP response with a thank-you note.
|
||||
// Notice that the correct workflow instance is resumed base on the incoming correlation ID.
|
||||
.WriteHttpResponse(activity => activity
|
||||
.WithStatusCode(HttpStatusCode.OK)
|
||||
.WithContentType("text/html")
|
||||
.WithContent(
|
||||
context =>
|
||||
{
|
||||
var registration = context.GetVariable<Registration>();
|
||||
return $"Thanks for confirming your registration, {registration.Name}!";
|
||||
}));
|
||||
}
|
||||
}
|
||||
}
|
||||
28
src/samples/Elsa.Samples.CorrelationHttp/workflows.http
Normal file
28
src/samples/Elsa.Samples.CorrelationHttp/workflows.http
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
# Register John.
|
||||
POST http://localhost:8201/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "John von Neumann",
|
||||
"email": "john@gmail.com"
|
||||
}
|
||||
|
||||
###
|
||||
# Register Julia.
|
||||
POST http://localhost:8201/register
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"name": "Julia Berger",
|
||||
"email": "julia@gmail.com"
|
||||
}
|
||||
|
||||
###
|
||||
# Confirm John's registration
|
||||
GET http://localhost:8201/confirm?correlation=john@gmail.com
|
||||
Content-Type: application/json
|
||||
|
||||
###
|
||||
# Confirm Julia's registration
|
||||
GET http://localhost:8201/confirm?correlation=julia@gmail.com
|
||||
Content-Type: application/json
|
||||
Loading…
Reference in a new issue