diff --git a/src/activities/Elsa.Activities.Http/Middleware/HttpRequestMiddleware.cs b/src/activities/Elsa.Activities.Http/Middleware/HttpRequestMiddleware.cs index e0a337b56..43108f45a 100644 --- a/src/activities/Elsa.Activities.Http/Middleware/HttpRequestMiddleware.cs +++ b/src/activities/Elsa.Activities.Http/Middleware/HttpRequestMiddleware.cs @@ -1,5 +1,4 @@ using System.Linq; -using System.Threading; using System.Threading.Tasks; using Elsa.Activities.Http.Triggers; using Elsa.Services; @@ -22,10 +21,10 @@ namespace Elsa.Activities.Http.Middleware var path = httpContext.Request.Path; var method = httpContext.Request.Method; var cancellationToken = httpContext.RequestAborted; - + var results = await workflowSelector.SelectWorkflowsAsync( - x => x.Path == path && x.Method == null || x.Method == method, - cancellationToken).ToListAsync(cancellationToken); + x => x.Path == path && x.Method == null || x.Method == method, + cancellationToken); var result = results.FirstOrDefault(); diff --git a/src/activities/Elsa.Activities.Http/Triggers/ReceiveHttpRequestTrigger.cs b/src/activities/Elsa.Activities.Http/Triggers/ReceiveHttpRequestTrigger.cs index 0a04f6fd9..ca3f268b7 100644 --- a/src/activities/Elsa.Activities.Http/Triggers/ReceiveHttpRequestTrigger.cs +++ b/src/activities/Elsa.Activities.Http/Triggers/ReceiveHttpRequestTrigger.cs @@ -1,14 +1,7 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Runtime.CompilerServices; -using System.Threading; +using System.Threading; using System.Threading.Tasks; -using Elsa.Services; -using Elsa.Services.Models; using Elsa.Triggers; using Microsoft.AspNetCore.Http; -using Microsoft.Extensions.DependencyInjection; namespace Elsa.Activities.Http.Triggers { @@ -16,42 +9,15 @@ namespace Elsa.Activities.Http.Triggers { public PathString Path { get; set; } public string? Method { get; set; } - public string ActivityId { get; set; } } - public class ReceiveHttpRequestTriggerProvider : TriggerProvider + public class ReceiveHttpRequestTriggerProvider : TriggerProvider { - private readonly IServiceProvider _serviceProvider; - private readonly IWorkflowFactory _workflowFactory; - - public ReceiveHttpRequestTriggerProvider(IServiceProvider serviceProvider, IWorkflowFactory workflowFactory) - { - _serviceProvider = serviceProvider; - _workflowFactory = workflowFactory; - } - - public override async IAsyncEnumerable GetTriggersAsync(IWorkflowBlueprint workflowBlueprint, [EnumeratorCancellation] CancellationToken cancellationToken = default) - { - var httpRequestActivities = workflowBlueprint - .GetStartActivities() - .Where(x => x.Type == nameof(ReceiveHttpRequest)) - .ToList(); - - using var scope = _serviceProvider.CreateScope(); - var workflowInstance = await _workflowFactory.InstantiateAsync(workflowBlueprint, cancellationToken: cancellationToken); - var workflowExecutionContext = new WorkflowExecutionContext(scope.ServiceProvider, workflowBlueprint, workflowInstance); - - foreach (var activity in httpRequestActivities) + public override async ValueTask GetTriggerAsync(TriggerProviderContext context, CancellationToken cancellationToken) => + new ReceiveHttpRequestTrigger { - var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, scope.ServiceProvider, activity); - - yield return new ReceiveHttpRequestTrigger - { - ActivityId = activity.Id, - Path = await workflowBlueprint.GetActivityPropertyValue(activity.Id, x => x.Path, activityExecutionContext, cancellationToken), - Method = await workflowBlueprint.GetActivityPropertyValue(activity.Id, x => x.Method, activityExecutionContext, cancellationToken) - }; - } - } + Path = await context.Activity.GetPropertyValueAsync(x => x.Path, cancellationToken), + Method = await context.Activity.GetPropertyValueAsync(x => x.Method, cancellationToken) + }; } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Extensions/WorkflowExtensions.cs b/src/core/Elsa.Abstractions/Extensions/WorkflowExtensions.cs index fb920b8b7..339a652ca 100644 --- a/src/core/Elsa.Abstractions/Extensions/WorkflowExtensions.cs +++ b/src/core/Elsa.Abstractions/Extensions/WorkflowExtensions.cs @@ -1,6 +1,8 @@ +using System; using System.Collections.Generic; using System.Linq; using Elsa.Models; +using Elsa.Services; using Elsa.Services.Models; namespace Elsa @@ -47,6 +49,10 @@ namespace Elsa return query; } + public static IEnumerable GetStartActivities(this IWorkflowBlueprint workflowBlueprint, string activityType) => workflowBlueprint.GetStartActivities().Where(x => x.Type == activityType); + public static IEnumerable GetStartActivities(this IWorkflowBlueprint workflowBlueprint, Type activityType) => workflowBlueprint.GetStartActivities(activityType.Name); + public static IEnumerable GetStartActivities(this IWorkflowBlueprint workflowBlueprint) where T : IActivity => workflowBlueprint.GetStartActivities(typeof(T)); + public static IActivityBlueprint? GetActivity(this IWorkflowBlueprint workflowBlueprint, string id) => workflowBlueprint.Activities.FirstOrDefault(x => x.Id == id); diff --git a/src/core/Elsa.Abstractions/Triggers/ActivityBlueprintWrapper.cs b/src/core/Elsa.Abstractions/Triggers/ActivityBlueprintWrapper.cs new file mode 100644 index 000000000..70e1b13ca --- /dev/null +++ b/src/core/Elsa.Abstractions/Triggers/ActivityBlueprintWrapper.cs @@ -0,0 +1,26 @@ +using System; +using System.Linq.Expressions; +using System.Threading; +using System.Threading.Tasks; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Triggers +{ + public class ActivityBlueprintWrapper where TActivity : IActivity + { + private readonly ActivityExecutionContext _activityExecutionContext; + + public ActivityBlueprintWrapper(ActivityExecutionContext activityExecutionContext) + { + _activityExecutionContext = activityExecutionContext; + } + + public async ValueTask GetPropertyValueAsync(Expression> propertyExpression, CancellationToken cancellationToken = default) + { + var workflowBlueprint = _activityExecutionContext.WorkflowExecutionContext.WorkflowBlueprint; + var activityId = _activityExecutionContext.ActivityBlueprint.Id; + return await workflowBlueprint.GetActivityPropertyValue(activityId, propertyExpression, _activityExecutionContext, cancellationToken); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Triggers/IDescribeTriggersContext.cs b/src/core/Elsa.Abstractions/Triggers/IDescribeTriggersContext.cs new file mode 100644 index 000000000..b7c01b313 --- /dev/null +++ b/src/core/Elsa.Abstractions/Triggers/IDescribeTriggersContext.cs @@ -0,0 +1,6 @@ +namespace Elsa.Triggers +{ + public interface IDescribeTriggersContext + { + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Triggers/ITrigger.cs b/src/core/Elsa.Abstractions/Triggers/ITrigger.cs index eb93fbb47..1b52d8564 100644 --- a/src/core/Elsa.Abstractions/Triggers/ITrigger.cs +++ b/src/core/Elsa.Abstractions/Triggers/ITrigger.cs @@ -2,6 +2,5 @@ { public interface ITrigger { - string ActivityId { get; set; } } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Triggers/ITriggerProvider.cs b/src/core/Elsa.Abstractions/Triggers/ITriggerProvider.cs index e3297d3f7..1c0ad0bac 100644 --- a/src/core/Elsa.Abstractions/Triggers/ITriggerProvider.cs +++ b/src/core/Elsa.Abstractions/Triggers/ITriggerProvider.cs @@ -1,13 +1,13 @@ using System; -using System.Collections.Generic; using System.Threading; -using Elsa.Services.Models; +using System.Threading.Tasks; namespace Elsa.Triggers { public interface ITriggerProvider { Type ForType(); - IAsyncEnumerable GetTriggersAsync(IWorkflowBlueprint workflowBlueprint, CancellationToken cancellationToken = default); + Type ForActivityType(); + ValueTask GetTriggerAsync(TriggerProviderContext context, CancellationToken cancellationToken = default); } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Triggers/IWorkflowSelector.cs b/src/core/Elsa.Abstractions/Triggers/IWorkflowSelector.cs index ca6cf32af..d872219c3 100644 --- a/src/core/Elsa.Abstractions/Triggers/IWorkflowSelector.cs +++ b/src/core/Elsa.Abstractions/Triggers/IWorkflowSelector.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; namespace Elsa.Triggers { public interface IWorkflowSelector { - IAsyncEnumerable SelectWorkflowsAsync( + Task> SelectWorkflowsAsync( Type triggerType, Func evaluate, CancellationToken cancellationToken = default); diff --git a/src/core/Elsa.Abstractions/Triggers/TriggerDescriptor.cs b/src/core/Elsa.Abstractions/Triggers/TriggerDescriptor.cs new file mode 100644 index 000000000..f2fc01f52 --- /dev/null +++ b/src/core/Elsa.Abstractions/Triggers/TriggerDescriptor.cs @@ -0,0 +1,12 @@ +using Elsa.Services.Models; + +namespace Elsa.Triggers +{ + public class TriggerDescriptor + { + public IWorkflowBlueprint WorkflowBlueprint { get; set; } = default!; + public string ActivityId { get; set; } = default!; + public string ActivityType { get; set; } = default!; + public ITrigger Trigger { get; set; } = default!; + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Triggers/TriggerProvider.cs b/src/core/Elsa.Abstractions/Triggers/TriggerProvider.cs index 7497d5f92..4f2b5135e 100644 --- a/src/core/Elsa.Abstractions/Triggers/TriggerProvider.cs +++ b/src/core/Elsa.Abstractions/Triggers/TriggerProvider.cs @@ -1,13 +1,17 @@ using System; -using System.Collections.Generic; using System.Threading; -using Elsa.Services.Models; +using System.Threading.Tasks; +using Elsa.Services; namespace Elsa.Triggers { - public abstract class TriggerProvider : ITriggerProvider where T : ITrigger + public abstract class TriggerProvider : ITriggerProvider where T : ITrigger where TActivity : IActivity { public Type ForType() => typeof(T); - public abstract IAsyncEnumerable GetTriggersAsync(IWorkflowBlueprint workflowBlueprint, CancellationToken cancellationToken = default); + public Type ForActivityType() => typeof(TActivity); + public abstract ValueTask GetTriggerAsync(TriggerProviderContext context, CancellationToken cancellationToken); + + ValueTask ITriggerProvider.GetTriggerAsync(TriggerProviderContext context, CancellationToken cancellationToken) => + GetTriggerAsync(new TriggerProviderContext(context.ActivityExecutionContext), cancellationToken); } } \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Triggers/TriggerProviderContext.cs b/src/core/Elsa.Abstractions/Triggers/TriggerProviderContext.cs new file mode 100644 index 000000000..b5332993f --- /dev/null +++ b/src/core/Elsa.Abstractions/Triggers/TriggerProviderContext.cs @@ -0,0 +1,25 @@ +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Triggers +{ + public class TriggerProviderContext + { + public TriggerProviderContext(ActivityExecutionContext activityExecutionContext) + { + ActivityExecutionContext = activityExecutionContext; + } + + public ActivityExecutionContext ActivityExecutionContext { get; } + public ActivityBlueprintWrapper GetActivity() where TActivity : IActivity => new ActivityBlueprintWrapper(ActivityExecutionContext); + } + + public class TriggerProviderContext : TriggerProviderContext where T:IActivity + { + public TriggerProviderContext(ActivityExecutionContext activityExecutionContext) : base(activityExecutionContext) + { + } + + public ActivityBlueprintWrapper Activity => GetActivity(); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Abstractions/Triggers/WorkflowSelectorExtensions.cs b/src/core/Elsa.Abstractions/Triggers/WorkflowSelectorExtensions.cs index b761428c3..0695ae9f1 100644 --- a/src/core/Elsa.Abstractions/Triggers/WorkflowSelectorExtensions.cs +++ b/src/core/Elsa.Abstractions/Triggers/WorkflowSelectorExtensions.cs @@ -1,12 +1,13 @@ using System; using System.Collections.Generic; using System.Threading; +using System.Threading.Tasks; namespace Elsa.Triggers { public static class WorkflowSelectorExtensions { - public static IAsyncEnumerable SelectWorkflowsAsync( + public static Task> SelectWorkflowsAsync( this IWorkflowSelector workflowSelector, Func evaluate, CancellationToken cancellationToken = default) where T : ITrigger => diff --git a/src/core/Elsa.Core/Triggers/WorkflowSelector.cs b/src/core/Elsa.Core/Triggers/WorkflowSelector.cs index 1a624d890..776bf8756 100644 --- a/src/core/Elsa.Core/Triggers/WorkflowSelector.cs +++ b/src/core/Elsa.Core/Triggers/WorkflowSelector.cs @@ -1,10 +1,11 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Runtime.CompilerServices; using System.Threading; using System.Threading.Tasks; using Elsa.Services; +using Elsa.Services.Models; +using Microsoft.Extensions.DependencyInjection; using Open.Linq.AsyncExtensions; namespace Elsa.Triggers @@ -12,31 +13,71 @@ namespace Elsa.Triggers public class WorkflowSelector : IWorkflowSelector { private readonly IWorkflowRegistry _workflowRegistry; + private readonly IWorkflowFactory _workflowFactory; private readonly IEnumerable _triggerProviders; + private readonly IServiceProvider _serviceProvider; + private ICollection? _descriptors; - public WorkflowSelector(IWorkflowRegistry workflowRegistry, IEnumerable triggerProviders) + public WorkflowSelector(IWorkflowRegistry workflowRegistry, IWorkflowFactory workflowFactory, IEnumerable triggerProviders, IServiceProvider serviceProvider) { _workflowRegistry = workflowRegistry; + _workflowFactory = workflowFactory; _triggerProviders = triggerProviders; + _serviceProvider = serviceProvider; } - public async IAsyncEnumerable SelectWorkflowsAsync( + public async Task> SelectWorkflowsAsync( Type triggerType, Func evaluate, - [EnumeratorCancellation] CancellationToken cancellationToken = default) + CancellationToken cancellationToken = default) { - var providers = _triggerProviders.Where(x => x.ForType() == triggerType).ToList(); - var workflowBlueprints = await _workflowRegistry.GetWorkflowsAsync(cancellationToken).ToList(); + _descriptors ??= await BuildDescriptorsAsync(cancellationToken).ToList(); + + var query = + from descriptor in _descriptors + let trigger = descriptor.Trigger + where trigger.GetType() == triggerType && evaluate(trigger) + select new WorkflowSelectorResult(descriptor.WorkflowBlueprint, descriptor.ActivityId); + + return query; + } + + private async Task> BuildDescriptorsAsync(CancellationToken cancellationToken) + { + var providers = _triggerProviders.ToList(); + var workflowBlueprints = await _workflowRegistry.GetWorkflowsAsync(cancellationToken).ToList(); + var descriptors = new List(); + + using var scope = _serviceProvider.CreateScope(); - foreach (var provider in providers) foreach (var workflowBlueprint in workflowBlueprints) { - var triggers = provider.GetTriggersAsync(workflowBlueprint, cancellationToken); + var workflowInstance = await _workflowFactory.InstantiateAsync(workflowBlueprint, cancellationToken: cancellationToken); + var workflowExecutionContext = new WorkflowExecutionContext(scope.ServiceProvider, workflowBlueprint, workflowInstance); + var startActivities = workflowBlueprint.GetStartActivities(); - await foreach (var trigger in triggers.WithCancellation(cancellationToken)) - if (evaluate(trigger)) - yield return new WorkflowSelectorResult(workflowBlueprint, trigger.ActivityId); + foreach (var startActivity in startActivities) + { + var activityExecutionContext = new ActivityExecutionContext(workflowExecutionContext, scope.ServiceProvider, startActivity); + var providerContext = new TriggerProviderContext(activityExecutionContext); + + foreach (var provider in providers) + { + var trigger = await provider.GetTriggerAsync(providerContext, cancellationToken); + var descriptor = new TriggerDescriptor + { + ActivityId = startActivity.Id, + ActivityType = startActivity.Type, + WorkflowBlueprint = workflowBlueprint, + Trigger = trigger, + }; + + descriptors.Add(descriptor); + } + } } + + return descriptors; } } } \ No newline at end of file diff --git a/src/samples/Elsa.Samples.HelloWorldHttp/elsa.db b/src/samples/Elsa.Samples.HelloWorldHttp/elsa.db index 03cf2c6e9..60984edfc 100644 Binary files a/src/samples/Elsa.Samples.HelloWorldHttp/elsa.db and b/src/samples/Elsa.Samples.HelloWorldHttp/elsa.db differ