示例#1
0
        public virtual async Task <RunWorkflowResult> RunWorkflowAsync(
            IWorkflowBlueprint workflowBlueprint,
            WorkflowInstance workflowInstance,
            string?activityId   = default,
            WorkflowInput?input = default,
            CancellationToken cancellationToken = default)
        {
            using var loggingScope           = _logger.BeginScope(new Dictionary <string, object> { ["WorkflowInstanceId"] = workflowInstance.Id });
            using var workflowExecutionScope = _serviceScopeFactory.CreateScope();

            if (input?.Input != null)
            {
                var workflowStorageContext = new WorkflowStorageContext(workflowInstance, workflowBlueprint.Id);
                var inputStorageProvider   = _workflowStorageService.GetProviderByNameOrDefault(input.StorageProviderName);
                await inputStorageProvider.SaveAsync(workflowStorageContext, nameof(WorkflowInstance.Input), input.Input, cancellationToken);

                workflowInstance.Input = new WorkflowInputReference(inputStorageProvider.Name);
                await _mediator.Publish(new WorkflowInputUpdated(workflowInstance), cancellationToken);
            }

            var workflowExecutionContext = new WorkflowExecutionContext(workflowExecutionScope.ServiceProvider, workflowBlueprint, workflowInstance, input?.Input);
            var result = await RunWorkflowInternalAsync(workflowExecutionContext, activityId, cancellationToken);

            await workflowExecutionContext.WorkflowExecutionLog.FlushAsync(cancellationToken);

            return(result);
        }
示例#2
0
        public async Task <RunWorkflowResult> BuildAndStartWorkflowAsync <T>(
            string?activityId    = default,
            WorkflowInput?input  = default,
            string?correlationId = default,
            string?contextId     = default,
            CancellationToken cancellationToken = default) where T : IWorkflow
        {
            var workflowBlueprint = _workflowBuilderFactory().Build <T>();

            return(await StartWorkflowAsync(workflowBlueprint, activityId, input, correlationId, contextId, cancellationToken : cancellationToken));
        }
示例#3
0
        public async Task StartWorkflowsAsync(
            string activityType,
            IBookmark bookmark,
            string?tenantId,
            WorkflowInput?input = default,
            string?contextId    = default,
            CancellationToken cancellationToken = default)
        {
            var results = await _triggerFinder.FindTriggersAsync(activityType, bookmark, tenantId, cancellationToken).ToList();

            await StartWorkflowsAsync(results, input, contextId, cancellationToken);
        }
示例#4
0
        public async Task FindAndResumeWorkflowsAsync(
            string activityType,
            IBookmark bookmark,
            string?tenantId,
            WorkflowInput?input  = default,
            string?correlationId = default,
            string?contextId     = default,
            CancellationToken cancellationToken = default)
        {
            var results = await _bookmarkFinder.FindBookmarksAsync(activityType, bookmark, correlationId, tenantId, cancellationToken : cancellationToken).ToList();

            await ResumeWorkflowsAsync(results, input, cancellationToken);
        }
示例#5
0
        public async Task <IEnumerable <RunWorkflowResult> > StartWorkflowsAsync(
            IEnumerable <IWorkflowBlueprint> workflowBlueprints,
            WorkflowInput?input = default,
            string?contextId    = default,
            CancellationToken cancellationToken = default)
        {
            var runWorkflowResults = new List <RunWorkflowResult>();

            foreach (var workflowBlueprint in workflowBlueprints)
            {
                var runWorkflowResult = await StartWorkflowAsync(workflowBlueprint, null, input, contextId : contextId, cancellationToken : cancellationToken);

                runWorkflowResults.Add(runWorkflowResult);
            }

            return(runWorkflowResults);
        }
示例#6
0
        public async Task <IEnumerable <RunWorkflowResult> > StartWorkflowsAsync(
            IEnumerable <TriggerFinderResult> results,
            WorkflowInput?input = default,
            string?contextId    = default,
            CancellationToken cancellationToken = default)
        {
            var runWorkflowResults = new List <RunWorkflowResult>();

            foreach (var result in results)
            {
                var workflowBlueprint = result.WorkflowBlueprint;
                var runWorkflowResult = await StartWorkflowAsync(workflowBlueprint, result.ActivityId, input, contextId : contextId, cancellationToken : cancellationToken);

                runWorkflowResults.Add(runWorkflowResult);
            }

            return(runWorkflowResults);
        }
