public static bool WithVersion(this IWorkflowBlueprint workflowBlueprint, VersionOptions version) { if (version.IsDraft) { return(!workflowBlueprint.IsPublished); } if (version.IsLatest) { return(workflowBlueprint.IsLatest); } if (version.IsPublished) { return(workflowBlueprint.IsPublished); } if (version.IsLatestOrPublished) { return(workflowBlueprint.IsPublished || workflowBlueprint.IsLatest); } if (version.AllVersions) { return(true); } if (version.Version > 0) { return(workflowBlueprint.Version == version.Version); } return(true); }
public virtual async Task <RunWorkflowResult> RunWorkflowAsync( IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string?activityId = default, //WorkflowInput? workflowInput = default, CancellationToken cancellationToken = default) { using var loggingScope = _logger.BeginScope(new Dictionary <string, object> { ["WorkflowInstanceId"] = workflowInstance.Id }); using var workflowExecutionScope = _serviceScopeFactory.CreateScope(); // var input = workflowInput?.Input ?? await _workflowStorageService.LoadAsync(workflowInstance, cancellationToken); // // // If input provided, update the workflow instance with this input. // if (input != null) // workflowInstance.Input = await _workflowStorageService.SaveAsync(workflowInput!, workflowInstance, cancellationToken); // // If no input was provided, load the input associated with the workflow instance (if any). // else var input = await _workflowStorageService.LoadAsync(workflowInstance, cancellationToken); var workflowExecutionContext = new WorkflowExecutionContext(workflowExecutionScope.ServiceProvider, workflowBlueprint, workflowInstance, input); var result = await RunWorkflowInternalAsync(workflowExecutionContext, activityId, cancellationToken); await workflowExecutionContext.WorkflowExecutionLog.FlushAsync(cancellationToken); return(result); }
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 TriggerFinderResult(IWorkflowBlueprint workflowBlueprint, string activityId, string activityType, IBookmark bookmark) { WorkflowBlueprint = workflowBlueprint; ActivityId = activityId; ActivityType = activityType; Bookmark = bookmark; }
public static object?GetActivityPropertyRawValue(this IWorkflowBlueprint workflowBlueprint, string activityId, string propertyName) { var provider = workflowBlueprint.ActivityPropertyProviders.GetProvider(activityId, propertyName); var value = provider?.RawValue; return(value); }
public static async ValueTask <object?> EvaluateActivityPropertyValue( this IWorkflowBlueprint workflowBlueprint, string activityId, string propertyName, ActivityExecutionContext activityExecutionContext, CancellationToken cancellationToken) { var provider = workflowBlueprint.ActivityPropertyProviders.GetProvider(activityId, propertyName); var value = provider != null ? await provider.GetValueAsync(activityExecutionContext, cancellationToken) : null; if (value == null) { // Get default value. var activityTypeService = activityExecutionContext.GetService <IActivityTypeService>(); var activityBlueprint = workflowBlueprint.GetActivity(activityId) !; var activityType = await activityTypeService.GetActivityTypeAsync(activityBlueprint.Type, cancellationToken); var activityDescriptor = await activityTypeService.DescribeActivityType(activityType, cancellationToken); var propertyDescriptor = activityDescriptor.InputProperties.FirstOrDefault(x => x.Name == propertyName); value = propertyDescriptor?.DefaultValue; } return(value); }
public WorkflowTrigger(IWorkflowBlueprint workflowBlueprint, string activityId, string activityType, string bookmarkHash, IBookmark bookmark) { WorkflowBlueprint = workflowBlueprint; ActivityId = activityId; ActivityType = activityType; BookmarkHash = bookmarkHash; Bookmark = bookmark; }
public static async ValueTask<T?> GetActivityPropertyValue<T>( this IWorkflowBlueprint workflowBlueprint, string activityId, string propertyName, ActivityExecutionContext activityExecutionContext, CancellationToken cancellationToken) { var value = await workflowBlueprint.GetActivityPropertyValue(activityId, propertyName, activityExecutionContext, cancellationToken); return value == null ? default : (T) value; }
public async Task <WorkflowInstance> InterruptActivityTypeAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityType, object?input, CancellationToken cancellationToken) { var blockingActivities = workflowInstance.BlockingActivities.Where(x => x.ActivityType == activityType).ToList(); foreach (var blockingActivity in blockingActivities) { workflowInstance = await InterruptActivityAsync(workflowBlueprint, workflowInstance, blockingActivity.ActivityId, input, cancellationToken); } return(workflowInstance); }
public static async ValueTask<object?> GetActivityPropertyValue( this IWorkflowBlueprint workflowBlueprint, string activityId, string propertyName, ActivityExecutionContext activityExecutionContext, CancellationToken cancellationToken) { var provider = workflowBlueprint.ActivityPropertyProviders.GetProvider(activityId, propertyName); if (provider == null) return default!;
public static ValueTask<T?> GetActivityPropertyValue<TActivity, T>( this IWorkflowBlueprint workflowBlueprint, string activityId, Expression<Func<TActivity, T>> propertyExpression, ActivityExecutionContext activityExecutionContext, CancellationToken cancellationToken) where TActivity : IActivity { var expression = (MemberExpression) propertyExpression.Body; string propertyName = expression.Member.Name; return GetActivityPropertyValue<T>(workflowBlueprint, activityId, propertyName, activityExecutionContext, cancellationToken); }
public async ValueTask <WorkflowBlueprintModel> MapAsync(IWorkflowBlueprint workflowBlueprint, CancellationToken cancellationToken = default) { using var scope = _serviceScopeFactory.CreateScope(); var wrapper = await _workflowBlueprintReflector.ReflectAsync(scope.ServiceProvider, workflowBlueprint, cancellationToken); var activityProperties = await Task.WhenAll(wrapper.Activities.Select(async x => (x.ActivityBlueprint.Id, await GetActivityPropertiesAsync(wrapper, x, cancellationToken)))); var activityPropertyDictionary = activityProperties.ToDictionary(x => x.Id, x => x.Item2); return(_mapper.Map <WorkflowBlueprintModel>(workflowBlueprint, options => options.Items[ActivityBlueprintConverter.ActivityPropertiesKey] = activityPropertyDictionary)); }
public async Task <StartableWorkflow?> FindStartableWorkflowAsync( IWorkflowBlueprint workflowBlueprint, string?activityId, string?correlationId = default, string?contextId = default, string?tenantId = default, CancellationToken cancellationToken = default) { async Task <StartableWorkflow?> CollectWorkflows() { var startableWorkflowDefinition = await CollectStartableWorkflowInternalAsync(workflowBlueprint, activityId, correlationId, contextId, tenantId, cancellationToken); return(startableWorkflowDefinition != null ? await InstantiateStartableWorkflow(startableWorkflowDefinition, cancellationToken) : default);
private async Task <IList <WorkflowTrigger> > GetTriggersForBookmarkProvider( IBookmarkProvider provider, BookmarkProviderContext context, IActivityBlueprint activityBlueprint, IWorkflowBlueprint workflowBlueprint, CancellationToken cancellationToken = default) { var bookmarkResults = (await provider.GetBookmarksAsync(context, cancellationToken)).ToList(); return(bookmarkResults .Select(x => new WorkflowTrigger(workflowBlueprint, activityBlueprint.Id, x.ActivityTypeName ?? activityBlueprint.Type, _bookmarkHasher.Hash(x.Bookmark), x.Bookmark)) .ToList()); }
private async Task <WorkflowInstance> TriggerSignalAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string signal) { var workflowExecutionContext = new WorkflowExecutionContext(ServiceScope.ServiceProvider, workflowBlueprint, workflowInstance); var workflowBlueprintWrapper = new WorkflowBlueprintWrapper(workflowBlueprint, workflowExecutionContext); var activities = workflowBlueprintWrapper.Activities.Where(x => x.ActivityBlueprint.Type == nameof(SignalReceived)); var blockingActivityIds = workflowInstance.BlockingActivities.Where(x => x.ActivityType == nameof(SignalReceived)).Select(x => x.ActivityId).ToList(); var receiveSignalActivities = activities.Where(x => blockingActivityIds.Contains(x.ActivityBlueprint.Id)).ToList(); var receiveSignal = receiveSignalActivities.Single(activity => workflowBlueprintWrapper.GetActivity <SignalReceived>(activity.ActivityBlueprint.Id) !.EvaluatePropertyValueAsync(x => x.Signal).GetAwaiter().GetResult() == signal); var triggeredSignal = new Signal(signal); var result = await WorkflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, receiveSignal.ActivityBlueprint.Id, new WorkflowInput(triggeredSignal)); return(result.WorkflowInstance !); }
private async Task <IEnumerable <WorkflowTrigger> > GetTriggersInternalAsync(IWorkflowBlueprint workflowBlueprint, IDictionary <string, ActivityType> activityTypes, CancellationToken cancellationToken) { var startingActivityBlueprints = _startingActivitiesProvider.GetStartActivities(workflowBlueprint); var workflowExecutionContext = await _workflowExecutionContextFactory.CreateWorkflowExecutionContextAsync(workflowBlueprint, cancellationToken); var tasksOfCollectionsOfTriggers = startingActivityBlueprints .Select(async activityBlueprint => await _triggerProvider.GetTriggersForActivityBlueprintAsync(activityBlueprint, workflowExecutionContext, activityTypes, cancellationToken)); return((await Task.WhenAll(tasksOfCollectionsOfTriggers)) .SelectMany(x => x) .ToList()); }
public WorkflowExecutionContext( IServiceProvider serviceProvider, IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, object?input = default ) { ServiceProvider = serviceProvider; WorkflowBlueprint = workflowBlueprint; WorkflowInstance = workflowInstance; Input = input; IsFirstPass = true; Serializer = serviceProvider.GetRequiredService <JsonSerializer>(); Mediator = serviceProvider.GetRequiredService <IMediator>(); }
public async Task <StartableWorkflow?> CollectStartableWorkflowAsync( IWorkflowBlueprint workflowBlueprint, string?activityId, string?correlationId = default, string?contextId = default, string?tenantId = default, CancellationToken cancellationToken = default) { correlationId ??= Guid.NewGuid().ToString("N"); // Acquire a lock on correlation ID to prevent duplicate workflow instances from being created. await using var correlationLockHandle = await AcquireLockAsync(correlationId, cancellationToken); return(await CollectStartableWorkflowInternalAsync(workflowBlueprint, activityId, correlationId, contextId, tenantId, cancellationToken)); }
private async Task <StartableWorkflow?> CollectStartableWorkflowInternalAsync( IWorkflowBlueprint workflowBlueprint, string?activityId, string correlationId, string?contextId = default, string?tenantId = default, CancellationToken cancellationToken = default) { var workflowDefinitionId = workflowBlueprint.Id; if (!ValidatePreconditions(workflowDefinitionId, workflowBlueprint)) { return(null); } // Acquire a lock on the workflow definition so that we can ensure singleton-workflows never execute more than one instance. var lockKey = $"execute-workflow-definition:tenant:{tenantId}:workflow-definition:{workflowDefinitionId}"; await using var workflowDefinitionHandle = await AcquireLockAsync(lockKey, cancellationToken); if (workflowBlueprint.IsSingleton) { if (await GetWorkflowIsAlreadyExecutingAsync(tenantId, workflowDefinitionId)) { _logger.LogDebug("Workflow {WorkflowDefinitionId} is a singleton workflow and is already running", workflowDefinitionId); return(null); } } var startActivities = _getsStartActivities.GetStartActivities(workflowBlueprint).Select(x => x.Id).ToHashSet(); var startActivityId = activityId == null?startActivities.FirstOrDefault() : startActivities.Contains(activityId) ? activityId : default; if (startActivityId == null) { _logger.LogWarning("Cannot start workflow {WorkflowDefinitionId} with version {WorkflowDefinitionVersion} because it has no starting activities", workflowBlueprint.Id, workflowBlueprint.Version); return(null); } var workflowInstance = await _workflowFactory.InstantiateAsync( workflowBlueprint, correlationId, contextId, cancellationToken); await _workflowInstanceStore.SaveAsync(workflowInstance, cancellationToken); return(new StartableWorkflow(workflowBlueprint, workflowInstance, startActivityId)); }
private static async Task <object?> GetPropertyValueAsync(IWorkflowBlueprint workflowBlueprint, IActivityBlueprintWrapper activityBlueprintWrapper, ActivityInputDescriptor propertyDescriptor, CancellationToken cancellationToken) { if (propertyDescriptor.IsDesignerCritical) { try { return(await activityBlueprintWrapper.EvaluatePropertyValueAsync(propertyDescriptor.Name, cancellationToken)); } catch (Exception e) { throw new WorkflowException("Failed to evaluate a designer-critical property value. Please make sure that the value does not rely on external context.", e); } } return(workflowBlueprint.GetActivityPropertyRawValue(activityBlueprintWrapper.ActivityBlueprint.Id, propertyDescriptor.Name)); }
public async Task <RunWorkflowResult> InterruptActivityAsync(IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string activityId, object?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 WorkflowExecutionContext( IServiceProvider serviceProvider, IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, object?input = default ) { ServiceProvider = serviceProvider; WorkflowBlueprint = workflowBlueprint; WorkflowInstance = workflowInstance; Input = input; IsFirstPass = true; Serializer = serviceProvider.GetRequiredService <JsonSerializer>(); Mediator = serviceProvider.GetRequiredService <IMediator>(); // This is a service that needs to be bound to the same lifetime scope as the workflow execution context. WorkflowExecutionLog = ActivatorUtilities.CreateInstance <WorkflowExecutionLog>(serviceProvider); }
public async Task <WorkflowInstance> StartWorkflowAsync( IWorkflowBlueprint workflowBlueprint, string?activityId = default, object?input = default, string?correlationId = default, string?contextId = default, CancellationToken cancellationToken = default) { var workflowInstance = await _workflowFactory.InstantiateAsync( workflowBlueprint, correlationId, contextId, cancellationToken); await _workflowInstanceStore.SaveAsync(workflowInstance, cancellationToken); return(await _workflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, 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 <WorkflowInstance> RunWorkflowAsync( IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string?activityId = default, object?input = default, CancellationToken cancellationToken = default) { var key = $"locking-workflow-runner:{workflowInstance.Id}"; await using var handle = await _distributedLockProvider.AcquireLockAsync(key, _elsaOptions.DistributedLockTimeout, cancellationToken); if (handle == null) { throw new LockAcquisitionException("Could not acquire a lock within the configured amount of time"); } workflowInstance = await _workflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, input, cancellationToken); await handle.DisposeAsync(); return(workflowInstance); }
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, tenantId, cancellationToken); await _workflowStorageService.UpdateInputAsync(workflowInstance, input, cancellationToken); await _workflowInstanceStore.SaveAsync(workflowInstance, cancellationToken); return(await _workflowRunner.RunWorkflowAsync(workflowBlueprint, workflowInstance, activityId, cancellationToken)); }
public Task <WorkflowInstance> InstantiateAsync( IWorkflowBlueprint workflowBlueprint, string?correlationId = default, string?contextId = default, CancellationToken cancellationToken = default) { var workflowInstance = new WorkflowInstance { Id = _idGenerator.Generate(), DefinitionId = workflowBlueprint.Id, TenantId = workflowBlueprint.TenantId, Version = workflowBlueprint.Version, WorkflowStatus = WorkflowStatus.Idle, CorrelationId = correlationId, ContextId = contextId, CreatedAt = _clock.GetCurrentInstant(), Variables = new Variables(workflowBlueprint.Variables), ContextType = workflowBlueprint.ContextOptions?.ContextType?.GetContextTypeName() }; return(Task.FromResult(workflowInstance)); }
private static async Task <bool> WorkflowHasNonFinishedWorkflowsAsync(IWorkflowBlueprint workflowBlueprint, IWorkflowInstanceStore workflowInstanceStore, CancellationToken cancellationToken) { var count = await workflowInstanceStore.CountAsync(new UnfinishedWorkflowSpecification().WithWorkflowDefinition(workflowBlueprint.Id), cancellationToken); return(count > 0); }
private IDictionary <string, IDictionary <string, object?> > GetActivityDataFromLastWorkflowInstance(WorkflowInstance lastWorkflowInstance, IWorkflowBlueprint workflowBlueprint, string startingActivityId) { IDictionary <string, IDictionary <string, object?> > CollectSourceActivityData(string targetActivityId, IDictionary <string, IDictionary <string, object?> > activityDataAccumulator) { var sourceActivityId = workflowBlueprint.Connections.FirstOrDefault(x => x.Target.Activity.Id == targetActivityId)?.Source.Activity.Id; if (sourceActivityId == null) { return(activityDataAccumulator); } activityDataAccumulator.Add(sourceActivityId, lastWorkflowInstance.ActivityData.GetItem(sourceActivityId)); return(CollectSourceActivityData(sourceActivityId, activityDataAccumulator)); } return(CollectSourceActivityData(startingActivityId, new Dictionary <string, IDictionary <string, object?> >())); }
public virtual async Task <RunWorkflowResult> RunWorkflowAsync( IWorkflowBlueprint workflowBlueprint, WorkflowInstance workflowInstance, string?activityId = default, object?input = default, CancellationToken cancellationToken = default) { using var loggingScope = _logger.BeginScope(new Dictionary <string, object> { ["WorkflowInstanceId"] = workflowInstance.Id }); using var workflowExecutionScope = _serviceScopeFactory.CreateScope(); var workflowExecutionContext = new WorkflowExecutionContext(workflowExecutionScope.ServiceProvider, workflowBlueprint, workflowInstance, 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) { _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) { _logger.LogTrace("Publishing a status event of type {EventType} for workflow {WorkflowInstanceId}", statusEvent.GetType().Name, workflowInstance.Id); await _mediator.Publish(statusEvent, cancellationToken); } await _mediator.Publish(new WorkflowExecutionFinished(workflowExecutionContext), cancellationToken); return(runWorkflowResult); }