elsa-core/src/common/Elsa.Testing.Shared/ServiceProviderExtensions.cs
Sipke Schoorstra 683f0ee6d8
Add Bulk Dispatch Workflow activity (#4683)
* Initial version of BulkDispatchWorkflow

* Update comments

* Refactor options models for running workflows and add Properties

* Fix resumption

* Add ability to provide arguments when evaluating expressions

* Update args syntax for JS

* Fix ProtoActor Runtime Properties mapping

* Use default constructor

* Provide completed workflow instance ID as input to BulkDispatchWorkflows

* Implement ChildFaulted port

* Switch to Publish to make Memory service bus work

* Update input evaluation and activity output retrieval

Refined the input evaluation process to only consider inputs with AutoEvaluate set to true. Added a comment to clarify the purpose of the GetActivitiesWithOutputs method. Removed the unnecessary check for AutoEvaluate from the EvaluateInputPropertyAsync method.
2023-12-06 22:32:08 +01:00

126 lines
5.6 KiB
C#

using Elsa.Common.Models;
using Elsa.Workflows.Core;
using Elsa.Workflows.Core.Contracts;
using Elsa.Workflows.Core.Models;
using Elsa.Workflows.Core.Options;
using Elsa.Workflows.Core.State;
using Elsa.Workflows.Management.Contracts;
using Elsa.Workflows.Management.Entities;
using Elsa.Workflows.Management.Models;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Options;
using JetBrains.Annotations;
using Microsoft.Extensions.DependencyInjection;
namespace Elsa.Testing.Shared;
/// <summary>
/// Provides extension methods for <see cref="IServiceProvider"/>.
/// </summary>
[PublicAPI]
public static class ServiceProviderExtensions
{
/// <summary>
/// Updates the registries.
/// </summary>
/// <param name="services">The services.</param>
public static Task PopulateRegistriesAsync(this IServiceProvider services)
{
var registriesPopulator = services.GetRequiredService<IRegistriesPopulator>();
return registriesPopulator.PopulateAsync();
}
/// <summary>
/// Imports a workflow definition from a file.
/// </summary>
/// <param name="services">The services.</param>
/// <param name="fileName">The file name.</param>
/// <returns>The workflow definition.</returns>
public static async Task<WorkflowDefinition> ImportWorkflowDefinitionAsync(this IServiceProvider services, string fileName)
{
var json = await File.ReadAllTextAsync(fileName);
var serializer = services.GetRequiredService<IActivitySerializer>();
var model = serializer.Deserialize<WorkflowDefinitionModel>(json);
var workflowDefinitionRequest = new SaveWorkflowDefinitionRequest
{
Model = model,
Publish = true
};
var workflowDefinitionImporter = services.GetRequiredService<IWorkflowDefinitionImporter>();
var result = await workflowDefinitionImporter.ImportAsync(workflowDefinitionRequest);
return result.WorkflowDefinition;
}
/// <summary>
/// Runs a workflow until its end, automatically resuming any bookmark it encounters.
/// </summary>
/// <param name="services">The services.</param>
/// <param name="workflowDefinitionId">The ID of the workflow definition.</param>
/// <param name="input">An optional dictionary of input values.</param>
/// <returns>The workflow state.</returns>
public static async Task<WorkflowState> RunWorkflowUntilEndAsync(this IServiceProvider services, string workflowDefinitionId, IDictionary<string, object>? input = default)
{
var startWorkflowOptions = new StartWorkflowRuntimeOptions
{
Input = input,
VersionOptions = VersionOptions.Published
};
var workflowRuntime = services.GetRequiredService<IWorkflowRuntime>();
var result = await workflowRuntime.StartWorkflowAsync(workflowDefinitionId, startWorkflowOptions);
var bookmarks = new Stack<Bookmark>(result.Bookmarks);
// Continue resuming the workflow for as long as there are bookmarks to resume and the workflow is not Finished.
while (result.Status != WorkflowStatus.Finished && bookmarks.TryPop(out var bookmark))
{
var resumeOptions = new ResumeWorkflowRuntimeOptions { BookmarkId = bookmark.Id };
var resumeResult = await workflowRuntime.ResumeWorkflowAsync(result.WorkflowInstanceId, resumeOptions);
foreach (var newBookmark in resumeResult!.Bookmarks)
bookmarks.Push(newBookmark);
}
// Return the workflow state.
return (await workflowRuntime.ExportWorkflowStateAsync(result.WorkflowInstanceId))!;
}
/// <summary>
/// Runs a workflow until its end, automatically resuming any bookmark it encounters.
/// </summary>
public static async Task<WorkflowState> RunWorkflowUntilEndAsync<TWorkflow>(this IServiceProvider services, IDictionary<string, object>? input = default) where TWorkflow : IWorkflow
{
var workflowDefinitionId = typeof(TWorkflow).Name;
return await services.RunWorkflowUntilEndAsync(workflowDefinitionId, input);
}
/// <summary>
/// Runs the specified activity.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="activity">The activity to run.</param>
/// <param name="cancellationToken">An optional cancellation token.</param>
/// <returns>The result of running the activity.</returns>
public static async Task<RunWorkflowResult> RunActivityAsync(this IServiceProvider services, IActivity activity, CancellationToken cancellationToken = default)
{
await services.PopulateRegistriesAsync();
var workflowRunner = services.GetRequiredService<IWorkflowRunner>();
var result = await workflowRunner.RunAsync(activity, cancellationToken: cancellationToken);
return result;
}
/// <summary>
/// Runs the specified activity.
/// </summary>
/// <param name="services">The service provider.</param>
/// <param name="activity">The activity to run.</param>
/// <param name="options">An set of options.</param>
/// <param name="cancellationToken">An optional cancellation token.</param>
/// <returns>The result of running the activity.</returns>
public static async Task<RunWorkflowResult> RunActivityAsync(this IServiceProvider services, IActivity activity, RunWorkflowOptions options, CancellationToken cancellationToken = default)
{
var workflowRunner = services.GetRequiredService<IWorkflowRunner>();
var result = await workflowRunner.RunAsync(activity, options, cancellationToken);
return result;
}
}