Skip to content

Commit 2cd5978

Browse files
committed
tests, release notes
1 parent 2d726d4 commit 2cd5978

File tree

13 files changed

+298
-8
lines changed

13 files changed

+298
-8
lines changed

ReleaseNotes/1.6.0.md

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
# Workflow Core 1.6.0
2+
3+
4+
* Added Saga transaction feature
5+
* Added `.CompensateWith` feature
6+
7+
8+
#### Specifying compensation steps for each component of a saga transaction
9+
10+
```c#
11+
builder
12+
.StartWith<SayHello>()
13+
.CompensateWith<UndoHello>()
14+
.Saga(saga => saga
15+
.StartWith<DoTask1>()
16+
.CompensateWith<UndoTask1>()
17+
.Then<DoTask2>()
18+
.CompensateWith<UndoTask2>()
19+
.Then<DoTask3>()
20+
.CompensateWith<UndoTask3>()
21+
)
22+
.Then<SayGoodbye>();
23+
```
24+
25+
#### Retrying a failed transaction
26+
27+
```c#
28+
builder
29+
.StartWith<SayHello>()
30+
.CompensateWith<UndoHello>()
31+
.Saga(saga => saga
32+
.StartWith<DoTask1>()
33+
.CompensateWith<UndoTask1>()
34+
.Then<DoTask2>()
35+
.CompensateWith<UndoTask2>()
36+
.Then<DoTask3>()
37+
.CompensateWith<UndoTask3>()
38+
)
39+
.OnError(Models.WorkflowErrorHandling.Retry, TimeSpan.FromSeconds(5))
40+
.Then<SayGoodbye>();
41+
```
42+
43+
#### Compensating the entire transaction
44+
45+
```c#
46+
builder
47+
.StartWith<SayHello>()
48+
.CompensateWith<UndoHello>()
49+
.Saga(saga => saga
50+
.StartWith<DoTask1>()
51+
.Then<DoTask2>()
52+
.Then<DoTask3>()
53+
)
54+
.CompensateWithSequence(comp => comp
55+
.StartWith<UndoTask1>()
56+
.Then<UndoTask2>()
57+
.Then<UndoTask3>()
58+
)
59+
.Then<SayGoodbye>();
60+
```
61+
62+
#### Passing parameters
63+
64+
Parameters can be passed to a compensation step as follows
65+
66+
```c#
67+
builder
68+
.StartWith<SayHello>()
69+
.CompensateWith<CustomMessage>(compensate =>
70+
{
71+
compensate.Input(step => step.Message, data => "undoing...");
72+
})
73+
```
74+
75+
76+
### Expressing a saga in JSON
77+
78+
A saga transaction can be expressed in JSON, by using the `WorkflowCore.Primitives.Sequence` step and setting the `Saga` parameter to `true`.
79+
80+
The compensation steps can be defined by specifying the `CompensateWith` parameter.
81+
82+
```json
83+
{
84+
"Id": "Saga-Sample",
85+
"Version": 1,
86+
"DataType": "MyApp.MyDataClass, MyApp",
87+
"Steps": [
88+
{
89+
"Id": "Hello",
90+
"StepType": "MyApp.HelloWorld, MyApp",
91+
"NextStepId": "MySaga"
92+
},
93+
{
94+
"Id": "MySaga",
95+
"StepType": "WorkflowCore.Primitives.Sequence, WorkflowCore",
96+
"NextStepId": "Bye",
97+
"Saga": true,
98+
"Do": [
99+
[
100+
{
101+
"Id": "do1",
102+
"StepType": "MyApp.Task1, MyApp",
103+
"NextStepId": "do2",
104+
"CompensateWith": [
105+
{
106+
"Id": "undo1",
107+
"StepType": "MyApp.UndoTask1, MyApp"
108+
}
109+
]
110+
},
111+
{
112+
"Id": "do2",
113+
"StepType": "MyApp.Task2, MyApp",
114+
"CompensateWith": [
115+
{
116+
"Id": "undo2-1",
117+
"NextStepId": "undo2-2",
118+
"StepType": "MyApp.UndoTask2, MyApp"
119+
},
120+
{
121+
"Id": "undo2-2",
122+
"StepType": "MyApp.DoSomethingElse, MyApp"
123+
}
124+
]
125+
}
126+
]
127+
]
128+
},
129+
{
130+
"Id": "Bye",
131+
"StepType": "MyApp.GoodbyeWorld, MyApp"
132+
}
133+
]
134+
}
135+
```

