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); }
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)); }
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); }
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); }
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); }
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); }
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)); }
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); }
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)); }
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)); }
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)); }
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); }
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); }
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)); }
public async Task <RunWorkflowResult> ExecuteAsync(WorkflowInstance workflowInstance, string?activityId, WorkflowInput?input = default, CancellationToken cancellationToken = default) => await _workflowInstanceExecutor.ExecuteAsync(workflowInstance, activityId, input, cancellationToken);
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); } } }
public async Task <IEnumerable <RunWorkflowResult> > InterruptActivityTypeAsync(string activityType, WorkflowInput?input, CancellationToken cancellationToken) => await InterruptActivityTypeInternalAsync(activityType, input, cancellationToken).ToListAsync(cancellationToken);
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)); }
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); }
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)); }
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)); }
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); } }
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))); }
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)); }
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); }
private async Task <RunWorkflowResult> ExecuteStartableWorkflowAsync(StartableWorkflow startableWorkflow, WorkflowInput?input, CancellationToken cancellationToken = default) => await _workflowRunner.RunWorkflowAsync(startableWorkflow.WorkflowBlueprint, startableWorkflow.WorkflowInstance, startableWorkflow.ActivityId, input, cancellationToken);
public Task DispatchPendingWorkflowAsync(string workflowInstanceId, string?activityId, WorkflowInput?input, CancellationToken cancellationToken = default) => DispatchPendingWorkflowAsync(new CollectedWorkflow(workflowInstanceId, activityId), input, cancellationToken);
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); } } }
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); } }