WIP #738 - Refactor-out a further service

The triggers-for-activity-blueprint functionality
is quite complex just on its own, so I have moved this to
a new service.

Once again the integration test which is left behind proves
that it still does the same job.
This commit is contained in:
Craig Fowler 2021-03-18 20:29:06 +00:00
parent 654f3072d9
commit d9724e5267
5 changed files with 134 additions and 62 deletions

View file

@ -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
{
/// <summary>
/// An object which can get a collection of the <see cref="WorkflowTrigger"/> for a specified activity blueprint and workflow.
/// </summary>
public interface IGetsTriggersForActivityBlueprintAndWorkflow
{
/// <summary>
/// Gets a collection of the workflow triggers for the specified activity blueprint.
/// </summary>
/// <param name="activityBlueprint">An activity blueprint</param>
/// <param name="workflowExecutionContext">A workflow execution context</param>
/// <param name="activityTypes">A dictionary of all of the activity types (by name)</param>
/// <param name="cancellationToken">An optional cancellation token</param>
/// <returns>A task exposing a collection of workflow triggers for the activity and workflow.</returns>
Task<IEnumerable<WorkflowTrigger>> GetTriggersForActivityBlueprintAsync(IActivityBlueprint activityBlueprint,
WorkflowExecutionContext workflowExecutionContext,
IDictionary<string, ActivityType> activityTypes,
CancellationToken cancellationToken = default);
}
}

View file

@ -146,6 +146,7 @@ namespace Microsoft.Extensions.DependencyInjection
.AddScoped<IBookmarkFinder, BookmarkFinder>()
.AddScoped<ITriggerIndexer, TriggerIndexer>()
.AddScoped<IGetsTriggersForWorkflowBlueprints, TriggersForBlueprintsProvider>()
.AddTransient<IGetsTriggersForActivityBlueprintAndWorkflow, TriggersForActivityBlueprintAndWorkflowProvider>()
.AddSingleton<ITriggerStore, TriggerStore>()
.AddScoped<ITriggerFinder, TriggerFinder>()
.AddBookmarkProvider<SignalReceivedBookmarkProvider>()

View file

@ -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
{
/// <summary>
/// Default implementation of <see cref="IGetsTriggersForActivityBlueprintAndWorkflow"/>.
/// </summary>
public class TriggersForActivityBlueprintAndWorkflowProvider : IGetsTriggersForActivityBlueprintAndWorkflow
{
readonly IBookmarkHasher bookmarkHasher;
readonly IEnumerable<IBookmarkProvider> bookmarkProviders;
readonly ICreatesActivityExecutionContextForActivityBlueprint activityExecutionContextFactory;
public TriggersForActivityBlueprintAndWorkflowProvider(IBookmarkHasher bookmarkHasher,
IEnumerable<IBookmarkProvider> 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));
}
/// <summary>
/// Gets a collection of the workflow triggers for the specified activity blueprint.
/// </summary>
/// <param name="activityBlueprint">An activity blueprint</param>
/// <param name="workflowExecutionContext">A workflow execution context</param>
/// <param name="activityTypes">A dictionary of all of the activity types (by name)</param>
/// <param name="cancellationToken">An optional cancellation token</param>
/// <returns>A task exposing a collection of workflow triggers for the activity and workflow.</returns>
public async Task<IEnumerable<WorkflowTrigger>> GetTriggersForActivityBlueprintAsync(IActivityBlueprint activityBlueprint,
WorkflowExecutionContext workflowExecutionContext,
IDictionary<string, ActivityType> 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<string,ActivityType> activityTypes)
{
var activityExecutionContext = activityExecutionContextFactory.CreateActivityExecutionContext(activity,
workflowExecutionContext,
cancellationToken);
var activityType = activityTypes[activity.Type];
return new BookmarkProviderContext(activityExecutionContext, activityType, BookmarkIndexingMode.WorkflowBlueprint);
}
async IAsyncEnumerable<IBookmarkProvider> GetSupportedBookmarkProvidersForContextAsync(BookmarkProviderContext context)
{
foreach (var provider in bookmarkProviders)
if (await provider.SupportsActivityAsync(context))
yield return provider;
}
async Task<IList<WorkflowTrigger>> 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();
}
}
}

View file

@ -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<IBookmarkProvider> bookmarkProviders;
readonly ICreatesWorkflowExecutionContextForWorkflowBlueprint workflowExecutionContextFactory;
readonly ICreatesActivityExecutionContextForActivityBlueprint activityExecutionContextFactory;
readonly IGetsTriggersForActivityBlueprintAndWorkflow triggerProvider;
public TriggersForBlueprintsProvider(IActivityTypeService activityTypeService,
IBookmarkHasher bookmarkHasher,
IEnumerable<IBookmarkProvider> 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));
}
/// <summary>
@ -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<string,ActivityType> activityTypes)
{
var activityExecutionContext = activityExecutionContextFactory.CreateActivityExecutionContext(activity,
workflowExecutionContext,
cancellationToken);
var activityType = activityTypes[activity.Type];
return new BookmarkProviderContext(activityExecutionContext, activityType, BookmarkIndexingMode.WorkflowBlueprint);
}
async IAsyncEnumerable<IBookmarkProvider> GetSupportedBookmarkProvidersForContextAsync(BookmarkProviderContext context)
{
foreach (var provider in bookmarkProviders)
if (await provider.SupportsActivityAsync(context))
yield return provider;
}
async Task<IList<WorkflowTrigger>> 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();
}
}
}

View file

@ -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<IBookmarkProvider> in the ctor
var sut = new TriggersForBlueprintsProvider(activityTypeService,
bookmarkHasher,
new[] { provider1, provider2, provider3 },
workflowExecutionContextFactory,
activityExecutionContextFactory);
triggersForActivityProvider);
Mock.Get(activityTypeService)
.Setup(x => x.GetActivityTypesAsync(default))