Incremental work on persistence
This commit is contained in:
parent
333e09744b
commit
5cb45c154d
|
|
@ -19,6 +19,14 @@ EndProjectSection
|
|||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flowsharp.Abstractions", "src\Flowsharp.Abstractions\Flowsharp.Abstractions.csproj", "{3130C574-557C-4DA2-933E-AED7A9EBE48A}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flowsharp.Persistence.Abstractions", "src\Flowsharp.Persistence.Abstractions\Flowsharp.Persistence.Abstractions.csproj", "{A5829896-89C5-4C4E-ADC7-C806C8077316}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flowsharp.Persistence.InMemory", "src\Flowsharp.Persistence.InMemory\Flowsharp.Persistence.InMemory.csproj", "{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flowsharp.Runtime", "src\Flowsharp.Runtime\Flowsharp.Runtime.csproj", "{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Flowsharp.Runtime.Abstractions", "src\Flowsharp.Runtime.Abstractions\Flowsharp.Runtime.Abstractions.csproj", "{792362BF-D26D-46E9-8970-E5668941AA75}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
|
|
@ -37,6 +45,22 @@ Global
|
|||
{3130C574-557C-4DA2-933E-AED7A9EBE48A}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3130C574-557C-4DA2-933E-AED7A9EBE48A}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3130C574-557C-4DA2-933E-AED7A9EBE48A}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{A5829896-89C5-4C4E-ADC7-C806C8077316}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{A5829896-89C5-4C4E-ADC7-C806C8077316}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{A5829896-89C5-4C4E-ADC7-C806C8077316}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{A5829896-89C5-4C4E-ADC7-C806C8077316}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{792362BF-D26D-46E9-8970-E5668941AA75}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{792362BF-D26D-46E9-8970-E5668941AA75}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{792362BF-D26D-46E9-8970-E5668941AA75}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{792362BF-D26D-46E9-8970-E5668941AA75}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
|
|
@ -48,5 +72,9 @@ Global
|
|||
{300EE2D5-54C5-46F2-AD03-BB43589EA074} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{48EDB976-3227-4DFD-BBB3-3BC9AEA19A88} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{3130C574-557C-4DA2-933E-AED7A9EBE48A} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{A5829896-89C5-4C4E-ADC7-C806C8077316} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{E93A1A6B-708F-4DCC-A399-1B8EC7682F92} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{3C1DCAA2-D77E-4F96-9899-DA8E0E0C757F} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
{792362BF-D26D-46E9-8970-E5668941AA75} = {DA71CDAA-8DD3-4D5F-9FBD-8E4B37A2D925}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@
|
|||
{
|
||||
public abstract class Activity : IActivity
|
||||
{
|
||||
public virtual string Name => GetType().Name;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2,5 +2,6 @@
|
|||
{
|
||||
public interface IActivity
|
||||
{
|
||||
string Name { get; }
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,16 +19,15 @@ namespace Flowsharp.Models
|
|||
CurrentScope = new WorkflowExecutionScope();
|
||||
Scopes = new Stack<WorkflowExecutionScope>(new[]{ CurrentScope });
|
||||
Arguments = new Variables();
|
||||
HaltedActivities = new List<IActivity>();
|
||||
BlockingActivities = new List<IActivity>();
|
||||
}
|
||||
|
||||
[JsonConverter(typeof(StringEnumConverter))]
|
||||
public WorkflowStatus Status { get; set; }
|
||||
public IList<IActivity> Activities { get; set; } = new List<IActivity>();
|
||||
public IList<Connection> Connections { get; set; } = new List<Connection>();
|
||||
public Stack<WorkflowExecutionScope> Scopes { get; set; }
|
||||
public WorkflowExecutionScope CurrentScope { get; set; }
|
||||
public Variables Arguments { get; set; }
|
||||
public IList<IActivity> HaltedActivities { get; set; }
|
||||
public IList<IActivity> BlockingActivities { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -63,9 +63,9 @@ namespace Flowsharp.Models
|
|||
public Task HaltAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var activity = CurrentActivity;
|
||||
if (!Workflow.HaltedActivities.Contains(activity))
|
||||
if (!Workflow.BlockingActivities.Contains(activity))
|
||||
{
|
||||
Workflow.HaltedActivities.Add(activity);
|
||||
Workflow.BlockingActivities.Add(activity);
|
||||
}
|
||||
|
||||
Workflow.Status = WorkflowStatus.Halted;
|
||||
|
|
|
|||
|
|
@ -61,7 +61,7 @@ namespace Flowsharp.Serialization.Tokenizers
|
|||
Status = (WorkflowStatus)Enum.Parse(typeof(WorkflowStatus), token["status"].Value<string>()),
|
||||
Activities = activityDictionary.Values.ToList(),
|
||||
Connections = DeserializeConnections(token, activityDictionary).ToList(),
|
||||
HaltedActivities = DeserializeHaltedActivities(token, activityDictionary).ToList(),
|
||||
BlockingActivities = DeserializeHaltedActivities(token, activityDictionary).ToList(),
|
||||
Scopes = new Stack<WorkflowExecutionScope>(scopeLookup.Values),
|
||||
CurrentScope = scopeLookup[currentScopeId]
|
||||
};
|
||||
|
|
@ -110,7 +110,7 @@ namespace Flowsharp.Serialization.Tokenizers
|
|||
{
|
||||
var haltedActivityModels = new JArray();
|
||||
|
||||
foreach (var activity in workflow.HaltedActivities)
|
||||
foreach (var activity in workflow.BlockingActivities)
|
||||
{
|
||||
var activityId = context.ActivityIdLookup[activity];
|
||||
haltedActivityModels.Add(activityId);
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace Flowsharp
|
|||
var isResuming = workflowExecutionContext.Workflow.Status == WorkflowStatus.Resuming;
|
||||
|
||||
if (startActivity != null)
|
||||
workflow.HaltedActivities.Remove(startActivity);
|
||||
workflow.BlockingActivities.Remove(startActivity);
|
||||
else
|
||||
startActivity = workflow.Activities.First();
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,26 @@
|
|||
using Flowsharp.Persistence.Primitives;
|
||||
using Flowsharp.Persistence.Specifications;
|
||||
|
||||
namespace Flowsharp.Persistence.Extensions
|
||||
{
|
||||
public static class SpecificationExtensions
|
||||
{
|
||||
public static ISpecification<T, TVisitor> And<T, TVisitor>(this ISpecification<T, TVisitor> left, ISpecification<T, TVisitor> right)
|
||||
where TVisitor : ISpecificationVisitor<TVisitor, T>
|
||||
{
|
||||
return new AndSpecification<T, TVisitor>(left, right);
|
||||
}
|
||||
|
||||
public static ISpecification<T, TVisitor> Or<T, TVisitor>(this ISpecification<T, TVisitor> left, ISpecification<T, TVisitor> right)
|
||||
where TVisitor : ISpecificationVisitor<TVisitor, T>
|
||||
{
|
||||
return new OrSpecification<T, TVisitor>(left, right);
|
||||
}
|
||||
|
||||
public static ISpecification<T, TVisitor> Not<T, TVisitor>(this ISpecification<T, TVisitor> specification)
|
||||
where TVisitor : ISpecificationVisitor<TVisitor, T>
|
||||
{
|
||||
return new NotSpecification<T, TVisitor>(specification);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
<RootNamespace>Flowsharp.Persistence</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flowsharp.Abstractions\Flowsharp.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Models;
|
||||
using Flowsharp.Persistence.Models;
|
||||
using Flowsharp.Persistence.Specifications;
|
||||
|
||||
namespace Flowsharp.Persistence
|
||||
{
|
||||
public interface IWorkflowDefinitionStore
|
||||
{
|
||||
Task<IEnumerable<WorkflowDefinition>> GetManyAsync(ISpecification<WorkflowDefinition, IWorkflowDefinitionSpecificationVisitor> specification, CancellationToken cancellationToken);
|
||||
Task AddAsync(WorkflowDefinition value, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,15 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Persistence.Models;
|
||||
using Flowsharp.Persistence.Specifications;
|
||||
|
||||
namespace Flowsharp.Persistence
|
||||
{
|
||||
public interface IWorkflowInstanceStore
|
||||
{
|
||||
Task<IEnumerable<WorkflowInstance>> GetManyAsync(ISpecification<WorkflowInstance, IWorkflowInstanceSpecificationVisitor> specification, CancellationToken cancellationToken);
|
||||
Task AddAsync(WorkflowInstance value, CancellationToken cancellationToken);
|
||||
Task UpdateAsync(WorkflowInstance value, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.Persistence.Models
|
||||
{
|
||||
public class WorkflowDefinition
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public Workflow Workflow { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.Persistence.Models
|
||||
{
|
||||
public class WorkflowInstance
|
||||
{
|
||||
public string Id { get; set; }
|
||||
public string WorkflowDefinitionId { get; set; }
|
||||
public Workflow Workflow { get; set; }
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using Flowsharp.Persistence.Specifications;
|
||||
|
||||
namespace Flowsharp.Persistence.Primitives
|
||||
{
|
||||
public class AndSpecification<T, TVisitor> : ISpecification<T, TVisitor> where TVisitor : ISpecificationVisitor<TVisitor, T>
|
||||
{
|
||||
public ISpecification<T, TVisitor> Left { get; }
|
||||
public ISpecification<T, TVisitor> Right { get; }
|
||||
|
||||
public AndSpecification(ISpecification<T, TVisitor> left, ISpecification<T, TVisitor> right)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
}
|
||||
|
||||
public void Accept(TVisitor visitor) => visitor.Visit(this);
|
||||
public bool IsSatisfiedBy(T obj) => Left.IsSatisfiedBy(obj) && Right.IsSatisfiedBy(obj);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,17 @@
|
|||
using Flowsharp.Persistence.Specifications;
|
||||
|
||||
namespace Flowsharp.Persistence.Primitives
|
||||
{
|
||||
public class NotSpecification<T, TVisitor> : ISpecification<T, TVisitor> where TVisitor : ISpecificationVisitor<TVisitor, T>
|
||||
{
|
||||
public ISpecification<T, TVisitor> Specification { get; }
|
||||
|
||||
public NotSpecification(ISpecification<T, TVisitor> specification)
|
||||
{
|
||||
Specification = specification;
|
||||
}
|
||||
|
||||
public void Accept(TVisitor visitor) => visitor.Visit(this);
|
||||
public bool IsSatisfiedBy(T obj) => !Specification.IsSatisfiedBy(obj);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,19 @@
|
|||
using Flowsharp.Persistence.Specifications;
|
||||
|
||||
namespace Flowsharp.Persistence.Primitives
|
||||
{
|
||||
public class OrSpecification<T, TVisitor> : ISpecification<T, TVisitor> where TVisitor : ISpecificationVisitor<TVisitor, T>
|
||||
{
|
||||
public ISpecification<T, TVisitor> Left { get; }
|
||||
public ISpecification<T, TVisitor> Right { get; }
|
||||
|
||||
public OrSpecification(ISpecification<T, TVisitor> left, ISpecification<T, TVisitor> right)
|
||||
{
|
||||
Left = left;
|
||||
Right = right;
|
||||
}
|
||||
|
||||
public void Accept(TVisitor visitor) => visitor.Visit(this);
|
||||
public bool IsSatisfiedBy(T obj) => Left.IsSatisfiedBy(obj) || Right.IsSatisfiedBy(obj);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
namespace Flowsharp.Persistence.Specifications
|
||||
{
|
||||
public interface ISpecification<in T>
|
||||
{
|
||||
bool IsSatisfiedBy(T item);
|
||||
}
|
||||
|
||||
public interface ISpecification<in T, in TVisitor> : ISpecification<T> where TVisitor : ISpecificationVisitor<TVisitor, T>
|
||||
{
|
||||
void Accept(TVisitor visitor);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
using Flowsharp.Persistence.Primitives;
|
||||
|
||||
namespace Flowsharp.Persistence.Specifications
|
||||
{
|
||||
public interface ISpecificationVisitor<TVisitor, T> where TVisitor : ISpecificationVisitor<TVisitor, T>
|
||||
{
|
||||
void Visit(AndSpecification<T, TVisitor> specification);
|
||||
void Visit(OrSpecification<T, TVisitor> specification);
|
||||
void Visit(NotSpecification<T, TVisitor> specification);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
using Flowsharp.Models;
|
||||
using Flowsharp.Persistence.Models;
|
||||
|
||||
namespace Flowsharp.Persistence.Specifications
|
||||
{
|
||||
public interface IWorkflowDefinitionSpecificationVisitor : ISpecificationVisitor<IWorkflowDefinitionSpecificationVisitor, WorkflowDefinition>
|
||||
{
|
||||
void Visit(WorkflowStartsWithActivity specification);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,9 @@
|
|||
using Flowsharp.Persistence.Models;
|
||||
|
||||
namespace Flowsharp.Persistence.Specifications
|
||||
{
|
||||
public interface IWorkflowInstanceSpecificationVisitor : ISpecificationVisitor<IWorkflowInstanceSpecificationVisitor, WorkflowInstance>
|
||||
{
|
||||
void Visit(WorkflowIsBlockedOnActivity specification);
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,30 @@
|
|||
using System.Linq;
|
||||
using Flowsharp.Persistence.Models;
|
||||
|
||||
namespace Flowsharp.Persistence.Specifications
|
||||
{
|
||||
public class WorkflowIsBlockedOnActivity: ISpecification<WorkflowInstance, IWorkflowInstanceSpecificationVisitor>
|
||||
{
|
||||
public WorkflowIsBlockedOnActivity(string activityName)
|
||||
{
|
||||
ActivityName = activityName;
|
||||
}
|
||||
|
||||
public string ActivityName { get; }
|
||||
|
||||
public bool IsSatisfiedBy(WorkflowInstance value)
|
||||
{
|
||||
var query =
|
||||
from activity in value.Workflow.BlockingActivities
|
||||
where activity.Name == ActivityName
|
||||
select activity;
|
||||
|
||||
return query.Any();
|
||||
}
|
||||
|
||||
public void Accept(IWorkflowInstanceSpecificationVisitor visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using System.Linq;
|
||||
using Flowsharp.Models;
|
||||
using Flowsharp.Persistence.Models;
|
||||
|
||||
namespace Flowsharp.Persistence.Specifications
|
||||
{
|
||||
public class WorkflowStartsWithActivity : ISpecification<WorkflowDefinition, IWorkflowDefinitionSpecificationVisitor>
|
||||
{
|
||||
public WorkflowStartsWithActivity(string activityName)
|
||||
{
|
||||
ActivityName = activityName;
|
||||
}
|
||||
|
||||
public string ActivityName { get; }
|
||||
|
||||
public bool IsSatisfiedBy(WorkflowDefinition value)
|
||||
{
|
||||
var query =
|
||||
from activity in value.Workflow.Activities
|
||||
where activity.Name == ActivityName
|
||||
where !value.Workflow.Connections.Select(x => x.Target.Activity).Contains(activity)
|
||||
select activity;
|
||||
|
||||
return query.Any();
|
||||
}
|
||||
|
||||
public void Accept(IWorkflowDefinitionSpecificationVisitor visitor)
|
||||
{
|
||||
visitor.Visit(this);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,11 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flowsharp.Persistence.Abstractions\Flowsharp.Persistence.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Persistence.Models;
|
||||
using Flowsharp.Persistence.Specifications;
|
||||
|
||||
namespace Flowsharp.Persistence.InMemory
|
||||
{
|
||||
public class InMemoryWorkflowDefinitionStore : IWorkflowDefinitionStore
|
||||
{
|
||||
public InMemoryWorkflowDefinitionStore()
|
||||
{
|
||||
WorkflowDefinitions = new List<WorkflowDefinition>();
|
||||
}
|
||||
|
||||
public IList<WorkflowDefinition> WorkflowDefinitions { get; }
|
||||
|
||||
public Task<IEnumerable<WorkflowDefinition>> GetManyAsync(ISpecification<WorkflowDefinition, IWorkflowDefinitionSpecificationVisitor> specification, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = WorkflowDefinitions.AsQueryable().Where(x => specification.IsSatisfiedBy(x));
|
||||
var matches = query.Distinct().ToList();
|
||||
return Task.FromResult<IEnumerable<WorkflowDefinition>>(matches);
|
||||
}
|
||||
|
||||
public Task AddAsync(WorkflowDefinition value, CancellationToken cancellationToken)
|
||||
{
|
||||
WorkflowDefinitions.Add(value);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Persistence.Models;
|
||||
using Flowsharp.Persistence.Specifications;
|
||||
|
||||
namespace Flowsharp.Persistence.InMemory
|
||||
{
|
||||
public class InMemoryWorkflowInstanceStore : IWorkflowInstanceStore
|
||||
{
|
||||
public InMemoryWorkflowInstanceStore()
|
||||
{
|
||||
WorkflowInstances = new List<WorkflowInstance>();
|
||||
}
|
||||
|
||||
public IList<WorkflowInstance> WorkflowInstances { get; }
|
||||
|
||||
public Task<IEnumerable<WorkflowInstance>> GetManyAsync(ISpecification<WorkflowInstance, IWorkflowInstanceSpecificationVisitor> specification, CancellationToken cancellationToken)
|
||||
{
|
||||
var query = WorkflowInstances.AsQueryable().Where(x => specification.IsSatisfiedBy(x));
|
||||
var matches = query.Distinct().ToList();
|
||||
return Task.FromResult<IEnumerable<WorkflowInstance>>(matches);
|
||||
}
|
||||
|
||||
public Task AddAsync(WorkflowInstance value, CancellationToken cancellationToken)
|
||||
{
|
||||
WorkflowInstances.Add(value);
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
|
||||
public Task UpdateAsync(WorkflowInstance value, CancellationToken cancellationToken)
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -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.Runtime.Abstractions/IWorkflowHost.cs
Normal file
17
src/Flowsharp.Runtime.Abstractions/IWorkflowHost.cs
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Models;
|
||||
|
||||
namespace Flowsharp.Runtime.Abstractions
|
||||
{
|
||||
/// <summary>
|
||||
/// Provides higher-level workflow functionality, such as triggering new and halted workflows based on received stimuli.
|
||||
/// </summary>
|
||||
public interface IWorkflowHost
|
||||
{
|
||||
/// <summary>
|
||||
/// Starts new workflows that start with the specified activity name and resumes halted workflows that are blocked on activities with the specified activity name.
|
||||
/// </summary>
|
||||
Task TriggerWorkflowAsync(string activityName, Variables arguments, CancellationToken cancellationToken);
|
||||
}
|
||||
}
|
||||
12
src/Flowsharp.Runtime/Flowsharp.Runtime.csproj
Normal file
12
src/Flowsharp.Runtime/Flowsharp.Runtime.csproj
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>netstandard2.0</TargetFramework>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flowsharp.Persistence.Abstractions\Flowsharp.Persistence.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\Flowsharp.Runtime.Abstractions\Flowsharp.Runtime.Abstractions.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
71
src/Flowsharp.Runtime/WorkflowHost.cs
Normal file
71
src/Flowsharp.Runtime/WorkflowHost.cs
Normal file
|
|
@ -0,0 +1,71 @@
|
|||
using System;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Models;
|
||||
using Flowsharp.Persistence;
|
||||
using Flowsharp.Persistence.Models;
|
||||
using Flowsharp.Persistence.Specifications;
|
||||
using Flowsharp.Runtime.Abstractions;
|
||||
|
||||
namespace Flowsharp.Runtime
|
||||
{
|
||||
public class WorkflowHost : IWorkflowHost
|
||||
{
|
||||
private readonly IWorkflowInvoker invoker;
|
||||
private readonly IWorkflowDefinitionStore workflowDefinitionStore;
|
||||
private readonly IWorkflowInstanceStore workflowInstanceStore;
|
||||
|
||||
public WorkflowHost(IWorkflowInvoker invoker, IWorkflowDefinitionStore workflowDefinitionStore, IWorkflowInstanceStore workflowInstanceStore)
|
||||
{
|
||||
this.invoker = invoker;
|
||||
this.workflowDefinitionStore = workflowDefinitionStore;
|
||||
this.workflowInstanceStore = workflowInstanceStore;
|
||||
}
|
||||
|
||||
public async Task TriggerWorkflowAsync(string activityName, Variables arguments, CancellationToken cancellationToken)
|
||||
{
|
||||
await StartNewWorkflowsAsync(activityName, arguments, cancellationToken);
|
||||
await ResumeExistingWorkflowsAsync(activityName, arguments, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task StartNewWorkflowsAsync(string activityName, Variables arguments, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflowDefinitions = await workflowDefinitionStore.GetManyAsync(new WorkflowStartsWithActivity(activityName), cancellationToken);
|
||||
|
||||
foreach (var workflowDefinition in workflowDefinitions)
|
||||
{
|
||||
var startActivities = workflowDefinition.Workflow.Activities.Where(x => x.Name == activityName && !workflowDefinition.Workflow.Connections.Select(c => c.Target.Activity).Contains(x));
|
||||
|
||||
foreach (var activity in startActivities)
|
||||
{
|
||||
var workflowContext = await invoker.InvokeAsync(workflowDefinition.Workflow, activity, arguments, cancellationToken);
|
||||
var workflowInstance = new WorkflowInstance
|
||||
{
|
||||
WorkflowDefinitionId = workflowDefinition.Id,
|
||||
Id = Guid.NewGuid().ToString(),
|
||||
Workflow = workflowContext.Workflow
|
||||
};
|
||||
|
||||
await workflowInstanceStore.AddAsync(workflowInstance, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async Task ResumeExistingWorkflowsAsync(string activityName, Variables arguments, CancellationToken cancellationToken)
|
||||
{
|
||||
var workflowInstances = await workflowInstanceStore.GetManyAsync(new WorkflowIsBlockedOnActivity(activityName), cancellationToken);
|
||||
|
||||
foreach (var workflowInstance in workflowInstances)
|
||||
{
|
||||
var blockingActivities = workflowInstance.Workflow.BlockingActivities.Where(x => x.Name == activityName).ToList();
|
||||
|
||||
foreach (var activity in blockingActivities)
|
||||
{
|
||||
await invoker.ResumeAsync(workflowInstance.Workflow, activity, arguments, cancellationToken);
|
||||
await workflowInstanceStore.UpdateAsync(workflowInstance, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -8,6 +8,10 @@
|
|||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Flowsharp.Core\Flowsharp.Core.csproj" />
|
||||
<ProjectReference Include="..\Flowsharp.Persistence.Abstractions\Flowsharp.Persistence.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\Flowsharp.Persistence.InMemory\Flowsharp.Persistence.InMemory.csproj" />
|
||||
<ProjectReference Include="..\Flowsharp.Runtime.Abstractions\Flowsharp.Runtime.Abstractions.csproj" />
|
||||
<ProjectReference Include="..\Flowsharp.Runtime\Flowsharp.Runtime.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
|
|
|
|||
|
|
@ -2,6 +2,10 @@
|
|||
using System.Threading.Tasks;
|
||||
using Flowsharp.Expressions;
|
||||
using Flowsharp.Handlers;
|
||||
using Flowsharp.Persistence;
|
||||
using Flowsharp.Persistence.InMemory;
|
||||
using Flowsharp.Runtime;
|
||||
using Flowsharp.Runtime.Abstractions;
|
||||
using Flowsharp.Samples.Console.Handlers;
|
||||
using Flowsharp.Samples.Console.Programs;
|
||||
using Flowsharp.Serialization;
|
||||
|
|
@ -23,12 +27,13 @@ namespace Flowsharp.Samples.Console
|
|||
.AddSingleton<IWorkflowInvoker, WorkflowInvoker>()
|
||||
.AddSingleton<IActivityInvoker, ActivityInvoker>()
|
||||
.AddSingleton<IWorkflowSerializer, WorkflowSerializer>()
|
||||
.AddSingleton<ITokenFormatter, JsonTokenFormatter>()
|
||||
//.AddSingleton<ITokenFormatter, JsonTokenFormatter>()
|
||||
.AddSingleton<ITokenFormatter, YamlTokenFormatter>()
|
||||
.AddSingleton<IWorkflowTokenizer, WorkflowTokenizer>()
|
||||
.AddSingleton<ITokenizerInvoker, TokenizerInvoker>()
|
||||
.AddSingleton<ITokenizer, DefaultTokenizer>()
|
||||
.AddSingleton<ITokenizer, ActivityTokenizer>()
|
||||
.AddSingleton<IWorkflowHost, WorkflowHost>()
|
||||
.AddLogging(logging => logging.AddConsole());
|
||||
|
||||
services
|
||||
|
|
@ -37,14 +42,20 @@ namespace Flowsharp.Samples.Console
|
|||
.AddSingleton<IActivityHandler, ReadLineHandler>()
|
||||
.AddSingleton<IActivityHandler, WriteLineHandler>();
|
||||
|
||||
services
|
||||
.AddSingleton<IWorkflowDefinitionStore, InMemoryWorkflowDefinitionStore>()
|
||||
.AddSingleton<IWorkflowInstanceStore, InMemoryWorkflowInstanceStore>();
|
||||
|
||||
services
|
||||
.AddSingleton<AdditionWorkflowProgram>()
|
||||
.AddSingleton<AdditionWorkflowProgramLongRunning>()
|
||||
.AddSingleton<FileBasedWorkflowProgramLongRunning>();
|
||||
.AddSingleton<FileBasedWorkflowProgramLongRunning>()
|
||||
.AddSingleton<WorkflowHostProgram>();
|
||||
|
||||
var serviceProvider = services.BuildServiceProvider();
|
||||
//var program = serviceProvider.GetRequiredService<AdditionWorkflowProgramLongRunning>();
|
||||
var program = serviceProvider.GetRequiredService<FileBasedWorkflowProgramLongRunning>();
|
||||
//var program = serviceProvider.GetRequiredService<FileBasedWorkflowProgramLongRunning>();
|
||||
var program = serviceProvider.GetRequiredService<WorkflowHostProgram>();
|
||||
|
||||
await program.RunAsync(CancellationToken.None);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ namespace Flowsharp.Samples.Console.Programs
|
|||
{
|
||||
var json = serializer.Serialize(workflow);
|
||||
workflow = serializer.Deserialize(json);
|
||||
var haltedActivity = (ReadLine)workflow.HaltedActivities.Single();
|
||||
var haltedActivity = (ReadLine)workflow.BlockingActivities.Single();
|
||||
var args = new Variables {{ haltedActivity.ArgumentName, System.Console.ReadLine() }};
|
||||
|
||||
return await workflowInvoker.ResumeAsync(workflow, haltedActivity, args, cancellationToken);
|
||||
|
|
|
|||
|
|
@ -40,7 +40,7 @@ namespace Flowsharp.Samples.Console.Programs
|
|||
|
||||
private async Task<WorkflowExecutionContext> ReadAndResumeAsync(Workflow workflow, CancellationToken cancellationToken)
|
||||
{
|
||||
var haltedActivity = (ReadLine)workflow.HaltedActivities.Single();
|
||||
var haltedActivity = (ReadLine)workflow.BlockingActivities.Single();
|
||||
var args = new Variables {{ haltedActivity.ArgumentName, System.Console.ReadLine() }};
|
||||
return await workflowInvoker.ResumeAsync(workflow, haltedActivity, args, cancellationToken);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,51 @@
|
|||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using Flowsharp.Models;
|
||||
using Flowsharp.Persistence;
|
||||
using Flowsharp.Persistence.Models;
|
||||
using Flowsharp.Runtime.Abstractions;
|
||||
using Flowsharp.Samples.Console.Activities;
|
||||
using Flowsharp.Serialization;
|
||||
|
||||
namespace Flowsharp.Samples.Console.Programs
|
||||
{
|
||||
public class WorkflowHostProgram
|
||||
{
|
||||
private readonly IWorkflowHost workflowHost;
|
||||
private readonly IWorkflowSerializer serializer;
|
||||
private readonly IWorkflowDefinitionStore workflowStore;
|
||||
|
||||
public WorkflowHostProgram(IWorkflowHost workflowHost, IWorkflowSerializer serializer, IWorkflowDefinitionStore workflowStore)
|
||||
{
|
||||
this.workflowHost = workflowHost;
|
||||
this.serializer = serializer;
|
||||
this.workflowStore = workflowStore;
|
||||
}
|
||||
|
||||
public async Task RunAsync(CancellationToken cancellationToken)
|
||||
{
|
||||
var assembly = typeof(FileBasedWorkflowProgramLongRunning).Assembly;
|
||||
var resource = assembly.GetManifestResourceStream("Flowsharp.Samples.Console.SampleWorkflow.yaml");
|
||||
var resourceReader = new StreamReader(resource);
|
||||
var data = await resourceReader.ReadToEndAsync();
|
||||
var workflow = serializer.Deserialize(data);
|
||||
var workflowDefinition = new WorkflowDefinition { Id = "1", Workflow = workflow};
|
||||
|
||||
await workflowStore.AddAsync(workflowDefinition, cancellationToken);
|
||||
await workflowHost.TriggerWorkflowAsync("WriteLine", Variables.Empty, cancellationToken);
|
||||
await ReadAndResumeAsync("x", cancellationToken);
|
||||
await ReadAndResumeAsync("y", cancellationToken);
|
||||
await ReadAndResumeAsync("tryAgain", cancellationToken);
|
||||
|
||||
System.Console.WriteLine(data);
|
||||
}
|
||||
|
||||
private async Task ReadAndResumeAsync(string argName, CancellationToken cancellationToken)
|
||||
{
|
||||
var args = new Variables { {argName, System.Console.ReadLine()} };
|
||||
await workflowHost.TriggerWorkflowAsync("ReadLine", args, cancellationToken);
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in a new issue