示例#7
0
        public async Task <RunWorkflowResult> StartWorkflowAsync(
            IWorkflowBlueprint workflowBlueprint,
            string?activityId    = default,
            WorkflowInput?input  = default,
            string?correlationId = default,
            string?contextId     = default,
            string?tenantId      = default,
            CancellationToken cancellationToken = default)
        {
            var workflowInstance = await _workflowFactory.InstantiateAsync(
                workflowBlueprint,
                correlationId,
                contextId,
                cancellationToken : cancellationToken);

            await _workflowInstanceStore.SaveAsync(workflowInstance, cancellationToken);

            return(await _workflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, input, cancellationToken));
        }
示例#8
0
        public async Task FindAndExecuteStartableWorkflowAsync(
            string workflowDefinitionId,
            string?activityId,
            string?correlationId = default,
            string?contextId     = default,
            WorkflowInput?input  = default,
            string?tenantId      = default,
            CancellationToken cancellationToken = default)
        {
            var workflowBlueprint = await _workflowRegistry.GetAsync(workflowDefinitionId, tenantId, VersionOptions.Published, cancellationToken);

            if (workflowBlueprint == null)
            {
                _logger.LogWarning("Could not find workflow with ID {WorkflowDefinitionId}", workflowDefinitionId);
                return;
            }

            await FindAndExecuteStartableWorkflowAsync(workflowBlueprint, activityId, correlationId, contextId, input, cancellationToken);
        }
示例#9
0
        public async Task <RunWorkflowResult> FindAndExecuteStartableWorkflowAsync(
            IWorkflowBlueprint workflowBlueprint,
            string?activityId,
            string?correlationId = default,
            string?contextId     = default,
            WorkflowInput?input  = default,
            CancellationToken cancellationToken = default)
        {
            var workflowDefinitionId = workflowBlueprint.Id;
            var tenantId             = workflowBlueprint.TenantId;

            var startableWorkflow = await FindStartableWorkflowAsync(workflowBlueprint, activityId, correlationId, contextId, tenantId, cancellationToken);

            if (startableWorkflow == null)
            {
                throw new WorkflowException($"Could not start workflow with ID {workflowDefinitionId}");
            }

            return(await ExecuteStartableWorkflowAsync(startableWorkflow, input, cancellationToken));
        }
示例#10
0
        public async Task <RunWorkflowResult> ResumeWorkflowAsync(WorkflowInstance workflowInstance, string?activityId = default, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            var workflowBlueprint = await _workflowRegistry.GetAsync(
                workflowInstance.DefinitionId,
                workflowInstance.TenantId,
                VersionOptions.SpecificVersion(workflowInstance.Version),
                cancellationToken);

            if (workflowBlueprint == null)
            {
                throw new WorkflowException($"Workflow instance {workflowInstance.Id} references workflow definition {workflowInstance.DefinitionId} version {workflowInstance.Version}, but no such workflow definition was found.");
            }

            if (workflowBlueprint.IsDisabled)
            {
                return(new RunWorkflowResult(workflowInstance, activityId, false));
            }

            return(await _workflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, input, cancellationToken));
        }
示例#11
0
        public async Task <RunWorkflowResult> BuildAndResumeWorkflowAsync(IWorkflow workflow, WorkflowInstance workflowInstance, string?activityId = default, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            var workflowBlueprint = _workflowBuilderFactory().Build(workflow);

            return(await _workflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, input, cancellationToken));
        }
