Skip to content

Commit 99de260

Browse files
committed
Cancellable Step
User Extensions
1 parent 26475c1 commit 99de260

33 files changed

+465
-138
lines changed

WorkflowCore.sln

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -76,23 +76,25 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample09", "sr
7676
EndProject
7777
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample10", "src\samples\WorkflowCore.Sample10\WorkflowCore.Sample10.csproj", "{5E792455-4C4C-460F-849E-50A5DCED454D}"
7878
EndProject
79-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample11", "src\samples\WorkflowCore.Sample11\WorkflowCore.Sample11.csproj", "{58D0480F-D05D-4348-86D9-B0A7255700E6}"
79+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample11", "src\samples\WorkflowCore.Sample11\WorkflowCore.Sample11.csproj", "{58D0480F-D05D-4348-86D9-B0A7255700E6}"
8080
EndProject
81-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample12", "src\samples\WorkflowCore.Sample12\WorkflowCore.Sample12.csproj", "{BB776411-D279-419F-8697-5C6F52BCD5CD}"
81+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample12", "src\samples\WorkflowCore.Sample12\WorkflowCore.Sample12.csproj", "{BB776411-D279-419F-8697-5C6F52BCD5CD}"
8282
EndProject
83-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Tests.Sqlite", "test\WorkflowCore.Tests.Sqlite\WorkflowCore.Tests.Sqlite.csproj", "{F9F8F9CD-01D9-468B-856D-6A87F0762A01}"
83+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Tests.Sqlite", "test\WorkflowCore.Tests.Sqlite\WorkflowCore.Tests.Sqlite.csproj", "{F9F8F9CD-01D9-468B-856D-6A87F0762A01}"
8484
EndProject
85-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.LockProviders.SqlServer", "src\providers\WorkflowCore.LockProviders.SqlServer\WorkflowCore.LockProviders.SqlServer.csproj", "{AAE2E9F9-37EF-4AE1-A200-D37417C9040C}"
85+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.LockProviders.SqlServer", "src\providers\WorkflowCore.LockProviders.SqlServer\WorkflowCore.LockProviders.SqlServer.csproj", "{AAE2E9F9-37EF-4AE1-A200-D37417C9040C}"
8686
EndProject
87-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample13", "src\samples\WorkflowCore.Sample13\WorkflowCore.Sample13.csproj", "{77C49ACA-203E-428C-A4DB-114DFE454988}"
87+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample13", "src\samples\WorkflowCore.Sample13\WorkflowCore.Sample13.csproj", "{77C49ACA-203E-428C-A4DB-114DFE454988}"
8888
EndProject
89-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Providers.Azure", "src\providers\WorkflowCore.Providers.Azure\WorkflowCore.Providers.Azure.csproj", "{A2374B7C-4198-40B3-B8FE-FAC3DB3F2539}"
89+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Providers.Azure", "src\providers\WorkflowCore.Providers.Azure\WorkflowCore.Providers.Azure.csproj", "{A2374B7C-4198-40B3-B8FE-FAC3DB3F2539}"
9090
EndProject
9191
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNotes", "{38ECB00C-3F3B-4442-8408-ACE3B37FFAA8}"
9292
ProjectSection(SolutionItems) = preProject
9393
ReleaseNotes\1.2.8.md = ReleaseNotes\1.2.8.md
9494
EndProjectSection
9595
EndProject
96+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}"
97+
EndProject
9698
Global
9799
GlobalSection(SolutionConfigurationPlatforms) = preSolution
98100
Debug|Any CPU = Debug|Any CPU
@@ -239,6 +241,10 @@ Global
239241
{A2374B7C-4198-40B3-B8FE-FAC3DB3F2539}.Debug|Any CPU.Build.0 = Debug|Any CPU
240242
{A2374B7C-4198-40B3-B8FE-FAC3DB3F2539}.Release|Any CPU.ActiveCfg = Release|Any CPU
241243
{A2374B7C-4198-40B3-B8FE-FAC3DB3F2539}.Release|Any CPU.Build.0 = Release|Any CPU
244+
{6BC66637-B42A-4334-ADFB-DBEC9F29D293}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
245+
{6BC66637-B42A-4334-ADFB-DBEC9F29D293}.Debug|Any CPU.Build.0 = Debug|Any CPU
246+
{6BC66637-B42A-4334-ADFB-DBEC9F29D293}.Release|Any CPU.ActiveCfg = Release|Any CPU
247+
{6BC66637-B42A-4334-ADFB-DBEC9F29D293}.Release|Any CPU.Build.0 = Release|Any CPU
242248
EndGlobalSection
243249
GlobalSection(SolutionProperties) = preSolution
244250
HideSolutionNode = FALSE
@@ -282,5 +288,6 @@ Global
282288
{AAE2E9F9-37EF-4AE1-A200-D37417C9040C} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2}
283289
{77C49ACA-203E-428C-A4DB-114DFE454988} = {5080DB09-CBE8-4C45-9957-C3BB7651755E}
284290
{A2374B7C-4198-40B3-B8FE-FAC3DB3F2539} = {2EEE6ABD-EE9B-473F-AF2D-6DABB85D7BA2}
291+
{6BC66637-B42A-4334-ADFB-DBEC9F29D293} = {5080DB09-CBE8-4C45-9957-C3BB7651755E}
285292
EndGlobalSection
286293
EndGlobal