WorkflowCore.sln

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "ReleaseNotes", "ReleaseNote
9090
ReleaseNotes\1.3.2.md = ReleaseNotes\1.3.2.md
9191
ReleaseNotes\1.3.3.md = ReleaseNotes\1.3.3.md
9292
ReleaseNotes\1.4.0.md = ReleaseNotes\1.4.0.md
93+
ReleaseNotes\1.6.0.md = ReleaseNotes\1.6.0.md
9394
EndProjectSection
9495
EndProject
9596
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample14", "src\samples\WorkflowCore.Sample14\WorkflowCore.Sample14.csproj", "{6BC66637-B42A-4334-ADFB-DBEC9F29D293}"
@@ -106,7 +107,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample16", "sr
106107
EndProject
107108
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ScratchPad", "test\ScratchPad\ScratchPad.csproj", "{6396453F-4D0E-4CD4-BC89-87E8970F2A80}"
108109
EndProject
109-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WorkflowCore.Sample17", "src\samples\WorkflowCore.Sample17\WorkflowCore.Sample17.csproj", "{42F475BC-95F4-42E1-8CCD-7B9C27487E33}"
110+
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WorkflowCore.Sample17", "src\samples\WorkflowCore.Sample17\WorkflowCore.Sample17.csproj", "{42F475BC-95F4-42E1-8CCD-7B9C27487E33}"
110111
EndProject
111112
Global
112113
GlobalSection(SolutionConfigurationPlatforms) = preSolution