示例#12
0
        public virtual async Task <RunWorkflowResult> RunWorkflowAsync(
            IWorkflowBlueprint workflowBlueprint,
            WorkflowInstance workflowInstance,
            string?activityId   = default,
            WorkflowInput?input = default,
            CancellationToken cancellationToken = default)
        {
            using var loggingScope           = _logger.BeginScope(new Dictionary <string, object> { ["WorkflowInstanceId"] = workflowInstance.Id });
            using var workflowExecutionScope = _serviceScopeFactory.CreateScope();

            if (input?.Input != null)
            {
                var workflowStorageContext = new WorkflowStorageContext(workflowInstance, workflowBlueprint.Id);
                var inputStorageProvider   = _workflowStorageService.GetProviderByNameOrDefault(input.StorageProviderName);
                await inputStorageProvider.SaveAsync(workflowStorageContext, nameof(WorkflowInstance.Input), input.Input, cancellationToken);

                workflowInstance.Input = new WorkflowInputReference(inputStorageProvider.Name);
                await _mediator.Publish(new WorkflowInputUpdated(workflowInstance), cancellationToken);
            }

            var workflowExecutionContext = new WorkflowExecutionContext(workflowExecutionScope.ServiceProvider, workflowBlueprint, workflowInstance, input?.Input);

            if (!string.IsNullOrWhiteSpace(workflowInstance.ContextId))
            {
                var loadContext = new LoadWorkflowContext(workflowExecutionContext);
                workflowExecutionContext.WorkflowContext = await _workflowContextManager.LoadContext(loadContext, cancellationToken);
            }

            // If the workflow instance has a CurrentActivity, it means the workflow instance is being retried.
            var currentActivity = workflowInstance.CurrentActivity;

            if (currentActivity != null)
            {
                activityId = currentActivity.ActivityId;
            }

            var activity = activityId != null?workflowBlueprint.GetActivity(activityId) : default;

            // Give application a chance to prevent workflow from executing.
            var validateWorkflowExecution = new ValidateWorkflowExecution(workflowExecutionContext, activity);
            await _mediator.Publish(validateWorkflowExecution, cancellationToken);

            if (!validateWorkflowExecution.CanExecuteWorkflow)
            {
                _logger.LogInformation("Workflow execution prevented for workflow {WorkflowInstanceId}", workflowInstance.Id);
                return(new RunWorkflowResult(workflowInstance, activityId, false));
            }

            await _mediator.Publish(new WorkflowExecuting(workflowExecutionContext), cancellationToken);

            RunWorkflowResult runWorkflowResult;

            switch (workflowExecutionContext.Status)
            {
            case WorkflowStatus.Idle:
                runWorkflowResult = await BeginWorkflow(workflowExecutionContext, activity, cancellationToken);

                if (!runWorkflowResult.Executed)
                {
                    if (workflowInstance.WorkflowStatus != WorkflowStatus.Faulted)
                    {
                        _logger.LogDebug("Workflow {WorkflowInstanceId} cannot begin from an idle state (perhaps it needs a specific input)", workflowInstance.Id);
                        return(runWorkflowResult);
                    }
                }

                break;

            case WorkflowStatus.Running:
                await RunWorkflowAsync(workflowExecutionContext, cancellationToken);

                runWorkflowResult = new RunWorkflowResult(workflowInstance, activityId, true);
                break;

            case WorkflowStatus.Suspended:
                runWorkflowResult = await ResumeWorkflowAsync(workflowExecutionContext, activity !, cancellationToken);

                if (!runWorkflowResult.Executed)
                {
                    _logger.LogDebug("Workflow {WorkflowInstanceId} cannot be resumed from a suspended state (perhaps it needs a specific input)", workflowInstance.Id);
                    return(runWorkflowResult);
                }

                break;

            default:
                throw new ArgumentOutOfRangeException();
            }

            await _mediator.Publish(new WorkflowExecuted(workflowExecutionContext), cancellationToken);

            var statusEvent = workflowExecutionContext.Status switch
            {
                WorkflowStatus.Cancelled => new WorkflowCancelled(workflowExecutionContext),
                WorkflowStatus.Finished => new WorkflowCompleted(workflowExecutionContext),
                WorkflowStatus.Faulted => new WorkflowFaulted(workflowExecutionContext),
                WorkflowStatus.Suspended => new WorkflowSuspended(workflowExecutionContext),
                _ => default(INotification)
            };

            if (statusEvent != null)
            {
                await _mediator.Publish(statusEvent, cancellationToken);
            }

            await _mediator.Publish(new WorkflowExecutionFinished(workflowExecutionContext), cancellationToken);

            return(runWorkflowResult);
        }
示例#13
0
        public async Task <IEnumerable <CollectedWorkflow> > CollectAndDispatchWorkflowsAsync(WorkflowsQuery query, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            var pendingWorkflows = await FindWorkflowsAsync(query, cancellationToken).ToList();

            await DispatchPendingWorkflowsAsync(pendingWorkflows, input, cancellationToken);

            return(pendingWorkflows);
        }
示例#14
0
        public async Task <RunWorkflowResult> ExecuteAsync(WorkflowInstance workflowInstance, string?activityId, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            if (!ValidatePreconditions(workflowInstance.Id, workflowInstance, activityId))
            {
                return(new RunWorkflowResult(workflowInstance, activityId, null, false));
            }

            if (input != null)
            {
                workflowInstance !.Input = await _workflowStorageService.SaveAsync(input, workflowInstance, cancellationToken);
            }

            return(await _workflowRunner.ResumeWorkflowAsync(
                       workflowInstance !,
                       activityId,
                       cancellationToken));
        }
示例#15
0
 public async Task <RunWorkflowResult> ExecuteAsync(WorkflowInstance workflowInstance, string?activityId, WorkflowInput?input = default, CancellationToken cancellationToken = default) =>
 await _workflowInstanceExecutor.ExecuteAsync(workflowInstance, activityId, input, cancellationToken);