src/WorkflowCore/Interface/IContainerStepBuilder.cs

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ public interface IContainerStepBuilder<TData, TStepBody, TReturnStep>
88
where TStepBody : IStepBody
99
where TReturnStep : IStepBody
1010
{
11+
/// <summary>
12+
/// The block of steps to execute
13+
/// </summary>
14+
/// <param name="builder"></param>
15+
/// <returns></returns>
1116
IStepBuilder<TData, TReturnStep> Do(Action<IWorkflowBuilder<TData>> builder);
1217
}
1318
}

src/WorkflowCore/Interface/IStepBuilder.cs

Lines changed: 24 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -25,8 +25,8 @@ public interface IStepBuilder<TData, TStepBody>
2525
/// <summary>
2626
/// Specify the next step in the workflow
2727
/// </summary>
28-
/// <typeparam name="TStep"></typeparam>
29-
/// <param name="stepSetup"></param>
28+
/// <typeparam name="TStep">The type of the step to execute</typeparam>
29+
/// <param name="stepSetup">Configure additional parameters for this step</param>
3030
/// <returns></returns>
3131
IStepBuilder<TData, TStep> Then<TStep>(Action<IStepBuilder<TData, TStep>> stepSetup = null) where TStep : IStepBody;
3232

@@ -73,7 +73,7 @@ public interface IStepBuilder<TData, TStepBody>
7373
/// Map properties on the step to properties on the workflow data object before the step executes
7474
/// </summary>
7575
/// <typeparam name="TInput"></typeparam>
76-
/// <param name="stepProperty"></param>
76+
/// <param name="stepProperty">The property on the step</param>
7777
/// <param name="value"></param>
7878
/// <returns></returns>
7979
IStepBuilder<TData, TStepBody> Input<TInput>(Expression<Func<TStepBody, TInput>> stepProperty, Expression<Func<TData, IStepExecutionContext, TInput>> value);
@@ -88,21 +88,30 @@ public interface IStepBuilder<TData, TStepBody>
8888
IStepBuilder<TData, TStepBody> Output<TOutput>(Expression<Func<TData, TOutput>> dataProperty, Expression<Func<TStepBody, TOutput>> value);
8989

9090
/// <summary>
91-
/// Put the workflow to sleep until to specified event is published
91+
/// Wait here until to specified event is published
9292
/// </summary>
93-
/// <param name="eventName"></param>
94-
/// <param name="eventKey"></param>
95-
/// <param name="effectiveDate"></param>
93+
/// <param name="eventName">The name used to identify the kind of event to wait for</param>
94+
/// <param name="eventKey">A specific key value within the context of the event to wait for</param>
95+
/// <param name="effectiveDate">Listen for events as of this effective date</param>
9696
/// <returns></returns>
9797
IStepBuilder<TData, WaitFor> WaitFor(string eventName, Expression<Func<TData, string>> eventKey, Expression<Func<TData, DateTime>> effectiveDate = null);
9898

