diff --git a/src/core/Elsa.Abstractions/Triggers/IGetsTriggersForActivityBlueprintAndWorkflow.cs b/src/core/Elsa.Abstractions/Triggers/IGetsTriggersForActivityBlueprintAndWorkflow.cs new file mode 100644 index 000000000..8a8e9bf25 --- /dev/null +++ b/src/core/Elsa.Abstractions/Triggers/IGetsTriggersForActivityBlueprintAndWorkflow.cs @@ -0,0 +1,27 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Elsa.ActivityProviders; +using Elsa.Services.Models; + +namespace Elsa.Triggers +{ + /// + /// An object which can get a collection of the for a specified activity blueprint and workflow. + /// + public interface IGetsTriggersForActivityBlueprintAndWorkflow + { + /// + /// Gets a collection of the workflow triggers for the specified activity blueprint. + /// + /// An activity blueprint + /// A workflow execution context + /// A dictionary of all of the activity types (by name) + /// An optional cancellation token + /// A task exposing a collection of workflow triggers for the activity and workflow. + Task> GetTriggersForActivityBlueprintAsync(IActivityBlueprint activityBlueprint, + WorkflowExecutionContext workflowExecutionContext, + IDictionary activityTypes, + CancellationToken cancellationToken = default); + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs index ee660fcb5..ca7ec6115 100644 --- a/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs +++ b/src/core/Elsa.Core/Extensions/ElsaServiceCollectionExtensions.cs @@ -146,6 +146,7 @@ namespace Microsoft.Extensions.DependencyInjection .AddScoped() .AddScoped() .AddScoped() + .AddTransient() .AddSingleton() .AddScoped() .AddBookmarkProvider() diff --git a/src/core/Elsa.Core/Triggers/TriggersForActivityBlueprintAndWorkflowProvider.cs b/src/core/Elsa.Core/Triggers/TriggersForActivityBlueprintAndWorkflowProvider.cs new file mode 100644 index 000000000..cab1f8bf9 --- /dev/null +++ b/src/core/Elsa.Core/Triggers/TriggersForActivityBlueprintAndWorkflowProvider.cs @@ -0,0 +1,89 @@ +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Elsa.ActivityProviders; +using Elsa.Bookmarks; +using Elsa.Services; +using Elsa.Services.Models; + +namespace Elsa.Triggers +{ + /// + /// Default implementation of . + /// + public class TriggersForActivityBlueprintAndWorkflowProvider : IGetsTriggersForActivityBlueprintAndWorkflow + { + readonly IBookmarkHasher bookmarkHasher; + readonly IEnumerable bookmarkProviders; + readonly ICreatesActivityExecutionContextForActivityBlueprint activityExecutionContextFactory; + + public TriggersForActivityBlueprintAndWorkflowProvider(IBookmarkHasher bookmarkHasher, + IEnumerable bookmarkProviders, + ICreatesActivityExecutionContextForActivityBlueprint activityExecutionContextFactory) + { + this.bookmarkHasher = bookmarkHasher ?? throw new System.ArgumentNullException(nameof(bookmarkHasher)); + this.bookmarkProviders = bookmarkProviders ?? throw new System.ArgumentNullException(nameof(bookmarkProviders)); + this.activityExecutionContextFactory = activityExecutionContextFactory ?? throw new System.ArgumentNullException(nameof(activityExecutionContextFactory)); + } + + /// + /// Gets a collection of the workflow triggers for the specified activity blueprint. + /// + /// An activity blueprint + /// A workflow execution context + /// A dictionary of all of the activity types (by name) + /// An optional cancellation token + /// A task exposing a collection of workflow triggers for the activity and workflow. + public async Task> GetTriggersForActivityBlueprintAsync(IActivityBlueprint activityBlueprint, + WorkflowExecutionContext workflowExecutionContext, + IDictionary activityTypes, + CancellationToken cancellationToken = default) + { + var bookmarkProviderContext = GetBookmarkProviderContext(activityBlueprint, workflowExecutionContext, cancellationToken, activityTypes); + var supportedBookmarkProviders = await GetSupportedBookmarkProvidersForContextAsync(bookmarkProviderContext) + .ToListAsync(cancellationToken); + + var tasksOfListsOfTriggers = supportedBookmarkProviders + .Select(async bookmarkProvider => await GetTriggersForBookmarkProvider(bookmarkProvider, + bookmarkProviderContext, + activityBlueprint, + workflowExecutionContext.WorkflowBlueprint, + cancellationToken)); + return (await Task.WhenAll(tasksOfListsOfTriggers)) + .SelectMany(x => x) + .ToList(); + } + + BookmarkProviderContext GetBookmarkProviderContext(IActivityBlueprint activity, + WorkflowExecutionContext workflowExecutionContext, + CancellationToken cancellationToken, + IDictionary activityTypes) + { + var activityExecutionContext = activityExecutionContextFactory.CreateActivityExecutionContext(activity, + workflowExecutionContext, + cancellationToken); + var activityType = activityTypes[activity.Type]; + return new BookmarkProviderContext(activityExecutionContext, activityType, BookmarkIndexingMode.WorkflowBlueprint); + } + + async IAsyncEnumerable GetSupportedBookmarkProvidersForContextAsync(BookmarkProviderContext context) + { + foreach (var provider in bookmarkProviders) + if (await provider.SupportsActivityAsync(context)) + yield return provider; + } + + async Task> GetTriggersForBookmarkProvider(IBookmarkProvider provider, + BookmarkProviderContext context, + IActivityBlueprint activityBlueprint, + IWorkflowBlueprint workflowBlueprint, + CancellationToken cancellationToken = default) + { + var bookmarks = (await provider.GetBookmarksAsync(context, cancellationToken)).ToList(); + return bookmarks + .Select(x => new WorkflowTrigger(workflowBlueprint, activityBlueprint.Id, activityBlueprint.Type, bookmarkHasher.Hash(x), x)) + .ToList(); + } + } +} \ No newline at end of file diff --git a/src/core/Elsa.Core/Triggers/TriggersForBlueprintsProvider.cs b/src/core/Elsa.Core/Triggers/TriggersForBlueprintsProvider.cs index 0a4891621..74e1aa5b4 100644 --- a/src/core/Elsa.Core/Triggers/TriggersForBlueprintsProvider.cs +++ b/src/core/Elsa.Core/Triggers/TriggersForBlueprintsProvider.cs @@ -4,7 +4,6 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; using Elsa.ActivityProviders; -using Elsa.Bookmarks; using Elsa.Services; using Elsa.Services.Models; @@ -17,22 +16,16 @@ namespace Elsa.Triggers public class TriggersForBlueprintsProvider : IGetsTriggersForWorkflowBlueprints { readonly IActivityTypeService activityTypeService; - readonly IBookmarkHasher bookmarkHasher; - readonly IEnumerable bookmarkProviders; readonly ICreatesWorkflowExecutionContextForWorkflowBlueprint workflowExecutionContextFactory; - readonly ICreatesActivityExecutionContextForActivityBlueprint activityExecutionContextFactory; + readonly IGetsTriggersForActivityBlueprintAndWorkflow triggerProvider; public TriggersForBlueprintsProvider(IActivityTypeService activityTypeService, - IBookmarkHasher bookmarkHasher, - IEnumerable bookmarkProviders, ICreatesWorkflowExecutionContextForWorkflowBlueprint workflowExecutionContextFactory, - ICreatesActivityExecutionContextForActivityBlueprint activityExecutionContextFactory) + IGetsTriggersForActivityBlueprintAndWorkflow triggerProvider) { this.activityTypeService = activityTypeService ?? throw new ArgumentNullException(nameof(activityTypeService)); - this.bookmarkHasher = bookmarkHasher ?? throw new ArgumentNullException(nameof(bookmarkHasher)); - this.bookmarkProviders = bookmarkProviders ?? throw new ArgumentNullException(nameof(bookmarkProviders)); this.workflowExecutionContextFactory = workflowExecutionContextFactory ?? throw new ArgumentNullException(nameof(workflowExecutionContextFactory)); - this.activityExecutionContextFactory = activityExecutionContextFactory ?? throw new ArgumentNullException(nameof(activityExecutionContextFactory)); + this.triggerProvider = triggerProvider ?? throw new ArgumentNullException(nameof(triggerProvider)); } /// @@ -52,56 +45,18 @@ namespace Elsa.Triggers var startingActivityBlueprints = workflowBlueprint.GetStartActivities(); var workflowExecutionContext = await workflowExecutionContextFactory.CreateWorkflowExecutionContextAsync(workflowBlueprint, cancellationToken); - - foreach (var activityBlueprint in startingActivityBlueprints) - { - var bookmarkProviderContext = GetBookmarkProviderContext(activityBlueprint, workflowExecutionContext, cancellationToken, activityTypes); - var supportedBookmarkProviders = await GetSupportedBookmarkProvidersForContextAsync(bookmarkProviderContext).ToListAsync(cancellationToken); - - foreach (var bookmarkProvider in supportedBookmarkProviders) - { - var triggers = await GetTriggersForBookmarkProvider(bookmarkProvider, - bookmarkProviderContext, - activityBlueprint, - workflowBlueprint, - cancellationToken); - allTriggers.AddRange(triggers); - } - } + var tasksOfCollectionsOfTriggers = startingActivityBlueprints + .Select(async activityBlueprint => await triggerProvider.GetTriggersForActivityBlueprintAsync(activityBlueprint, + workflowExecutionContext, + activityTypes, + cancellationToken)); + var triggers = (await Task.WhenAll(tasksOfCollectionsOfTriggers)) + .SelectMany(x => x) + .ToList(); + allTriggers.AddRange(triggers); } return allTriggers; } - - BookmarkProviderContext GetBookmarkProviderContext(IActivityBlueprint activity, - WorkflowExecutionContext workflowExecutionContext, - CancellationToken cancellationToken, - IDictionary activityTypes) - { - var activityExecutionContext = activityExecutionContextFactory.CreateActivityExecutionContext(activity, - workflowExecutionContext, - cancellationToken); - var activityType = activityTypes[activity.Type]; - return new BookmarkProviderContext(activityExecutionContext, activityType, BookmarkIndexingMode.WorkflowBlueprint); - } - - async IAsyncEnumerable GetSupportedBookmarkProvidersForContextAsync(BookmarkProviderContext context) - { - foreach (var provider in bookmarkProviders) - if (await provider.SupportsActivityAsync(context)) - yield return provider; - } - - async Task> GetTriggersForBookmarkProvider(IBookmarkProvider provider, - BookmarkProviderContext context, - IActivityBlueprint activityBlueprint, - IWorkflowBlueprint workflowBlueprint, - CancellationToken cancellationToken = default) - { - var bookmarks = (await provider.GetBookmarksAsync(context, cancellationToken)).ToList(); - return bookmarks - .Select(x => new WorkflowTrigger(workflowBlueprint, activityBlueprint.Id, activityBlueprint.Type, bookmarkHasher.Hash(x), x)) - .ToList(); - } } } \ No newline at end of file diff --git a/test/unit/Elsa.UnitTests/Triggers/TriggersForBlueprintsProviderTests.cs b/test/unit/Elsa.UnitTests/Triggers/TriggersForBlueprintsProviderTests.cs index 77a193e11..f6d2fccaf 100644 --- a/test/unit/Elsa.UnitTests/Triggers/TriggersForBlueprintsProviderTests.cs +++ b/test/unit/Elsa.UnitTests/Triggers/TriggersForBlueprintsProviderTests.cs @@ -17,7 +17,7 @@ namespace Elsa.UnitTests.Triggers { public class TriggersForBlueprintsProviderTests { - [Theory(DisplayName = "The GetTriggersAsync method should return all triggers for the compatible bookmarks of the start activities of the blueprints"), AutoMoqData] + [Theory(DisplayName = "The GetTriggersAsync method should return all triggers for the compatible bookmarks of the start activities of the blueprints. This is actually an integration test."), AutoMoqData] public async Task GetTriggersAsyncGetsAllTriggersForAllBlueprintsStartActivitiesAndCompatibleBookmarks(IActivityTypeService activityTypeService, IBookmarkHasher bookmarkHasher, IBookmarkProvider provider1, @@ -65,13 +65,13 @@ namespace Elsa.UnitTests.Triggers { var workflowExecutionContextFactory = new WorkflowExecutionContextForWorkflowBlueprintFactory(serviceProvider, workflowFactory); var activityExecutionContextFactory = new ActivityExecutionContextForActivityBlueprintFactory(serviceProvider); + var triggersForActivityProvider = new TriggersForActivityBlueprintAndWorkflowProvider(bookmarkHasher, + new[] { provider1, provider2, provider3 }, + activityExecutionContextFactory); - // Unfortunately the SUT can't be created by Autofixture because of the IEnumerable in the ctor var sut = new TriggersForBlueprintsProvider(activityTypeService, - bookmarkHasher, - new[] { provider1, provider2, provider3 }, workflowExecutionContextFactory, - activityExecutionContextFactory); + triggersForActivityProvider); Mock.Get(activityTypeService) .Setup(x => x.GetActivityTypesAsync(default))