示例#16
0
        private async IAsyncEnumerable <RunWorkflowResult> InterruptActivityTypeInternalAsync(string activityType, WorkflowInput?input, [EnumeratorCancellation] CancellationToken cancellationToken)
        {
            var bookmarks = await _bookmarkFinder.FindBookmarksAsync(activityType, Enumerable.Empty <IBookmark>(), cancellationToken : cancellationToken);

            var workflowInstanceIds = bookmarks.Select(x => x.WorkflowInstanceId).Distinct().ToList();
            var workflowInstances   = await _workflowInstanceStore.FindManyAsync(new ManyWorkflowInstanceIdsSpecification(workflowInstanceIds), cancellationToken : cancellationToken);

            foreach (var workflowInstance in workflowInstances)
            {
                var results = await InterruptActivityTypeAsync(workflowInstance, activityType, input, cancellationToken);

                foreach (var result in results)
                {
                    yield return(result);
                }
            }
        }
示例#17
0
 public async Task <IEnumerable <RunWorkflowResult> > InterruptActivityTypeAsync(string activityType, WorkflowInput?input, CancellationToken cancellationToken) =>
 await InterruptActivityTypeInternalAsync(activityType, input, cancellationToken).ToListAsync(cancellationToken);
示例#18
0
        public async Task <IEnumerable <RunWorkflowResult> > InterruptActivityTypeAsync(WorkflowInstance workflowInstance, string activityType, WorkflowInput?input, CancellationToken cancellationToken = default)
        {
            var workflowBlueprint = await GetWorkflowBlueprintAsync(workflowInstance, cancellationToken);

            return(await InterruptActivityTypeAsync(workflowBlueprint !, workflowInstance, activityType, input, cancellationToken));
        }
示例#19
0
        public async Task <IEnumerable <RunWorkflowResult> > InterruptActivityTypeAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityType, WorkflowInput?input, CancellationToken cancellationToken)
        {
            var blockingActivities = workflowInstance.BlockingActivities.Where(x => x.ActivityType == activityType).ToList();
            var results            = new List <RunWorkflowResult>();

            foreach (var blockingActivity in blockingActivities)
            {
                var result = await InterruptActivityAsync(workflowBlueprint, workflowInstance, blockingActivity.ActivityId, input, cancellationToken);

                results.Add(result);
            }

            return(results);
        }
示例#20
0
        public async Task <RunWorkflowResult> InterruptActivityAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityId, WorkflowInput?input, CancellationToken cancellationToken)
        {
            if (workflowInstance.WorkflowStatus != WorkflowStatus.Suspended)
            {
                throw new WorkflowException("Cannot interrupt workflows that are not in the Suspended state.");
            }

            var blockingActivity = workflowInstance.BlockingActivities.SingleOrDefault(x => x.ActivityId == activityId);

            if (blockingActivity == null)
            {
                throw new WorkflowException($"No blocking activity with ID {activityId} found.");
            }

            return(await _workflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, input, cancellationToken));
        }
示例#21
0
        public async Task <RunWorkflowResult> InterruptActivityAsync(WorkflowInstance workflowInstance, string activityId, WorkflowInput?input, CancellationToken cancellationToken)
        {
            var workflowBlueprint = await GetWorkflowBlueprintAsync(workflowInstance, cancellationToken);

            return(await InterruptActivityAsync(workflowBlueprint !, workflowInstance, activityId, input, cancellationToken));
        }
        public async Task <RunWorkflowResult> ExecuteAsync(WorkflowInstance workflowInstance, string?activityId, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            if (!ValidatePreconditions(workflowInstance.Id, workflowInstance, activityId))
            {
                return(new RunWorkflowResult(workflowInstance, activityId, false));
            }

            return(await _workflowRunner.ResumeWorkflowAsync(
                       workflowInstance !,
                       activityId,
                       input,
                       cancellationToken));
        }