99+
/// <summary>
100+
/// Wait here until to specified event is published
101+
/// </summary>
102+
/// <param name="eventName">The name used to identify the kind of event to wait for</param>
103+
/// <param name="eventKey">A specific key value within the context of the event to wait for</param>
104+
/// <param name="effectiveDate">Listen for events as of this effective date</param>
105+
/// <returns></returns>
106+
IStepBuilder<TData, WaitFor> WaitFor(string eventName, Expression<Func<TData, IStepExecutionContext, string>> eventKey, Expression<Func<TData, DateTime>> effectiveDate = null);
107+
99108
IStepBuilder<TData, TStep> End<TStep>(string name) where TStep : IStepBody;
100109

101110
/// <summary>
102111
/// Configure the behavior when this step throws an unhandled exception
103112
/// </summary>
104-
/// <param name="behavior"></param>
105-
/// <param name="retryInterval"></param>
113+
/// <param name="behavior">What action to take when this step throws an unhandled exception</param>
114+
/// <param name="retryInterval">If the behavior is retry, how often</param>
106115
/// <returns></returns>
107116
IStepBuilder<TData, TStepBody> OnError(WorkflowErrorHandling behavior, TimeSpan? retryInterval = null);
108117

@@ -122,21 +131,21 @@ public interface IStepBuilder<TData, TStepBody>
122131
/// <summary>
123132
/// Execute a block of steps, once for each item in a collection in a parallel foreach
124133
/// </summary>
125-
/// <param name="collection"></param>
134+
/// <param name="collection">Resolves a collection for iterate over</param>
126135
/// <returns></returns>
127136
IContainerStepBuilder<TData, Foreach, Foreach> ForEach(Expression<Func<TData, IEnumerable>> collection);
128137

129138
/// <summary>
130139
/// Repeat a block of steps until a condition becomes true
131140
/// </summary>
132-
/// <param name="condition"></param>
141+
/// <param name="condition">Resolves a condition to break out of the while loop</param>
133142
/// <returns></returns>
134143
IContainerStepBuilder<TData, While, While> While(Expression<Func<TData, bool>> condition);
135144

136145
/// <summary>
137146
/// Execute a block of steps if a condition is true
138147
/// </summary>
139-
/// <param name="condition"></param>
148+
/// <param name="condition">Resolves a condition to evaluate</param>
140149
/// <returns></returns>
141150
IContainerStepBuilder<TData, If, If> If(Expression<Func<TData, bool>> condition);
142151

@@ -156,15 +165,15 @@ public interface IStepBuilder<TData, TStepBody>
156165
/// <summary>
157166
/// Schedule a block of steps to execute in parallel sometime in the future
158167
/// </summary>
159-
/// <param name="time"></param>
168+
/// <param name="time">The time span to wait before executing the block</param>
160169
/// <returns></returns>
161170
IContainerStepBuilder<TData, Schedule, TStepBody> Schedule(Expression<Func<TData, TimeSpan>> time);
162171

163172
/// <summary>
164173
/// Schedule a block of steps to execute in parallel sometime in the future at a recurring interval
165174
/// </summary>
166-
/// <param name="interval"></param>
167-
/// <param name="until"></param>
175+
/// <param name="interval">The time span to wait between recurring executions</param>
176+
/// <param name="until">Resolves a condition to stop the recurring task</param>
168177
/// <returns></returns>
169178
IContainerStepBuilder<TData, Recur, TStepBody> Recur(Expression<Func<TData, TimeSpan>> interval, Expression<Func<TData, bool>> until);
170179
}