src/providers/WorkflowCore.Persistence.EntityFramework/Services/EntityFrameworkPersistenceProvider.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -250,6 +250,7 @@ public async Task<IEnumerable<EventSubscription>> GetSubcriptions(string eventNa
250250
_mutex.WaitOne();
251251
try
252252
{
253+
asOf = asOf.ToUniversalTime();
253254
var raw = await Set<PersistedSubscription>()
254255
.Where(x => x.EventName == eventName && x.EventKey == eventKey && x.SubscribeAsOf <= asOf)
255256
.ToListAsync();
@@ -306,6 +307,7 @@ public async Task<IEnumerable<string>> GetRunnableEvents(DateTime asAt)
306307
_mutex.WaitOne();
307308
try
308309
{
310+
asAt = asAt.ToUniversalTime();
309311
var raw = await Set<PersistedEvent>()
310312
.Where(x => !x.IsProcessed)
311313
.Where(x => x.EventTime <= now)

test/WorkflowCore.IntegrationTests/Scenarios/EventScenario.cs

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,8 @@ public class EventScenario : WorkflowTest<EventScenario.EventWorkflow, EventScen
1414
{
1515
public class MyDataClass
1616
{
17-
public string StrValue { get; set; }
17+
public string StrValue1 { get; set; }
18+
public string StrValue2 { get; set; }
1819
}
1920

2021
public class EventWorkflow : IWorkflow<MyDataClass>
@@ -25,8 +26,10 @@ public void Build(IWorkflowBuilder<MyDataClass> builder)
2526
{
2627
builder
2728
.StartWith(context => ExecutionResult.Next())
28-
.WaitFor("MyEvent", data => data.StrValue)
29-
.Output(data => data.StrValue, step => step.EventData);
29+
.WaitFor("MyEvent", data => data.StrValue1, data => DateTime.Now)
30+
.Output(data => data.StrValue1, step => step.EventData)
31+
.WaitFor("MyEvent2", data => data.StrValue2)
32+
.Output(data => data.StrValue2, step => step.EventData);
3033
}
3134
}
3235

@@ -39,14 +42,18 @@ public EventScenario()
3942
public void Scenario()
4043
{
4144
var eventKey = Guid.NewGuid().ToString();
42-
var workflowId = StartWorkflow(new MyDataClass() { StrValue = eventKey });
45+
var workflowId = StartWorkflow(new MyDataClass() { StrValue1 = eventKey, StrValue2 = eventKey });
4346
WaitForEventSubscription("MyEvent", eventKey, TimeSpan.FromSeconds(30));
44-
Host.PublishEvent("MyEvent", eventKey, "Pass");
47+
Host.PublishEvent("MyEvent", eventKey, "Pass1");
48+
WaitForEventSubscription("MyEvent2", eventKey, TimeSpan.FromSeconds(30));
49+
Host.PublishEvent("MyEvent2", eventKey, "Pass2");
50+
4551
WaitForWorkflowToComplete(workflowId, TimeSpan.FromSeconds(30));
4652

4753
GetStatus(workflowId).Should().Be(WorkflowStatus.Complete);
4854
UnhandledStepErrors.Count.Should().Be(0);
49-
GetData(workflowId).StrValue.Should().Be("Pass");
55+
GetData(workflowId).StrValue1.Should().Be("Pass1");
56+
GetData(workflowId).StrValue2.Should().Be("Pass2");
5057
}
5158
}
5259
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using WorkflowCore.IntegrationTests.Scenarios;
6+
using Xunit;
7+
8+
namespace WorkflowCore.Tests.MongoDB.Scenarios
9+
{
10+
[Collection("Mongo collection")]
11+
public class MongoCompensationScenario : CompensationScenario
12+
{
13+
protected override void ConfigureServices(IServiceCollection services)
14+
{
15+
services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests"));
16+
}
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using WorkflowCore.IntegrationTests.Scenarios;
6+
using Xunit;
7+
8+
namespace WorkflowCore.Tests.MongoDB.Scenarios
9+
{
10+
[Collection("Mongo collection")]
11+
public class MongoRetrySagaScenario : RetrySagaScenario
12+
{
13+
protected override void ConfigureServices(IServiceCollection services)
14+
{
15+
services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests"));
16+
}
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using WorkflowCore.IntegrationTests.Scenarios;
6+
using Xunit;
7+
8+
namespace WorkflowCore.Tests.MongoDB.Scenarios
9+
{
10+
[Collection("Mongo collection")]
11+
public class MongoSagaScenario : SagaScenario
12+
{
13+
protected override void ConfigureServices(IServiceCollection services)
14+
{
15+
services.AddWorkflow(x => x.UseMongoDB(MongoDockerSetup.ConnectionString, "integration-tests"));
16+
}
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using WorkflowCore.IntegrationTests.Scenarios;
6+
using Xunit;
7+
8+
namespace WorkflowCore.Tests.PostgreSQL.Scenarios
9+
{
10+
[Collection("Postgres collection")]
11+
public class PostgresRetrySagaScenario : RetrySagaScenario
12+
{
13+
protected override void ConfigureServices(IServiceCollection services)
14+
{
15+
services.AddWorkflow(x => x.UsePostgreSQL(PostgresDockerSetup.ScenarioConnectionString, true, true));
16+
}
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using WorkflowCore.IntegrationTests.Scenarios;
6+
using Xunit;
7+
8+
namespace WorkflowCore.Tests.PostgreSQL.Scenarios
9+
{
10+
[Collection("Postgres collection")]
11+
public class PostgresSagaScenario : SagaScenario
12+
{
13+
protected override void ConfigureServices(IServiceCollection services)
14+
{
15+
services.AddWorkflow(x => x.UsePostgreSQL(PostgresDockerSetup.ScenarioConnectionString, true, true));
16+
}
17+
}
18+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using System;
2+
using System.Collections.Generic;
3+
using System.Text;
4+
using Microsoft.Extensions.DependencyInjection;
5+
using WorkflowCore.IntegrationTests.Scenarios;
6+
using Xunit;
7+
8+
namespace WorkflowCore.Tests.SqlServer.Scenarios
9+
{
10+
[Collection("SqlServer collection")]
11+
public class SqlServerCompenstationScenario : CompensationScenario
12+
{
13+
protected override void ConfigureServices(IServiceCollection services)
14+
{
15+
services.AddWorkflow(x => x.UseSqlServer(SqlDockerSetup.ScenarioConnectionString, true, true));
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)