示例#23
0
        public async Task <RunWorkflowResult> ExecuteAsync(string workflowInstanceId, string?activityId, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            var workflowInstanceLockKey = $"workflow-instance:{workflowInstanceId}";

            await using var workflowInstanceLockHandle = await _distributedLockProvider.AcquireLockAsync(workflowInstanceLockKey, _elsaOptions.DistributedLockTimeout, cancellationToken);

            if (workflowInstanceLockHandle == null)
            {
                throw new LockAcquisitionException("Could not acquire a lock within the configured amount of time");
            }

            var workflowInstance = await _workflowInstanceStore.FindByIdAsync(workflowInstanceId, cancellationToken);

            if (workflowInstance == null)
            {
                _logger.LogWarning("Could not run workflow instance with ID {WorkflowInstanceId} because it does not exist", workflowInstanceId);
                return(new RunWorkflowResult(workflowInstance, activityId, false));
            }

            var correlationId = workflowInstance.CorrelationId;

            if (!string.IsNullOrWhiteSpace(correlationId))
            {
                // We need to lock on correlation ID to prevent a race condition with WorkflowLaunchpad that is used to find workflows by correlation ID to execute.
                // The race condition is: when a workflow instance is done executing, the BookmarkIndexer will collect bookmarks.
                // But if in the meantime an event comes in that triggers correlated workflows, the bookmarks may not have been created yet.
                await using var correlationLockHandle = await _distributedLockProvider.AcquireLockAsync(correlationId, _elsaOptions.DistributedLockTimeout, cancellationToken);

                if (correlationLockHandle == null)
                {
                    throw new LockAcquisitionException($"Could not acquire a lock on correlation {correlationId} within the configured amount of time");
                }

                var result = await _workflowInstanceExecutor.ExecuteAsync(workflowInstance, activityId, input, cancellationToken);

                return(result);
            }
            else
            {
                var result = await _workflowInstanceExecutor.ExecuteAsync(workflowInstance, activityId, input, cancellationToken);

                return(result);
            }
        }
示例#24
0
        public async Task <IEnumerable <CollectedWorkflow> > CollectAndExecuteWorkflowsAsync(WorkflowsQuery query, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            var pendingWorkflows = await FindWorkflowsAsync(query, cancellationToken).ToList();

            await ExecutePendingWorkflowsAsync(pendingWorkflows, input, cancellationToken);

            return(pendingWorkflows.Select(x => new CollectedWorkflow(x.WorkflowInstanceId, x.ActivityId)));
        }
示例#25
0
        public async Task <RunWorkflowResult> ExecuteAsync(string workflowInstanceId, string?activityId, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            var workflowInstance = await WorkflowInstanceStore.FindByIdAsync(workflowInstanceId, cancellationToken);

            if (!ValidatePreconditions(workflowInstanceId, workflowInstance, activityId))
            {
                return(new RunWorkflowResult(workflowInstance, activityId, null, false));
            }

            await _workflowStorageService.UpdateInputAsync(workflowInstance !, input, cancellationToken);

            return(await _workflowRunner.ResumeWorkflowAsync(
                       workflowInstance !,
                       activityId,
                       cancellationToken));
        }
示例#26
0
        public async Task <CollectedWorkflow> DispatchStartableWorkflowAsync(StartableWorkflow startableWorkflow, WorkflowInput?input, CancellationToken cancellationToken = default)
        {
            var pendingWorkflow = new CollectedWorkflow(startableWorkflow.WorkflowInstance.Id, startableWorkflow.ActivityId);

            await DispatchPendingWorkflowAsync(pendingWorkflow, input, cancellationToken);

            return(pendingWorkflow);
        }
示例#27
0
 private async Task <RunWorkflowResult> ExecuteStartableWorkflowAsync(StartableWorkflow startableWorkflow, WorkflowInput?input, CancellationToken cancellationToken = default) =>
 await _workflowRunner.RunWorkflowAsync(startableWorkflow.WorkflowBlueprint, startableWorkflow.WorkflowInstance, startableWorkflow.ActivityId, input, cancellationToken);
示例#28
0
 public Task DispatchPendingWorkflowAsync(string workflowInstanceId, string?activityId, WorkflowInput?input, CancellationToken cancellationToken = default) =>
 DispatchPendingWorkflowAsync(new CollectedWorkflow(workflowInstanceId, activityId), input, cancellationToken);
示例#29
0
        public async Task ResumeWorkflowsAsync(IEnumerable <BookmarkFinderResult> results, WorkflowInput?input = default, CancellationToken cancellationToken = default)
        {
            foreach (var result in results)
            {
                var workflowInstance = await _workflowInstanceStore.FindByIdAsync(result.WorkflowInstanceId, cancellationToken);

                if (workflowInstance?.WorkflowStatus == WorkflowStatus.Suspended)
                {
                    await _workflowStorageService.UpdateInputAsync(workflowInstance, input, cancellationToken);
                    await ResumeWorkflowAsync(workflowInstance !, result.ActivityId, cancellationToken);
                }
            }
        }
示例#30
0
 public static async ValueTask UpdateInputAsync(this IWorkflowStorageService service, WorkflowInstance workflowInstance, WorkflowInput?workflowInput, CancellationToken cancellationToken = default)
 {
     if (workflowInput != null)
     {
         workflowInstance !.Input = await service.SaveAsync(workflowInput, workflowInstance, cancellationToken);
     }
 }