src/WorkflowCore/Interface/IWorkflowHost.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,14 @@ namespace WorkflowCore.Interface
99
{
1010
public interface IWorkflowHost
1111
{
12+
/// <summary>
13+
/// Start the workflow host, this enable execution of workflows
14+
/// </summary>
1215
void Start();
16+
17+
/// <summary>
18+
/// Stop the workflow host
19+
/// </summary>
1320
void Stop();
1421
Task<string> StartWorkflow(string workflowId, object data = null);
1522
Task<string> StartWorkflow(string workflowId, int? version, object data = null);
@@ -20,8 +27,25 @@ public interface IWorkflowHost
2027
void RegisterWorkflow<TWorkflow>() where TWorkflow : IWorkflow, new();
2128
void RegisterWorkflow<TWorkflow, TData>() where TWorkflow : IWorkflow<TData>, new() where TData : new();
2229

30+
/// <summary>
31+
/// Suspend the execution of a given workflow until .ResumeWorkflow is called
32+
/// </summary>
33+
/// <param name="workflowId"></param>
34+
/// <returns></returns>
2335
Task<bool> SuspendWorkflow(string workflowId);
36+
37+
/// <summary>
38+
/// Resume a previously suspended workflow
39+
/// </summary>
40+
/// <param name="workflowId"></param>
41+
/// <returns></returns>
2442
Task<bool> ResumeWorkflow(string workflowId);
43+
44+
/// <summary>
45+
/// Permanently terminate the exeuction of a given workflow
46+
/// </summary>
47+
/// <param name="workflowId"></param>
48+
/// <returns></returns>
2549
Task<bool> TerminateWorkflow(string workflowId);
2650

2751
event StepErrorEventHandler OnStepError;

src/WorkflowCore/Models/WorkflowStep.cs

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -73,11 +73,7 @@ public enum ExecutionPipelineDirective { Next = 0, Defer = 1, EndWorkflow = 2 }
7373
public class WorkflowStep<TStepBody> : WorkflowStep
7474
where TStepBody : IStepBody
7575
{
76-
public override Type BodyType
77-
{
78-
get { return typeof(TStepBody); }
79-
}
80-
76+
public override Type BodyType => typeof(TStepBody);
8177
}
8278

8379

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq.Expressions;
4+
using System.Text;
5+
using WorkflowCore.Interface;
6+
using WorkflowCore.Models;
7+
8+
namespace WorkflowCore.Primitives
9+
{
10+
public class CancellableStep<TStepBody, TData> : WorkflowStep<TStepBody>
11+
where TStepBody : IStepBody
12+
{
13+
private readonly Expression<Func<TData, bool>> _cancelCondition;
14+
15+
public CancellableStep(Expression<Func<TData, bool>> cancelCondition)
16+
{
17+
_cancelCondition = cancelCondition;
18+
}
19+
20+
public override void AfterWorkflowIteration(WorkflowExecutorResult executorResult, WorkflowDefinition defintion, WorkflowInstance workflow, ExecutionPointer executionPointer)
21+
{
22+
base.AfterWorkflowIteration(executorResult, defintion, workflow, executionPointer);
23+
var func = _cancelCondition.Compile();
24+
if (func((TData) workflow.Data))
25+
{
26+
executionPointer.EndTime = DateTime.Now.ToUniversalTime();
27+
executionPointer.Active = false;
28+
}
29+
}
30+
}
31+
}

src/WorkflowCore/Services/SkipStepBuilder.cs renamed to src/WorkflowCore/Services/ReturnStepBuilder.cs

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010

1111
namespace WorkflowCore.Services
1212
{
13-
public class SkipStepBuilder<TData, TStepBody, TParentStep> : IContainerStepBuilder<TData, TStepBody, TParentStep>
13+
public class ReturnStepBuilder<TData, TStepBody, TParentStep> : IContainerStepBuilder<TData, TStepBody, TParentStep>
1414
where TStepBody : IStepBody
1515
where TParentStep : IStepBody
1616
{
17-
private IStepBuilder<TData, TParentStep> _referenceBuilder;
17+
private readonly IStepBuilder<TData, TParentStep> _referenceBuilder;
1818

1919
public IWorkflowBuilder<TData> WorkflowBuilder { get; private set; }
2020

2121
public WorkflowStep<TStepBody> Step { get; set; }
2222

23-
public SkipStepBuilder(IWorkflowBuilder<TData> workflowBuilder, WorkflowStep<TStepBody> step, IStepBuilder<TData, TParentStep> referenceBuilder)
23+
public ReturnStepBuilder(IWorkflowBuilder<TData> workflowBuilder, WorkflowStep<TStepBody> step, IStepBuilder<TData, TParentStep> referenceBuilder)
2424
{
2525
WorkflowBuilder = workflowBuilder;
2626
Step = step;

src/WorkflowCore/Services/StepBuilder.cs

Lines changed: 16 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -122,6 +122,18 @@ public IStepBuilder<TData, WaitFor> WaitFor(string eventName, Expression<Func<TD
122122
return stepBuilder;
123123
}
124124

125+
public IStepBuilder<TData, WaitFor> WaitFor(string eventName, Expression<Func<TData, IStepExecutionContext, string>> eventKey, Expression<Func<TData, DateTime>> effectiveDate = null)
126+
{
127+
var newStep = new WorkflowStep<WaitFor>();
128+
WorkflowBuilder.AddStep(newStep);
129+
var stepBuilder = new StepBuilder<TData, WaitFor>(WorkflowBuilder, newStep);
130+
stepBuilder.Input((step) => step.EventName, (data) => eventName);
131+
stepBuilder.Input((step) => step.EventKey, eventKey);
132+
stepBuilder.Input((step) => step.EffectiveDate, effectiveDate);
133+
Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });
134+
return stepBuilder;
135+
}
136+
125137
public IStepBuilder<TData, TStep> End<TStep>(string name) where TStep : IStepBody
126138
{
127139
var ancestor = IterateParents(Step.Id, name);
@@ -283,7 +295,7 @@ public IContainerStepBuilder<TData, When, OutcomeSwitch> When(Expression<Func<TD
283295
}
284296

285297
WorkflowBuilder.AddStep(newStep);
286-
var stepBuilder = new SkipStepBuilder<TData, When, OutcomeSwitch>(WorkflowBuilder, newStep, switchBuilder);
298+
var stepBuilder = new ReturnStepBuilder<TData, When, OutcomeSwitch>(WorkflowBuilder, newStep, switchBuilder);
287299

288300
switchBuilder.Step.Children.Add(newStep.Id);
289301

@@ -316,22 +328,22 @@ public IContainerStepBuilder<TData, Schedule, TStepBody> Schedule(Expression<Fun
316328
newStep.Inputs.Add(mapping);
317329

318330
WorkflowBuilder.AddStep(newStep);
319-
var stepBuilder = new SkipStepBuilder<TData, Schedule, TStepBody>(WorkflowBuilder, newStep, this);
331+
var stepBuilder = new ReturnStepBuilder<TData, Schedule, TStepBody>(WorkflowBuilder, newStep, this);
320332
Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });
321333

322334
return stepBuilder;
323335
}
324336

325337
public IContainerStepBuilder<TData, Recur, TStepBody> Recur(Expression<Func<TData, TimeSpan>> interval, Expression<Func<TData, bool>> until)
326338
{
327-
var newStep = new WorkflowStep<Recur>();
339+
var newStep = new CancellableStep<Recur, TData>(until);
328340
Expression<Func<Recur, TimeSpan>> intervalExpr = (x => x.Interval);
329341
Expression<Func<Recur, bool>> untilExpr = (x => x.StopCondition);
330342
newStep.Inputs.Add(new DataMapping() { Source = interval, Target = intervalExpr });
331343
newStep.Inputs.Add(new DataMapping() { Source = until, Target = untilExpr });
332344

333345
WorkflowBuilder.AddStep(newStep);
334-
var stepBuilder = new SkipStepBuilder<TData, Recur, TStepBody>(WorkflowBuilder, newStep, this);
346+
var stepBuilder = new ReturnStepBuilder<TData, Recur, TStepBody>(WorkflowBuilder, newStep, this);
335347
Step.Outcomes.Add(new StepOutcome() { NextStep = newStep.Id });
336348

337349
return stepBuilder;

src/WorkflowCore/WorkflowCore.csproj

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -18,9 +18,9 @@
1818
<GenerateAssemblyCompanyAttribute>false</GenerateAssemblyCompanyAttribute>
1919
<GenerateAssemblyProductAttribute>false</GenerateAssemblyProductAttribute>
2020
<Description>Workflow Core is a light weight workflow engine targeting .NET Standard.</Description>
21-
<Version>1.2.8</Version>
22-
<AssemblyVersion>1.2.8.0</AssemblyVersion>
23-
<FileVersion>1.2.8.0</FileVersion>
21+
<Version>1.2.9</Version>
22+
<AssemblyVersion>1.2.9.0</AssemblyVersion>
23+
<FileVersion>1.2.9.0</FileVersion>
2424
<PackageReleaseNotes></PackageReleaseNotes>
2525
</PropertyGroup>
2626

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Linq.Expressions;
4+
using System.Text;
5+
using WorkflowCore.Interface;
6+
using WorkflowCore.Primitives;
7+
using WorkflowCore.Users.Primitives;
8+
9+
namespace WorkflowCore.Users.Interface
10+
{
11+
public interface IUserTaskBuilder<TData> : IStepBuilder<TData, UserTask>
12+
{
13+
IUserTaskReturnBuilder<TData> WithOption(string value, string label);
14+
15+
}
16+
}

0 commit comments

Comments
 (0)