/// <summary> /// Handles the <see cref="BranchProcessor"/>'s execution /// </summary> /// <param name="processor">The <see cref="BranchProcessor"/> that has produced the <see cref="V1WorkflowActivityCompletedIntegrationEvent"/> to process</param> /// <param name="e">The <see cref="V1WorkflowActivityCompletedIntegrationEvent"/> to handle</param> /// <param name="cancellationToken">A <see cref="CancellationToken"/></param> /// <returns>A new awaitable <see cref="Task"/></returns> protected virtual async Task OnBranchExecutedAsync(BranchProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) { if (this.BranchesExecuted) { return; } var output = null as object; var childActivities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) .Where(a => a.Type == V1WorkflowActivityType.Branch) .ToList(); var executed = false; switch (this.State.CompletionType) { case ParallelCompletionType.AtLeastN: var executedActivities = childActivities .Where(p => p.Status >= V1WorkflowActivityStatus.Faulted) .ToList(); if (executedActivities.Count() >= this.State.N) { executed = true; output = new(); foreach (var executedActivity in executedActivities) { output = output.Merge(executedActivity.Output !.ToObject() !); } } break; case ParallelCompletionType.AllOf: if (childActivities.All(p => p.Status >= V1WorkflowActivityStatus.Faulted)) { executed = true; output = new(); foreach (var executedActivity in childActivities) { output = output.Merge(executedActivity.Output !.ToObject() !); } } break; default: throw new NotSupportedException($"The specified {nameof(ParallelCompletionType)} '{this.State.CompletionType}' is not supported"); } if (!executed) { return; } using (await this.Lock.LockAsync(cancellationToken)) { if (this.BranchesExecuted) { return; } else { this.BranchesExecuted = true; } } await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); }
/// <summary> /// Handles the next <see cref="IWorkflowActivityProcessor"/>'s <see cref="V1WorkflowActivityCompletedIntegrationEvent"/> /// </summary> /// <param name="processor">The <see cref="IWorkflowActivityProcessor"/> that returned the <see cref="V1WorkflowActivityCompletedIntegrationEvent"/></param> /// <param name="e">The <see cref="V1WorkflowActivityCompletedIntegrationEvent"/> to handle</param> /// <param name="cancellationToken">A <see cref="CancellationToken"/></param> /// <returns>A new awaitable <see cref="Task"/></returns> protected virtual async Task OnTriggerResultAsync(IWorkflowActivityProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e, CancellationToken cancellationToken) { using (await this.Lock.LockAsync(cancellationToken)) { if (this.Activity.Status == V1WorkflowActivityStatus.Completed) { return; } var completedActivities = (await this.Context.Workflow.GetActivitiesAsync(this.Activity, cancellationToken)) .Where(p => p.Type == V1WorkflowActivityType.EventTrigger && p.Status == V1WorkflowActivityStatus.Completed) .ToList(); if (this.State.Exclusive || completedActivities.Count == this.State.Triggers.Count) { var output = new object(); foreach (var activity in completedActivities .Where(p => p.Output != null)) { output = output.Merge(activity.Output.ToObject()); } await this.OnNextAsync(new V1WorkflowActivityCompletedIntegrationEvent(this.Activity.Id, output), cancellationToken); await this.OnCompletedAsync(cancellationToken); } } }
/// <summary> /// Handles the next <see cref="V1WorkflowActivityCompletedIntegrationEvent"/> /// </summary> /// <param name="processor">The <see cref="IWorkflowActivityProcessor"/> that has produced an <see cref="V1WorkflowActivityCompletedIntegrationEvent"/></param> /// <param name="e">The <see cref="V1WorkflowActivityCompletedIntegrationEvent"/> to process</param> /// <returns>A new awaitable <see cref="Task"/></returns> protected virtual async Task OnTransitionCompletedAsync(ITransitionProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e) { if (!this.Context.Workflow.Definition.TryGetState(processor.Transition.NextState, out StateDefinition nextState)) { throw new NullReferenceException($"Failed to find a state with name '{processor.Transition.NextState}' in workflow '{this.Context.Workflow.Definition.Id} {this.Context.Workflow.Definition.Version}'"); } await this.Context.Workflow.TransitionToAsync(nextState, this.CancellationToken); var metadata = new Dictionary <string, string>() { { V1WorkflowActivityMetadata.State, nextState.Name } }; var activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.State, e.Output, metadata, null, this.CancellationToken); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed this.CreateActivityProcessor(activity); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed }
/// <summary> /// Handles the completion of the specified <see cref="IEndProcessor"/> /// </summary> /// <param name="processor">The <see cref="IEndProcessor"/> that has produced the <see cref="V1WorkflowActivityCompletedIntegrationEvent"/></param> /// <param name="e">The <see cref="V1WorkflowActivityCompletedIntegrationEvent"/> to handle</param> /// <returns>A new awaitable <see cref="Task"/></returns> protected virtual async Task OnEndCompletedAsync(IEndProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e) { await this.Context.Workflow.SetOutputAsync(e.Output, this.CancellationToken); }
/// <summary> /// Handles the completion of a state /// </summary> /// <param name="processor">The <see cref="IStateProcessor"/> that has finished processing the state</param> /// <param name="e">The <see cref="V1WorkflowActivityCompletedIntegrationEvent"/> that describes the processed state's output</param> /// <returns>A new awaitable <see cref="Task"/></returns> protected virtual async Task OnStateCompletedAsync(IStateProcessor processor, V1WorkflowActivityCompletedIntegrationEvent e) { var metadata = new Dictionary <string, string>() { { V1WorkflowActivityMetadata.State, processor.State.Name } }; if (processor.State is SwitchStateDefinition switchState) { if (!processor.Activity.Metadata.TryGetValue(V1WorkflowActivityMetadata.Case, out var caseName)) { throw new InvalidOperationException($"Failed to retrieve the required switch state metadata with key '{V1WorkflowActivityMetadata.Case}'"); } if (!switchState.TryGetCase(caseName, out SwitchCaseDefinition switchCase)) { throw new InvalidOperationException($"Failed to find a case with name '{caseName}' in the state '{processor.State.Name}' of workflow '{this.Context.Workflow.Definition.Id}'"); } metadata.Add(V1WorkflowActivityMetadata.Case, caseName); var activity = null as V1WorkflowActivity; switch (switchCase.Type) { case ConditionType.End: activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.End, e.Output, metadata, null, this.CancellationToken); break; case ConditionType.Transition: activity = await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Transition, e.Output, metadata, null, this.CancellationToken); break; default: throw new NotSupportedException($"The specified condition type '{switchCase.Type}' is not supported in this context"); } #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed this.CreateActivityProcessor(activity); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } else { if (processor.State.Transition != null || !string.IsNullOrWhiteSpace(processor.State.TransitionToStateName)) { await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.Transition, e.Output !.ToObject() !, metadata, null, this.CancellationToken); } else if (processor.State.End != null || processor.State.IsEnd) { await this.Context.Workflow.CreateActivityAsync(V1WorkflowActivityType.End, e.Output !.ToObject() !, metadata, null, this.CancellationToken); } else { throw new InvalidOperationException($"The state '{processor.State.Name}' must declare a transition definition or an end definition for it is part of the main execution logic of the workflow '{this.Context.Workflow.Definition.Id}'"); } foreach (var activity in await this.Context.Workflow.GetOperativeActivitiesAsync(this.CancellationToken)) { #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed this.CreateActivityProcessor(activity); #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed } } }