Skip to content

Commit b335771

Browse files
authored
Handle compensatedBy in diagram generation (#161)
handle compensatedBy diagram generation
1 parent a89acd0 commit b335771

File tree

3 files changed

+151
-18
lines changed

3 files changed

+151
-18
lines changed

src/lib/diagram/mermaidState.ts

Lines changed: 51 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -23,13 +23,22 @@ export class MermaidState {
2323
type?: string;
2424
transition?: string | Specification.Transition;
2525
end?: boolean | Specification.End;
26+
compensatedBy?: string;
2627
onErrors?: Specification.Error[];
28+
usedForCompensation?: boolean;
2729
},
2830
private isFirstState: boolean = false
2931
) {}
3032

3133
sourceCode() {
32-
return this.definitions() + '\n' + this.transitions();
34+
const stateDefinition = this.definitions();
35+
const stateTransitions = this.transitions();
36+
37+
const stateDescription = stateTransitions.reduce((p, c) => {
38+
return p + '\n' + c;
39+
}, stateDefinition);
40+
41+
return stateDescription;
3342
}
3443

3544
private definitions(): string {
@@ -41,19 +50,18 @@ export class MermaidState {
4150
);
4251
}
4352

44-
private transitions(): string {
53+
private transitions(): string[] {
4554
const transitions: string[] = [];
4655

4756
transitions.push(...this.startTransition());
4857
transitions.push(...this.dataConditionsTransitions());
4958
transitions.push(...this.eventConditionsTransition());
5059
transitions.push(...this.errorTransitions());
5160
transitions.push(...this.naturalTransition(this.stateKeyDiagram(this.state.name), this.state.transition));
61+
transitions.push(...this.compensatedByTransition());
5262
transitions.push(...this.endTransition());
5363

54-
return transitions.reduce((p, c) => {
55-
return p + '\n' + c;
56-
});
64+
return transitions;
5765
}
5866

5967
private stateKeyDiagram(name: string | undefined) {
@@ -178,34 +186,61 @@ export class MermaidState {
178186
return transitions;
179187
}
180188

189+
private compensatedByTransition() {
190+
const transitions: string[] = [];
191+
192+
if (this.state.compensatedBy) {
193+
transitions.push(...this.naturalTransition(this.state.name, this.state.compensatedBy, 'compensated by'));
194+
}
195+
return transitions;
196+
}
197+
181198
private definitionDetails() {
199+
let definition: string | undefined;
200+
182201
switch (this.state.type) {
183202
case 'sleep':
184-
return this.sleepStateDetails();
203+
definition = this.sleepStateDetails();
204+
break;
185205
case 'event':
186-
return undefined; //NOTHING
206+
// NOTHING
207+
break;
187208
case 'operation':
188-
return this.operationStateDetails();
209+
definition = this.operationStateDetails();
210+
break;
189211
case 'parallel':
190-
return this.parallelStateDetails();
212+
definition = this.parallelStateDetails();
213+
break;
191214
case 'switch':
192215
const switchState: any = this.state;
193216
if (switchState.dataConditions) {
194-
return this.dataBasedSwitchStateDetails();
217+
definition = this.dataBasedSwitchStateDetails();
218+
break;
195219
}
196220
if (switchState.eventConditions) {
197-
return this.eventBasedSwitchStateDetails();
221+
definition = this.eventBasedSwitchStateDetails();
222+
break;
198223
}
199224
throw new Error(`Unexpected switch type; \n state value= ${JSON.stringify(this.state, null, 4)}`);
200225
case 'inject':
201-
return undefined; // NOTHING
226+
// NOTHING
227+
break;
202228
case 'foreach':
203-
return this.foreachStateDetails();
229+
definition = this.foreachStateDetails();
230+
break;
204231
case 'callback':
205-
return this.callbackStateDetails();
232+
definition = this.callbackStateDetails();
233+
break;
206234
default:
207235
throw new Error(`Unexpected type= ${this.state.type}; \n state value= ${JSON.stringify(this.state, null, 4)}`);
208236
}
237+
238+
if (this.state.usedForCompensation) {
239+
definition = definition ? definition : '';
240+
definition = this.stateDescription(this.stateKeyDiagram(this.state.name), 'usedForCompensation\n') + definition;
241+
}
242+
243+
return definition ? definition : undefined;
209244
}
210245

211246
private definitionType() {
@@ -349,7 +384,7 @@ export class MermaidState {
349384
return this.stateKeyDiagram(source) + ' --> ' + this.stateKeyDiagram(target) + (label ? ' : ' + label : '');
350385
}
351386

352-
private stateDescription(stateName: string | undefined, description: string, value: string) {
353-
return stateName + ` : ${description} = ${value}`;
387+
private stateDescription(stateName: string | undefined, description: string, value?: string) {
388+
return stateName + ` : ${description}${value !== undefined ? ' = ' + value : ''}`;
354389
}
355390
}

tests/lib/diagram/mermaidDiagram.spec.ts

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,8 @@ import fs from 'fs';
1919

2020
describe('MermaidDiagram', () => {
2121
it('should create mermaid diagram source code', () => {
22-
const expected = fs.readFileSync('./tests/examples/jobmonitoring.json', 'utf8');
23-
const actual = new MermaidDiagram(Specification.Workflow.fromSource(expected)).sourceCode();
22+
const jsonSource = fs.readFileSync('./tests/examples/jobmonitoring.json', 'utf8');
23+
const actual = new MermaidDiagram(Specification.Workflow.fromSource(jsonSource)).sourceCode();
2424
expect(actual).toBe(`stateDiagram-v2
2525
SubmitJob : SubmitJob
2626
SubmitJob : type = Operation State
@@ -59,4 +59,28 @@ JobFailed : Action mode = sequential
5959
JobFailed : Num. of actions = 1
6060
JobFailed --> [*]`);
6161
});
62+
63+
it(`should handle compensated by`, () => {
64+
const jsonSource = fs.readFileSync('./tests/lib/diagram/wf_with_compensation.json', 'utf8');
65+
const actual = new MermaidDiagram(Specification.Workflow.fromSource(jsonSource)).sourceCode();
66+
67+
expect(actual).toBe(`stateDiagram-v2
68+
Item_Purchase : Item Purchase
69+
Item_Purchase : type = Event State
70+
[*] --> Item_Purchase
71+
Item_Purchase --> Cancel_Purchase : compensated by
72+
Item_Purchase --> [*]
73+
74+
Cancel_Purchase : Cancel Purchase
75+
Cancel_Purchase : type = Operation State
76+
Cancel_Purchase : usedForCompensation
77+
Cancel_Purchase : Action mode = sequential
78+
Cancel_Purchase : Num. of actions = 1
79+
Cancel_Purchase --> Send_confirmation_purchase_cancelled
80+
81+
Send_confirmation_purchase_cancelled : Send confirmation purchase cancelled
82+
Send_confirmation_purchase_cancelled : type = Operation State
83+
Send_confirmation_purchase_cancelled : Action mode = sequential
84+
Send_confirmation_purchase_cancelled : Num. of actions = 1`);
85+
});
6286
});
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
{
2+
"id": "newItemPurchaseWorkflow",
3+
"version": "1.0",
4+
"specVersion": "0.8",
5+
"name": "New Item Purchase Workflow",
6+
"states": [
7+
{
8+
"name": "Item Purchase",
9+
"type": "event",
10+
"onEvents": [
11+
{
12+
"eventRefs": [
13+
"New Purchase Event"
14+
],
15+
"actions": [
16+
{
17+
"functionRef": {
18+
"refName": "Invoke Debit Customer Function",
19+
"arguments": {
20+
"customerid": "${ .purchase.customerid }",
21+
"amount": "${ .purchase.amount }"
22+
}
23+
}
24+
}
25+
]
26+
}
27+
],
28+
"compensatedBy": "Cancel Purchase",
29+
"end": true,
30+
"onErrors": [
31+
{
32+
"errorRef": "Debit Error",
33+
"end": {
34+
"compensate": true
35+
}
36+
}
37+
]
38+
},
39+
{
40+
"name": "Cancel Purchase",
41+
"type": "operation",
42+
"usedForCompensation": true,
43+
"actions": [
44+
{
45+
"functionRef": {
46+
"refName": "Invoke Credit Customer Function",
47+
"arguments": {
48+
"customerid": "${ .purchase.customerid }",
49+
"amount": "${ .purchase.amount }"
50+
}
51+
}
52+
}
53+
],
54+
"transition": "Send confirmation purchase cancelled"
55+
},
56+
{
57+
"name": "Send confirmation purchase cancelled",
58+
"type": "operation",
59+
"actions": [
60+
{
61+
"functionRef": {
62+
"refName": "Send email",
63+
"arguments": {
64+
"customerid": "${ .purchase.customerid }",
65+
}
66+
}
67+
}
68+
]
69+
}
70+
],
71+
"functions": "http://myservicedefs.io/graphqldef.json",
72+
"events": "http://myeventdefs.io/eventdefs.json",
73+
"errors": "file://mydefs/errordefs.json"
74+
}

0 commit comments

Comments
 (0)