elsa-core/src/modules/Elsa.Workflows.Runtime/Services/DefaultBackgroundActivityInvoker.cs

141 lines
6.5 KiB
C#
Raw Normal View History

using Elsa.Common.Models;
Add a more generic UIHandler to customize how inputAttributes can be handle by UI (#4688) * add a more generic UIHandler to customize how inputAttributes can be handle by the ui * Add IPropertyUIHandlerResolver and update PropertyUIHandlerResolver Introduced a new interface, IPropertyUIHandlerResolver, to resolve UI options for a property. Refactored PropertyUIHandlerResolver to implement this interface and removed the unnecessary partial class structure. Also, cleaned up some unnecessary usings in various files for better code organization. * Refactor variable name and description in InputDescriptor The 'uISpecifications' variable in the InputDescriptor model is renamed to 'uiSpecifications' for better readability. Additionally, the associated comment was revised to explain that the dictionary is used by the UI. * "Refactor codebase for improved organization and cleaner architecture" The codebase has been significantly refactored, moving several classes to more appropriate namespaces for improved organization and cleaner architecture. This includes shifting UI hint handlers, activities, and memory-related components, amongst others. The changes should improve code readability and maintainability, but as this is a broad refactoring effort, thorough regression testing is advised. * Add CheckList UIHint with associated handler and provider This update introduces a new UIHint called CheckList to the Elsa.Workflows.Core. This includes the necessary handler and provider classes. The handler is registered in the WorkflowsFeature.cs, and the CheckList UIHint key has been added to the InputUIHints.cs. Various associated files have been created in both the Elsa.Api.Client and Elsa.Workflows.Core project to support this new UIHint. --------- Co-authored-by: Jérémie DEVILLARD <jdevillard@users.noreply.github.com> Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
2023-12-26 17:56:29 +00:00
using Elsa.Workflows.Contracts;
using Elsa.Workflows.Helpers;
using Elsa.Workflows.Management.Contracts;
Add a more generic UIHandler to customize how inputAttributes can be handle by UI (#4688) * add a more generic UIHandler to customize how inputAttributes can be handle by the ui * Add IPropertyUIHandlerResolver and update PropertyUIHandlerResolver Introduced a new interface, IPropertyUIHandlerResolver, to resolve UI options for a property. Refactored PropertyUIHandlerResolver to implement this interface and removed the unnecessary partial class structure. Also, cleaned up some unnecessary usings in various files for better code organization. * Refactor variable name and description in InputDescriptor The 'uISpecifications' variable in the InputDescriptor model is renamed to 'uiSpecifications' for better readability. Additionally, the associated comment was revised to explain that the dictionary is used by the UI. * "Refactor codebase for improved organization and cleaner architecture" The codebase has been significantly refactored, moving several classes to more appropriate namespaces for improved organization and cleaner architecture. This includes shifting UI hint handlers, activities, and memory-related components, amongst others. The changes should improve code readability and maintainability, but as this is a broad refactoring effort, thorough regression testing is advised. * Add CheckList UIHint with associated handler and provider This update introduces a new UIHint called CheckList to the Elsa.Workflows.Core. This includes the necessary handler and provider classes. The handler is registered in the WorkflowsFeature.cs, and the CheckList UIHint key has been added to the InputUIHints.cs. Various associated files have been created in both the Elsa.Api.Client and Elsa.Workflows.Core project to support this new UIHint. --------- Co-authored-by: Jérémie DEVILLARD <jdevillard@users.noreply.github.com> Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
2023-12-26 17:56:29 +00:00
using Elsa.Workflows.Memory;
using Elsa.Workflows.Models;
using Elsa.Workflows.Runtime.Contracts;
using Elsa.Workflows.Runtime.Middleware.Activities;
using Elsa.Workflows.Runtime.Models;
2023-07-19 22:05:43 +00:00
using Elsa.Workflows.Runtime.Requests;
Add a more generic UIHandler to customize how inputAttributes can be handle by UI (#4688) * add a more generic UIHandler to customize how inputAttributes can be handle by the ui * Add IPropertyUIHandlerResolver and update PropertyUIHandlerResolver Introduced a new interface, IPropertyUIHandlerResolver, to resolve UI options for a property. Refactored PropertyUIHandlerResolver to implement this interface and removed the unnecessary partial class structure. Also, cleaned up some unnecessary usings in various files for better code organization. * Refactor variable name and description in InputDescriptor The 'uISpecifications' variable in the InputDescriptor model is renamed to 'uiSpecifications' for better readability. Additionally, the associated comment was revised to explain that the dictionary is used by the UI. * "Refactor codebase for improved organization and cleaner architecture" The codebase has been significantly refactored, moving several classes to more appropriate namespaces for improved organization and cleaner architecture. This includes shifting UI hint handlers, activities, and memory-related components, amongst others. The changes should improve code readability and maintainability, but as this is a broad refactoring effort, thorough regression testing is advised. * Add CheckList UIHint with associated handler and provider This update introduces a new UIHint called CheckList to the Elsa.Workflows.Core. This includes the necessary handler and provider classes. The handler is registered in the WorkflowsFeature.cs, and the CheckList UIHint key has been added to the InputUIHints.cs. Various associated files have been created in both the Elsa.Api.Client and Elsa.Workflows.Core project to support this new UIHint. --------- Co-authored-by: Jérémie DEVILLARD <jdevillard@users.noreply.github.com> Co-authored-by: Sipke Schoorstra <sipkeschoorstra@outlook.com>
2023-12-26 17:56:29 +00:00
using Elsa.Workflows.Services;
2023-04-13 18:35:55 +00:00
using Microsoft.Extensions.Logging;
namespace Elsa.Workflows.Runtime.Services;
/// <summary>
/// An activity invoker that invokes activities detached from the workflow. This is useful for invoking activities from a background worker.
/// </summary>
public class DefaultBackgroundActivityInvoker : IBackgroundActivityInvoker
{
private readonly IWorkflowRuntime _workflowRuntime;
private readonly IWorkflowDispatcher _workflowDispatcher;
private readonly IWorkflowDefinitionService _workflowDefinitionService;
private readonly IVariablePersistenceManager _variablePersistenceManager;
private readonly IActivityInvoker _activityInvoker;
private readonly IBookmarksPersister _bookmarksPersister;
private readonly IWorkflowStateExtractor _workflowStateExtractor;
private readonly IServiceProvider _serviceProvider;
2023-04-13 18:35:55 +00:00
private readonly ILogger _logger;
/// <summary>
/// Initializes a new instance of the <see cref="DefaultBackgroundActivityInvoker"/> class.
/// </summary>
public DefaultBackgroundActivityInvoker(
IWorkflowRuntime workflowRuntime,
IWorkflowDispatcher workflowDispatcher,
IWorkflowDefinitionService workflowDefinitionService,
IVariablePersistenceManager variablePersistenceManager,
IActivityInvoker activityInvoker,
IBookmarksPersister bookmarksPersister,
IWorkflowStateExtractor workflowStateExtractor,
2023-04-13 18:35:55 +00:00
IServiceProvider serviceProvider,
ILogger<DefaultBackgroundActivityInvoker> logger)
{
_workflowRuntime = workflowRuntime;
_workflowDispatcher = workflowDispatcher;
_workflowDefinitionService = workflowDefinitionService;
_variablePersistenceManager = variablePersistenceManager;
_activityInvoker = activityInvoker;
_bookmarksPersister = bookmarksPersister;
_workflowStateExtractor = workflowStateExtractor;
_serviceProvider = serviceProvider;
2023-04-13 18:35:55 +00:00
_logger = logger;
}
/// <inheritdoc />
public async Task ExecuteAsync(ScheduledBackgroundActivity scheduledBackgroundActivity, CancellationToken cancellationToken = default)
{
var workflowInstanceId = scheduledBackgroundActivity.WorkflowInstanceId;
2024-01-03 08:30:24 +00:00
var workflowState = await _workflowRuntime.ExportWorkflowStateAsync(workflowInstanceId, cancellationToken);
if (workflowState == null)
throw new Exception("Workflow state not found");
var workflowDefinition = await _workflowDefinitionService.FindAsync(workflowState.DefinitionId, VersionOptions.SpecificVersion(workflowState.DefinitionVersion), cancellationToken);
if (workflowDefinition == null)
throw new Exception("Workflow definition not found");
var workflow = await _workflowDefinitionService.MaterializeWorkflowAsync(workflowDefinition, cancellationToken);
var workflowExecutionContext = await WorkflowExecutionContext.CreateAsync(_serviceProvider, workflow, workflowState, cancellationTokens: cancellationToken);
2023-04-13 18:05:14 +00:00
var activityNodeId = scheduledBackgroundActivity.ActivityNodeId;
var activityExecutionContext = workflowExecutionContext.ActivityExecutionContexts.First(x => x.NodeId == activityNodeId);
// Load persistent variables for the activity to use.
await _variablePersistenceManager.LoadVariablesAsync(workflowExecutionContext);
// Mark the activity as being invoked from a background worker.
activityExecutionContext.SetIsBackgroundExecution();
// Invoke the activity.
await _activityInvoker.InvokeAsync(activityExecutionContext);
// Capture any activity output produced by the activity (but only if the associated memory block is stored in the workflow itself).
var outputDescriptors = activityExecutionContext.ActivityDescriptor.Outputs;
var outputValues = new Dictionary<string, object>();
var outcomes = activityExecutionContext.GetBackgroundOutcomes().ToList();
foreach (var outputDescriptor in outputDescriptors)
{
var output = (Output?)outputDescriptor.ValueGetter(activityExecutionContext.Activity);
if (output == null)
continue;
var memoryBlockReference = output.MemoryBlockReference();
if (!activityExecutionContext.ExpressionExecutionContext.TryGetBlock(memoryBlockReference, out var memoryBlock))
continue;
var variableMetadata = memoryBlock.Metadata as VariableBlockMetadata;
var driver = variableMetadata?.StorageDriverType;
// We only capture output written to the workflow itself. Other drivers like blob storage, etc. will be ignored since the foreground context will be loading those.
if (driver != typeof(WorkflowStorageDriver))
continue;
var outputValue = activityExecutionContext.Get(memoryBlockReference);
if (outputValue != null)
outputValues[outputDescriptor.Name] = outputValue;
}
// Resume the workflow, passing along the activity output.
var bookmarkId = scheduledBackgroundActivity.BookmarkId;
var inputKey = BackgroundActivityInvokerMiddleware.GetBackgroundActivityOutputKey(activityNodeId);
var outcomesKey = BackgroundActivityInvokerMiddleware.GetBackgroundActivityOutcomesKey(activityNodeId);
var journalDataKey = BackgroundActivityInvokerMiddleware.GetBackgroundActivityJournalDataKey(activityNodeId);
var dispatchRequest = new DispatchWorkflowInstanceRequest
{
InstanceId = workflowInstanceId,
BookmarkId = bookmarkId,
Properties = new Dictionary<string, object>
{
[outcomesKey] = outcomes,
[inputKey] = outputValues,
[journalDataKey] = activityExecutionContext.JournalData
}
};
if (cancellationToken.IsCancellationRequested)
2023-04-13 18:35:55 +00:00
{
_logger.LogInformation("Background execution for activity {ActivityNodeId} was canceled", activityNodeId);
return;
}
await _workflowDispatcher.DispatchAsync(dispatchRequest, cancellationToken);
}
}