Basic functioning workflow engine
This commit is contained in:
parent
d659e38c6f
commit
1635618b01
8
.editorconfig
Normal file
8
.editorconfig
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
[*]
|
||||
charset=utf-8
|
||||
end_of_line=crlf
|
||||
trim_trailing_whitespace=false
|
||||
insert_final_newline=false
|
||||
indent_style=space
|
||||
indent_size=4
|
||||
|
||||
39
.gitignore
vendored
Normal file
39
.gitignore
vendored
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
#Ignore thumbnails created by Windows
|
||||
Thumbs.db
|
||||
|
||||
#Ignore files built by Visual Studio
|
||||
*.obj
|
||||
*.exe
|
||||
*.pdb
|
||||
*.user
|
||||
*.aps
|
||||
*.pch
|
||||
*.vspscc
|
||||
*_i.c
|
||||
*_p.c
|
||||
*.ncb
|
||||
*.suo
|
||||
*.tlb
|
||||
*.tlh
|
||||
*.bak
|
||||
*.cache
|
||||
*.ilk
|
||||
*.log
|
||||
[Bb]in
|
||||
[Dd]ebug*/
|
||||
*.lib
|
||||
*.sbr
|
||||
obj/
|
||||
[Rr]elease*/
|
||||
_ReSharper*/
|
||||
[Tt]est[Rr]esult*
|
||||
.vs/
|
||||
|
||||
#Nuget packages folder
|
||||
packages/
|
||||
|
||||
#Ignore git-related files
|
||||
*.orig
|
||||
|
||||
#Rider
|
||||
.idea
|
||||
58
Flowsharp.sln
Normal file
58
Flowsharp.sln
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio 15
|
||||
VisualStudioVersion = 15.0.28010.2036
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flowsharp.Abstractions", "src\Flowsharp.Abstractions\Flowsharp.Abstractions.csproj", "{300EE2D5-54C5-46F2-AD03-BB43589EA074}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Flowsharp.Core", "src\Flowsharp.Core\Flowsharp.Core.csproj", "{3B33AE9C-0465-4DA3-8C02-65E5766A7ED4}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flowsharp.Samples.Console", "src\Flowsharp.Samples.Console\Flowsharp.Samples.Console.csproj", "{48EDB976-3227-4DFD-BBB3-3BC9AEA19A88}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}"
|
||||
EndProject
|
||||
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "items", "items", "{7165BB9E-F22C-40D2-B7B1-CA3EFA2529A0}"
|
||||
ProjectSection(SolutionItems) = preProject
|
||||
.editorconfig = .editorconfig
|
||||
.gitignore = .gitignore
|
||||
README.md = README.md
|
||||
EndProjectSection
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flowsharp.Fluid", "src\Flowsharp.Fluid\Flowsharp.Fluid.csproj", "{B20D6AF5-91B1-4455-8541-22C5B31288D0}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{300EE2D5-54C5-46F2-AD03-BB43589EA074}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{300EE2D5-54C5-46F2-AD03-BB43589EA074}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{300EE2D5-54C5-46F2-AD03-BB43589EA074}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{300EE2D5-54C5-46F2-AD03-BB43589EA074}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3B33AE9C-0465-4DA3-8C02-65E5766A7ED4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3B33AE9C-0465-4DA3-8C02-65E5766A7ED4}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3B33AE9C-0465-4DA3-8C02-65E5766A7ED4}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3B33AE9C-0465-4DA3-8C02-65E5766A7ED4}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{48EDB976-3227-4DFD-BBB3-3BC9AEA19A88}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{48EDB976-3227-4DFD-BBB3-3BC9AEA19A88}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{48EDB976-3227-4DFD-BBB3-3BC9AEA19A88}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{48EDB976-3227-4DFD-BBB3-3BC9AEA19A88}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{B20D6AF5-91B1-4455-8541-22C5B31288D0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{B20D6AF5-91B1-4455-8541-22C5B31288D0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{B20D6AF5-91B1-4455-8541-22C5B31288D0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{B20D6AF5-91B1-4455-8541-22C5B31288D0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {8B0975FD-7050-48B0-88C5-48C33378E158}
|
||||
EndGlobalSection
|
||||
GlobalSection(NestedProjects) = preSolution
|
||||
{300EE2D5-54C5-46F2-AD03-BB43589EA074} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{3B33AE9C-0465-4DA3-8C02-65E5766A7ED4} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{48EDB976-3227-4DFD-BBB3-3BC9AEA19A88} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{B20D6AF5-91B1-4455-8541-22C5B31288D0} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
135
src/Flowsharp.Abstractions/Activities/Activity.cs
Normal file
135
src/Flowsharp.Abstractions/Activities/Activity.cs
Normal file
|
|
@ -0,0 +1,135 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.ActivityResults;
|
||||
using Flowsharp.Models;
|
||||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Flowsharp.Activities
|
||||
{
|
||||
public abstract class Activity : IActivity
|
||||
{
|
||||
public virtual string Name => GetType().Name;
|
||||
|
||||
public Task ProvideMetadataAsync(ActivityMetadataContext context, CancellationToken cancellationToken)
|
||||
{
|
||||
ProvideMetadata(context);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task<bool> CanExecuteAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(true);
|
||||
}
|
||||
|
||||
public virtual Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(Execute(workflowContext, activityContext));
|
||||
}
|
||||
|
||||
public virtual Task<ActivityExecutionResult> ResumeAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(Resume(workflowContext, activityContext));
|
||||
}
|
||||
|
||||
public virtual IEnumerable<Outcome> GetOutcomes(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext)
|
||||
{
|
||||
return Enumerable.Empty<Outcome>();
|
||||
}
|
||||
|
||||
public virtual Task OnActivityExecutedAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken)
|
||||
{
|
||||
OnActivityExecuted(workflowContext, activityContext);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task OnActivityExecutingAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken)
|
||||
{
|
||||
OnActivityExecuting(workflowContext, activityContext);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task ReceiveInputAsync(WorkflowExecutionContext workflowContext, IDictionary<string, object> input, CancellationToken cancellationToken)
|
||||
{
|
||||
ReceiveInput(workflowContext, input);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task WorkflowResumedAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
|
||||
{
|
||||
WorkflowResumed(workflowContext);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task WorkflowResumingAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
|
||||
{
|
||||
WorkflowResuming(workflowContext);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task WorkflowStartedAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
|
||||
{
|
||||
WorkflowStarted(workflowContext);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public virtual Task WorkflowStartingAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
|
||||
{
|
||||
WorkflowStarting(workflowContext);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual void ProvideMetadata(ActivityMetadataContext context)
|
||||
{
|
||||
}
|
||||
|
||||
protected virtual ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext)
|
||||
{
|
||||
return Noop();
|
||||
}
|
||||
|
||||
protected virtual ActivityExecutionResult Resume(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext)
|
||||
{
|
||||
return Noop();
|
||||
}
|
||||
|
||||
protected virtual void OnActivityExecuted(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) {}
|
||||
protected virtual void OnActivityExecuting(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext) {}
|
||||
protected virtual void ReceiveInput(WorkflowExecutionContext workflowContext, IDictionary<string, object> input) {}
|
||||
protected virtual void WorkflowResumed(WorkflowExecutionContext workflowContext) {}
|
||||
protected virtual void WorkflowResuming(WorkflowExecutionContext workflowContext) {}
|
||||
protected virtual void WorkflowStarted(WorkflowExecutionContext workflowContext) {}
|
||||
protected virtual void WorkflowStarting(WorkflowExecutionContext workflowContext) {}
|
||||
|
||||
protected IEnumerable<Outcome> Outcomes(params LocalizedString[] names)
|
||||
{
|
||||
return names.Select(x => new Outcome(x));
|
||||
}
|
||||
|
||||
protected IEnumerable<Outcome> Outcomes(IEnumerable<LocalizedString> names)
|
||||
{
|
||||
return names.Select(x => new Outcome(x));
|
||||
}
|
||||
|
||||
protected ActivityExecutionResult Outcomes(params string[] names)
|
||||
{
|
||||
return Outcomes((IEnumerable<string>)names);
|
||||
}
|
||||
|
||||
protected ActivityExecutionResult Outcomes(IEnumerable<string> names)
|
||||
{
|
||||
return new OutcomeResult(names);
|
||||
}
|
||||
|
||||
protected ActivityExecutionResult Halt()
|
||||
{
|
||||
return new HaltResult();
|
||||
}
|
||||
|
||||
protected ActivityExecutionResult Noop()
|
||||
{
|
||||
return new NoopResult();
|
||||
}
|
||||
}
|
||||
}
|
||||
83
src/Flowsharp.Abstractions/Activities/IActivity.cs
Normal file
83
src/Flowsharp.Abstractions/Activities/IActivity.cs
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.ActivityResults;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.Activities
|
||||
{
|
||||
public interface IActivity
|
||||
{
|
||||
/// <summary>
|
||||
/// The system name of the activity.
|
||||
/// </summary>
|
||||
/// <remarks>The Name is used to identify a given activity type and must be unique.</remarks>
|
||||
string Name { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Provides metadata about the specified activity.
|
||||
/// </summary>
|
||||
Task ProvideMetadataAsync(ActivityMetadataContext context, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of possible outcomes when the activity is executed.
|
||||
/// </summary>
|
||||
IEnumerable<Outcome> GetOutcomes(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext);
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value of whether the specified activity can execute.
|
||||
/// </summary>
|
||||
Task<bool> CanExecuteAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified activity.
|
||||
/// </summary>
|
||||
Task<ActivityExecutionResult> ExecuteAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Resumes the specified activity.
|
||||
/// </summary>
|
||||
Task<ActivityExecutionResult> ResumeAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Executes before a workflow starts or resumes, giving activities an opportunity to read and store any values of interest.
|
||||
/// </summary>
|
||||
Task ReceiveInputAsync(WorkflowExecutionContext workflowContext, IDictionary<string, object> input, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a workflow is about to start.
|
||||
/// </summary>
|
||||
Task WorkflowStartingAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a workflow has started.
|
||||
/// </summary>
|
||||
Task WorkflowStartedAsync(WorkflowExecutionContext context, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a workflow is about to be resumed.
|
||||
/// </summary>
|
||||
Task WorkflowResumingAsync(WorkflowExecutionContext context, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a workflow is resumed.
|
||||
/// </summary>
|
||||
Task WorkflowResumedAsync(WorkflowExecutionContext context, CancellationToken cancellationToken);
|
||||
|
||||
/// <summary>
|
||||
/// Executes when an activity is about to be executed.
|
||||
/// </summary>
|
||||
/// <param name="workflowContext">The workflow execution context.</param>
|
||||
/// <param name="activityContext">The activity context containing the activity that is the subject of the event.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
Task OnActivityExecutingAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken = default(CancellationToken));
|
||||
|
||||
/// <summary>
|
||||
/// Called on each activity when an activity has been executed.
|
||||
/// </summary>
|
||||
/// <param name="workflowContext">The workflow execution context.</param>
|
||||
/// <param name="activityContext">The activity context containing the activity that is the subject of the event.</param>
|
||||
/// <param name="cancellationToken">The cancellation token.</param>
|
||||
Task OnActivityExecutedAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,49 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Activities;
|
||||
using Flowsharp.Descriptors;
|
||||
using Flowsharp.Services;
|
||||
|
||||
namespace Flowsharp.ActivityProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides activities based on <see cref="IActivity"/> implementations that have been registered with the service container.
|
||||
/// </summary>
|
||||
public class TypedActivityProvider : IActivityProvider
|
||||
{
|
||||
private readonly Func<IEnumerable<IActivity>> _activitiesFactory;
|
||||
|
||||
public TypedActivityProvider(Func<IEnumerable<IActivity>> activitiesFactory)
|
||||
{
|
||||
_activitiesFactory = activitiesFactory;
|
||||
}
|
||||
|
||||
public Task<IEnumerable<ActivityDescriptor>> GetActivityDescriptorsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.FromResult(_activitiesFactory().Select(ToDescriptor));
|
||||
}
|
||||
|
||||
private ActivityDescriptor ToDescriptor(IActivity activity)
|
||||
{
|
||||
return new ActivityDescriptor
|
||||
{
|
||||
Name = activity.Name,
|
||||
GetMetadataAsync = activity.ProvideMetadataAsync,
|
||||
CanExecuteAsync = activity.CanExecuteAsync,
|
||||
GetOutcomes = activity.GetOutcomes,
|
||||
ExecuteActivityAsync = activity.ExecuteAsync,
|
||||
ResumeActivityAsync = activity.ResumeAsync,
|
||||
ReceiveInputAsync = activity.ReceiveInputAsync,
|
||||
WorkflowResumedAsync = activity.WorkflowResumedAsync,
|
||||
WorkflowResumingAsync = activity.WorkflowResumingAsync,
|
||||
WorkflowStartedAsync = activity.WorkflowStartedAsync,
|
||||
WorkflowStartingAsync = activity.WorkflowStartingAsync,
|
||||
OnActivityExecutedAsync = activity.OnActivityExecutedAsync,
|
||||
OnActivityExecutingAsync = activity.OnActivityExecutingAsync
|
||||
};
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
using System;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.ActivityResults
|
||||
{
|
||||
public abstract class ActivityExecutionResult : IActivityExecutionResult
|
||||
{
|
||||
public virtual Task ExecuteAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
|
||||
{
|
||||
Execute(workflowContext);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
protected virtual void Execute(WorkflowExecutionContext workflowContext)
|
||||
{
|
||||
throw new NotImplementedException("You must either implement ExecuteAsync or Execute");
|
||||
}
|
||||
}
|
||||
}
|
||||
31
src/Flowsharp.Abstractions/ActivityResults/HaltResult.cs
Normal file
31
src/Flowsharp.Abstractions/ActivityResults/HaltResult.cs
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.ActivityResults
|
||||
{
|
||||
/// <summary>
|
||||
/// Halts workflow execution.
|
||||
/// </summary>
|
||||
public class HaltResult : ActivityExecutionResult
|
||||
{
|
||||
public override async Task ExecuteAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken)
|
||||
{
|
||||
var currentActivity = workflowContext.CurrentExecutingActivity;
|
||||
|
||||
if (workflowContext.IsFirstPass)
|
||||
{
|
||||
// Resume immediately when this is the first pass.
|
||||
var result = await currentActivity.ActivityDescriptor.ResumeActivityAsync(workflowContext, currentActivity, cancellationToken);
|
||||
workflowContext.IsFirstPass = false;
|
||||
|
||||
await result.ExecuteAsync(workflowContext, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Block on this activity.
|
||||
workflowContext.BlockingActivities.Add(currentActivity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.ActivityResults
|
||||
{
|
||||
public interface IActivityExecutionResult
|
||||
{
|
||||
Task ExecuteAsync(WorkflowExecutionContext workflowContext, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
15
src/Flowsharp.Abstractions/ActivityResults/NoopResult.cs
Normal file
15
src/Flowsharp.Abstractions/ActivityResults/NoopResult.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.ActivityResults
|
||||
{
|
||||
/// <summary>
|
||||
/// A result that does nothing.
|
||||
/// </summary>
|
||||
public class NoopResult : ActivityExecutionResult
|
||||
{
|
||||
protected override void Execute(WorkflowExecutionContext workflowContext)
|
||||
{
|
||||
// Noop.
|
||||
}
|
||||
}
|
||||
}
|
||||
37
src/Flowsharp.Abstractions/ActivityResults/OutcomeResult.cs
Normal file
37
src/Flowsharp.Abstractions/ActivityResults/OutcomeResult.cs
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.ActivityResults
|
||||
{
|
||||
/// <summary>
|
||||
/// An activity execution result that sets the next outcomes to execute.
|
||||
/// </summary>
|
||||
public class OutcomeResult : ActivityExecutionResult
|
||||
{
|
||||
private readonly IEnumerable<string> outcomeNames;
|
||||
|
||||
public OutcomeResult(IEnumerable<string> names)
|
||||
{
|
||||
outcomeNames = names.ToList();
|
||||
}
|
||||
|
||||
protected override void Execute(WorkflowExecutionContext workflowContext)
|
||||
{
|
||||
var workflowType = workflowContext.WorkflowType;
|
||||
var currentActivity = workflowContext.CurrentExecutingActivity;
|
||||
|
||||
foreach (var outcome in outcomeNames)
|
||||
{
|
||||
// Look for next activity in the graph.
|
||||
var transition = workflowType.Transitions.FirstOrDefault(x => x.From.ActivityId == currentActivity.ActivityType.Id && x.From.OutcomeName == outcome);
|
||||
|
||||
if (transition != null)
|
||||
{
|
||||
var destinationActivity = workflowContext.Activities.Values.Single(x => x.ActivityType.Id == transition.To.ActivityId);
|
||||
workflowContext.PushScheduledActivity(destinationActivity);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
78
src/Flowsharp.Abstractions/Descriptors/ActivityDescriptor.cs
Normal file
78
src/Flowsharp.Abstractions/Descriptors/ActivityDescriptor.cs
Normal file
|
|
@ -0,0 +1,78 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.ActivityResults;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.Descriptors
|
||||
{
|
||||
public class ActivityDescriptor
|
||||
{
|
||||
public string Name { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Provides metadata about the specified activity.
|
||||
/// </summary>
|
||||
public Func<ActivityMetadataContext, CancellationToken, Task> GetMetadataAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a list of possible outcomes when the activity is executed.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, ActivityExecutionContext, IEnumerable<Outcome>> GetOutcomes { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Returns a value of whether the specified activity can execute.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, ActivityExecutionContext, CancellationToken, Task<bool>> CanExecuteAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes the specified activity.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, ActivityExecutionContext, CancellationToken, Task<ActivityExecutionResult>> ExecuteActivityAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Resumes the specified activity.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, ActivityExecutionContext, CancellationToken, Task<ActivityExecutionResult>> ResumeActivityAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes before a workflow starts or resumes, giving activities an opportunity to read and store any values of interest.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, IDictionary<string, object>, CancellationToken, Task> ReceiveInputAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a workflow is about to start.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, CancellationToken, Task> WorkflowStartingAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a workflow has started.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, CancellationToken, Task> WorkflowStartedAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a workflow is about to be resumed.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, CancellationToken, Task> WorkflowResumingAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes when a workflow is resumed.
|
||||
/// </summary>
|
||||
public Func<WorkflowExecutionContext, CancellationToken, Task> WorkflowResumedAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Executes when an activity is about to be executed.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity for which the event is invoked. This is not necessarily the activity that is about to be executed.</param>
|
||||
/// <param name="activityContext">The activity context containing the activity that is the subject of the event.</param>
|
||||
public Func<WorkflowExecutionContext, ActivityExecutionContext, CancellationToken, Task> OnActivityExecutingAsync { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Called on each activity when an activity has been executed.
|
||||
/// </summary>
|
||||
/// <param name="activity">The activity for which the event is invoked. This is not necessarily the activity that is about to be executed.</param>
|
||||
/// <param name="activityContext">The activity context containing the activity that is the subject of the event.</param>
|
||||
public Func<WorkflowExecutionContext, ActivityExecutionContext, CancellationToken, Task> OnActivityExecutedAsync { get; set; }
|
||||
}
|
||||
}
|
||||
17
src/Flowsharp.Abstractions/Extensions/ExceptionExtensions.cs
Normal file
17
src/Flowsharp.Abstractions/Extensions/ExceptionExtensions.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Security;
|
||||
|
||||
namespace Flowsharp.Extensions
|
||||
{
|
||||
public static class ExceptionExtensions
|
||||
{
|
||||
public static bool IsFatal(this Exception ex)
|
||||
{
|
||||
return
|
||||
ex is OutOfMemoryException ||
|
||||
ex is SecurityException ||
|
||||
ex is SEHException;
|
||||
}
|
||||
}
|
||||
}
|
||||
57
src/Flowsharp.Abstractions/Extensions/InvokeExtensions.cs
Normal file
57
src/Flowsharp.Abstractions/Extensions/InvokeExtensions.cs
Normal file
|
|
@ -0,0 +1,57 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Threading.Tasks;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flowsharp.Extensions
|
||||
{
|
||||
public static class InvokeExtensions
|
||||
{
|
||||
/// <summary>
|
||||
/// Safely invoke methods by catching non fatal exceptions and logging them.
|
||||
/// </summary>
|
||||
public static void Invoke<TEvents>(this IEnumerable<TEvents> events, Action<TEvents> dispatch, ILogger logger)
|
||||
{
|
||||
foreach (var sink in events)
|
||||
{
|
||||
try
|
||||
{
|
||||
dispatch(sink);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex, logger, typeof(TEvents).Name, sink.GetType().FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Safely invoke methods by catching non fatal exceptions and logging them.
|
||||
/// </summary>
|
||||
public static async Task InvokeAsync<TEvents>(this IEnumerable<TEvents> events, Func<TEvents, Task> dispatch, ILogger logger)
|
||||
{
|
||||
foreach (var sink in events)
|
||||
{
|
||||
try
|
||||
{
|
||||
await dispatch(sink);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
HandleException(ex, logger, typeof(TEvents).Name, sink.GetType().FullName);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public static void HandleException(Exception ex, ILogger logger, string sourceType, string method)
|
||||
{
|
||||
if (ex.IsFatal())
|
||||
throw ex;
|
||||
|
||||
logger.LogError(ex, "{Type} thrown from {Method} by {Exception}",
|
||||
sourceType,
|
||||
method,
|
||||
ex.GetType().Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/Flowsharp.Abstractions/Flowsharp.Abstractions.csproj
Normal file
14
src/Flowsharp.Abstractions/Flowsharp.Abstractions.csproj
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<RootNamespace>Flowsharp</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Microsoft.Extensions.Localization.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="2.1.1" />
|
||||
<PackageReference Include="Newtonsoft.Json" Version="11.0.2" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
30
src/Flowsharp.Abstractions/Json/LocalizedStringConverter.cs
Normal file
30
src/Flowsharp.Abstractions/Json/LocalizedStringConverter.cs
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
using System;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Flowsharp.Json
|
||||
{
|
||||
/// <summary>
|
||||
/// Serializes the <see cref="LocalizedString"/> to a simple string using the translated text.
|
||||
/// </summary>
|
||||
public class LocalizedStringConverter : JsonConverter
|
||||
{
|
||||
public override bool CanConvert(Type objectType)
|
||||
{
|
||||
return objectType == typeof(LocalizedString);
|
||||
}
|
||||
|
||||
public override bool CanRead => false;
|
||||
|
||||
public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer)
|
||||
{
|
||||
var localizedString = (LocalizedString)value;
|
||||
writer.WriteValue(localizedString.Value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using Flowsharp.Descriptors;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Flowsharp.Models
|
||||
{
|
||||
public class ActivityExecutionContext
|
||||
{
|
||||
public ActivityExecutionContext(ActivityType activityType, ActivityDescriptor activityDescriptor)
|
||||
{
|
||||
ActivityType = activityType;
|
||||
ActivityDescriptor = activityDescriptor;
|
||||
State = new JObject(activityType.State);
|
||||
}
|
||||
|
||||
public ActivityType ActivityType { get; }
|
||||
public ActivityDescriptor ActivityDescriptor { get; }
|
||||
public JObject State { get; set; }
|
||||
}
|
||||
}
|
||||
10
src/Flowsharp.Abstractions/Models/ActivityMetadata.cs
Normal file
10
src/Flowsharp.Abstractions/Models/ActivityMetadata.cs
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
using Microsoft.Extensions.Localization;
|
||||
|
||||
namespace Flowsharp.Abstractions.Models
|
||||
{
|
||||
public class ActivityMetadata
|
||||
{
|
||||
public LocalizedString DisplayName { get; set; }
|
||||
public LocalizedString Category { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Flowsharp.Abstractions.Models;
|
||||
|
||||
namespace Flowsharp.Models
|
||||
{
|
||||
public class ActivityMetadataContext
|
||||
{
|
||||
public ActivityMetadata Metadata { get; set; }
|
||||
}
|
||||
}
|
||||
18
src/Flowsharp.Abstractions/Models/ActivityType.cs
Normal file
18
src/Flowsharp.Abstractions/Models/ActivityType.cs
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace Flowsharp.Models
|
||||
{
|
||||
public class ActivityType
|
||||
{
|
||||
public ActivityType(string id, string name, JObject state = null)
|
||||
{
|
||||
Id = id;
|
||||
Name = name;
|
||||
State = state ?? new JObject();
|
||||
}
|
||||
|
||||
public string Id { get; }
|
||||
public string Name { get; }
|
||||
public JObject State { get; }
|
||||
}
|
||||
}
|
||||
6
src/Flowsharp.Abstractions/Models/DestinationEndpoint.cs
Normal file
6
src/Flowsharp.Abstractions/Models/DestinationEndpoint.cs
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
namespace Flowsharp.Models
|
||||
{
|
||||
public class DestinationEndpoint : Endpoint
|
||||
{
|
||||
}
|
||||
}
|
||||
7
src/Flowsharp.Abstractions/Models/Endpoint.cs
Normal file
7
src/Flowsharp.Abstractions/Models/Endpoint.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace Flowsharp.Models
|
||||
{
|
||||
public class Endpoint
|
||||
{
|
||||
public string ActivityId { get; set; }
|
||||
}
|
||||
}
|
||||
24
src/Flowsharp.Abstractions/Models/Outcome.cs
Normal file
24
src/Flowsharp.Abstractions/Models/Outcome.cs
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
using Flowsharp.Json;
|
||||
using Microsoft.Extensions.Localization;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Flowsharp.Models
|
||||
{
|
||||
public class Outcome
|
||||
{
|
||||
public Outcome(LocalizedString displayName) : this(displayName.Name, displayName)
|
||||
{
|
||||
}
|
||||
|
||||
public Outcome(string name, LocalizedString displayName)
|
||||
{
|
||||
Name = name;
|
||||
DisplayName = displayName;
|
||||
}
|
||||
|
||||
public string Name { get; }
|
||||
|
||||
[JsonConverter(typeof(LocalizedStringConverter))]
|
||||
public LocalizedString DisplayName { get; }
|
||||
}
|
||||
}
|
||||
7
src/Flowsharp.Abstractions/Models/SourceEndpoint.cs
Normal file
7
src/Flowsharp.Abstractions/Models/SourceEndpoint.cs
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
namespace Flowsharp.Models
|
||||
{
|
||||
public class SourceEndpoint : Endpoint
|
||||
{
|
||||
public string OutcomeName { get; set; }
|
||||
}
|
||||
}
|
||||
8
src/Flowsharp.Abstractions/Models/Transition.cs
Normal file
8
src/Flowsharp.Abstractions/Models/Transition.cs
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
namespace Flowsharp.Models
|
||||
{
|
||||
public class Transition
|
||||
{
|
||||
public SourceEndpoint From { get; set; }
|
||||
public DestinationEndpoint To { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,46 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Flowsharp.Descriptors;
|
||||
|
||||
namespace Flowsharp.Models
|
||||
{
|
||||
public class WorkflowExecutionContext
|
||||
{
|
||||
public WorkflowExecutionContext(IDictionary<string, ActivityDescriptor> activityDescriptorDictionary, WorkflowType workflowType, WorkflowStatus status)
|
||||
{
|
||||
WorkflowType = workflowType;
|
||||
Activities = workflowType.Activities.ToDictionary(x => x.Id, x => new ActivityExecutionContext(x, activityDescriptorDictionary[x.Name]));
|
||||
BlockingActivities = new List<ActivityExecutionContext>();
|
||||
Status = status;
|
||||
IsFirstPass = true;
|
||||
|
||||
scheduledActivities = new Stack<ActivityExecutionContext>();
|
||||
}
|
||||
|
||||
private readonly Stack<ActivityExecutionContext> scheduledActivities;
|
||||
|
||||
public WorkflowType WorkflowType { get; }
|
||||
public IDictionary<string, ActivityExecutionContext> Activities { get; }
|
||||
public ICollection<ActivityExecutionContext> BlockingActivities { get; }
|
||||
public WorkflowStatus Status { get; set; }
|
||||
public bool HasScheduledActivities => scheduledActivities.Any();
|
||||
public bool IsFirstPass { get; set; }
|
||||
public ActivityExecutionContext CurrentExecutingActivity { get; private set; }
|
||||
|
||||
public void PushScheduledActivity(ActivityExecutionContext activityExecutionContext)
|
||||
{
|
||||
scheduledActivities.Push(activityExecutionContext);
|
||||
}
|
||||
|
||||
public ActivityExecutionContext PopScheduledActivity()
|
||||
{
|
||||
return CurrentExecutingActivity = scheduledActivities.Pop();
|
||||
}
|
||||
|
||||
public void Fault(Exception exception, ActivityExecutionContext activity)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
}
|
||||
}
|
||||
14
src/Flowsharp.Abstractions/Models/WorkflowStatus.cs
Normal file
14
src/Flowsharp.Abstractions/Models/WorkflowStatus.cs
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
namespace Flowsharp.Models
|
||||
{
|
||||
public enum WorkflowStatus
|
||||
{
|
||||
Idle,
|
||||
Starting,
|
||||
Resuming,
|
||||
Executing,
|
||||
Halted,
|
||||
Finished,
|
||||
Faulted,
|
||||
Aborted
|
||||
}
|
||||
}
|
||||
11
src/Flowsharp.Abstractions/Models/WorkflowType.cs
Normal file
11
src/Flowsharp.Abstractions/Models/WorkflowType.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace Flowsharp.Models
|
||||
{
|
||||
public class WorkflowType
|
||||
{
|
||||
public string Name { get; set; }
|
||||
public ICollection<ActivityType> Activities { get; set; }
|
||||
public ICollection<Transition> Transitions { get; set; }
|
||||
}
|
||||
}
|
||||
12
src/Flowsharp.Abstractions/Services/IActivityLibrary.cs
Normal file
12
src/Flowsharp.Abstractions/Services/IActivityLibrary.cs
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Descriptors;
|
||||
|
||||
namespace Flowsharp.Services
|
||||
{
|
||||
public interface IActivityLibrary
|
||||
{
|
||||
Task<IEnumerable<ActivityDescriptor>> GetActivityDescriptorsAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
15
src/Flowsharp.Abstractions/Services/IActivityProvider.cs
Normal file
15
src/Flowsharp.Abstractions/Services/IActivityProvider.cs
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Descriptors;
|
||||
|
||||
namespace Flowsharp.Services
|
||||
{
|
||||
/// <summary>
|
||||
/// Implementors provide available activity descriptors.
|
||||
/// </summary>
|
||||
public interface IActivityProvider
|
||||
{
|
||||
Task<IEnumerable<ActivityDescriptor>> GetActivityDescriptorsAsync(CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
11
src/Flowsharp.Abstractions/Services/IWorkflowInvoker.cs
Normal file
11
src/Flowsharp.Abstractions/Services/IWorkflowInvoker.cs
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.Services
|
||||
{
|
||||
public interface IWorkflowInvoker
|
||||
{
|
||||
Task InvokeAsync(WorkflowExecutionContext workflowContext, string startActivityId, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
19
src/Flowsharp.Core/Activities/WriteLine.cs
Normal file
19
src/Flowsharp.Core/Activities/WriteLine.cs
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
using System;
|
||||
using Flowsharp.Activities;
|
||||
using Flowsharp.ActivityResults;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.ActivityProviders
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides activities based on <see cref="IActivity"/> implementations that have been registered with the service container.
|
||||
/// </summary>
|
||||
public class WriteLine : Activity
|
||||
{
|
||||
protected override ActivityExecutionResult Execute(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext)
|
||||
{
|
||||
Console.WriteLine("Hello World!");
|
||||
return Outcomes("Done");
|
||||
}
|
||||
}
|
||||
}
|
||||
17
src/Flowsharp.Core/Flowsharp.Core.csproj
Normal file
17
src/Flowsharp.Core/Flowsharp.Core.csproj
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<RootNamespace>Flowsharp</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flowsharp.Abstractions\Flowsharp.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="ActivityResults" />
|
||||
<Folder Include="Models\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
35
src/Flowsharp.Core/Services/ActivityLibrary.cs
Normal file
35
src/Flowsharp.Core/Services/ActivityLibrary.cs
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Descriptors;
|
||||
|
||||
namespace Flowsharp.Services
|
||||
{
|
||||
public class ActivityLibrary : IActivityLibrary
|
||||
{
|
||||
private readonly IEnumerable<IActivityProvider> providers;
|
||||
|
||||
public ActivityLibrary(IEnumerable<IActivityProvider> providers)
|
||||
{
|
||||
this.providers = providers;
|
||||
}
|
||||
|
||||
public async Task<IEnumerable<ActivityDescriptor>> GetActivityDescriptorsAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var tasks = providers.Select(x => x.GetActivityDescriptorsAsync(cancellationToken)).ToList();
|
||||
var results = await Task.WhenAll(tasks);
|
||||
|
||||
return results.SelectMany(x => x);
|
||||
}
|
||||
}
|
||||
|
||||
public static class ActivityLibraryExtensions
|
||||
{
|
||||
public static async Task<IDictionary<string, ActivityDescriptor>> GetActivityDescriptorDictionaryAsync(this IActivityLibrary activityLibrary, CancellationToken cancellationToken)
|
||||
{
|
||||
var activityDescriptors = await activityLibrary.GetActivityDescriptorsAsync(cancellationToken);
|
||||
return activityDescriptors.ToDictionary(x => x.Name);
|
||||
}
|
||||
}
|
||||
}
|
||||
99
src/Flowsharp.Core/Services/WorkflowInvoker.cs
Normal file
99
src/Flowsharp.Core/Services/WorkflowInvoker.cs
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.ActivityResults;
|
||||
using Flowsharp.Extensions;
|
||||
using Flowsharp.Models;
|
||||
using Microsoft.Extensions.Logging;
|
||||
|
||||
namespace Flowsharp.Services
|
||||
{
|
||||
public class WorkflowInvoker : IWorkflowInvoker
|
||||
{
|
||||
public WorkflowInvoker(ILogger<WorkflowInvoker> logger)
|
||||
{
|
||||
this.logger = logger;
|
||||
}
|
||||
|
||||
private readonly ILogger logger;
|
||||
|
||||
public async Task InvokeAsync(WorkflowExecutionContext workflowContext, string startActivityId, CancellationToken cancellationToken)
|
||||
{
|
||||
var isResuming = workflowContext.Status == WorkflowStatus.Resuming;
|
||||
var startActivity = workflowContext.Activities[startActivityId];
|
||||
|
||||
workflowContext.Status = WorkflowStatus.Executing;
|
||||
workflowContext.PushScheduledActivity(startActivity);
|
||||
|
||||
while (workflowContext.HasScheduledActivities)
|
||||
{
|
||||
var currentActivity = workflowContext.PopScheduledActivity();
|
||||
|
||||
if (!await ExecuteActivityAsync(workflowContext, currentActivity, isResuming, cancellationToken))
|
||||
break;
|
||||
|
||||
workflowContext.IsFirstPass = false;
|
||||
isResuming = false;
|
||||
}
|
||||
|
||||
workflowContext.Status = workflowContext.BlockingActivities.Any() ? WorkflowStatus.Halted : WorkflowStatus.Finished;
|
||||
}
|
||||
|
||||
private async Task<bool> ExecuteActivityAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, bool isResuming, CancellationToken cancellationToken)
|
||||
{
|
||||
try
|
||||
{
|
||||
await InvokeActivitiesAsync(workflowContext, x => x.ActivityDescriptor.OnActivityExecutingAsync(workflowContext, activityContext, cancellationToken));
|
||||
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
workflowContext.Status = WorkflowStatus.Aborted;
|
||||
return false;
|
||||
}
|
||||
|
||||
var result = await ExecuteOrResumeActivityAsync(workflowContext, activityContext, isResuming, cancellationToken);
|
||||
await InvokeActivitiesAsync(workflowContext, x => x.ActivityDescriptor.OnActivityExecutedAsync(workflowContext, activityContext, cancellationToken));
|
||||
await result.ExecuteAsync(workflowContext, cancellationToken);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
FaultWorkflow(workflowContext, activityContext, ex);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private void FaultWorkflow(WorkflowExecutionContext workflowContext, ActivityExecutionContext activityContext, Exception ex)
|
||||
{
|
||||
logger.LogError(
|
||||
ex,
|
||||
"An unhandled error occurred while executing an activity. Workflow ID: '{WorkflowTypeId}'. Activity: '{ActivityId}', '{ActivityName}'. Putting the workflow in the faulted state.",
|
||||
workflowContext.WorkflowType.Name,
|
||||
activityContext.ActivityType.Id,
|
||||
activityContext.ActivityType.Name
|
||||
);
|
||||
workflowContext.Fault(ex, activityContext);
|
||||
}
|
||||
|
||||
private async Task<ActivityExecutionResult> ExecuteOrResumeActivityAsync(WorkflowExecutionContext workflowContext, ActivityExecutionContext activity, bool isResuming, CancellationToken cancellationToken)
|
||||
{
|
||||
if (!isResuming)
|
||||
{
|
||||
// Execute the current activity.
|
||||
return await activity.ActivityDescriptor.ExecuteActivityAsync(workflowContext, activity, cancellationToken);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Resume the current activity.
|
||||
return await activity.ActivityDescriptor.ResumeActivityAsync(workflowContext, activity, cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private async Task InvokeActivitiesAsync(WorkflowExecutionContext workflowContext, Func<ActivityExecutionContext, Task> action)
|
||||
{
|
||||
await workflowContext.Activities.Values.InvokeAsync(action, logger);
|
||||
}
|
||||
}
|
||||
}
|
||||
11
src/Flowsharp.Fluid/Flowsharp.Fluid.csproj
Normal file
11
src/Flowsharp.Fluid/Flowsharp.Fluid.csproj
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flowsharp.Abstractions\Flowsharp.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
17
src/Flowsharp.Fluid/WorkflowBuilder.cs
Normal file
17
src/Flowsharp.Fluid/WorkflowBuilder.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.Fluid
|
||||
{
|
||||
public class WorkflowBuilder
|
||||
{
|
||||
public WorkflowBuilder()
|
||||
{
|
||||
activityTypes = new List<ActivityType>();
|
||||
}
|
||||
|
||||
private IList<ActivityType> activityTypes;
|
||||
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,14 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>netcoreapp2.1</TargetFramework>
|
||||
<LangVersion>7.1</LangVersion>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flowsharp.Abstractions\Flowsharp.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\Flowsharp.Core\Flowsharp.Core.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
34
src/Flowsharp.Samples.Console/Program.cs
Normal file
34
src/Flowsharp.Samples.Console/Program.cs
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Activities;
|
||||
using Flowsharp.ActivityProviders;
|
||||
using Flowsharp.Models;
|
||||
using Flowsharp.Services;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using Microsoft.Extensions.Logging.Abstractions;
|
||||
|
||||
namespace Flowsharp.Samples.Console
|
||||
{
|
||||
class Program
|
||||
{
|
||||
static async Task Main()
|
||||
{
|
||||
var workflowType = new WorkflowType
|
||||
{
|
||||
Activities = new[]
|
||||
{
|
||||
new ActivityType("1", "WriteLine")
|
||||
},
|
||||
Transitions = new Transition[0]
|
||||
};
|
||||
|
||||
var typedActivityProvider = new TypedActivityProvider(() => new IActivity[] { new WriteLine() } );
|
||||
var activityLibrary = new ActivityLibrary(new[]{ typedActivityProvider });
|
||||
var dictionary = await activityLibrary.GetActivityDescriptorDictionaryAsync(CancellationToken.None);
|
||||
var workflowContext = new WorkflowExecutionContext(dictionary, workflowType, WorkflowStatus.Idle);
|
||||
var invoker = new WorkflowInvoker(new Logger<WorkflowInvoker>(new NullLoggerFactory()));
|
||||
|
||||
await invoker.InvokeAsync(workflowContext, "1", CancellationToken.None);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue