Skip to content

Commit 3dfdfb2

Browse files
committed
feat: add graph and MermaidJS builders
Signed-off-by: Jean-Baptiste Bianchi <[email protected]>
1 parent 7d510e5 commit 3dfdfb2

File tree

11 files changed

+566
-110
lines changed

11 files changed

+566
-110
lines changed

README.md

+111-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,26 @@
11
![Node CI](https://github.com/serverlessworkflow/sdk-typescript/workflows/Node%20CI/badge.svg) [![Gitpod ready-to-code](https://img.shields.io/badge/Gitpod-ready--to--code-blue?logo=gitpod)](https://gitpod.io/#https://github.com/serverlessworkflow/sdk-typescript)
22

3+
- [Serverless Workflow Specification - TypeScript SDK](#serverless-workflow-specification---typescript-sdk)
4+
- [Status](#status)
5+
- [SDK Structure](#sdk-structure)
6+
- [Types and Interfaces](#types-and-interfaces)
7+
- [Classes](#classes)
8+
- [Fluent Builders](#fluent-builders)
9+
- [Validation Function](#validation-function)
10+
- [Other tools](#other-tools)
11+
- [Getting Started](#getting-started)
12+
- [Installation](#installation)
13+
- [Usage](#usage)
14+
- [Create a Workflow Definition from YAML or JSON](#create-a-workflow-definition-from-yaml-or-json)
15+
- [Create a Workflow Definition by Casting an Object](#create-a-workflow-definition-by-casting-an-object)
16+
- [Create a Workflow Definition Using a Class Constructor](#create-a-workflow-definition-using-a-class-constructor)
17+
- [Create a Workflow Definition Using the Builder API](#create-a-workflow-definition-using-the-builder-api)
18+
- [Serialize a Workflow Definition to YAML or JSON](#serialize-a-workflow-definition-to-yaml-or-json)
19+
- [Validate Workflow Definitions](#validate-workflow-definitions)
20+
- [Generate a directed graph](#generate-a-directed-graph)
21+
- [Generate a MermaidJS flowchart](#generate-a-mermaidjs-flowchart)
22+
- [Building Locally](#building-locally)
23+
324
# Serverless Workflow Specification - TypeScript SDK
425

526
This SDK provides a TypeScript API for working with the [Serverless Workflow Specification](https://github.com/serverlessworkflow/specification).
@@ -14,7 +35,7 @@ The npm [`@serverlessworkflow/sdk`](https://www.npmjs.com/package/@serverlesswor
1435

1536
| Latest Releases | Conformance to Spec Version |
1637
| :---: | :---: |
17-
| [v1.0.0.\*](https://github.com/serverlessworkflow/sdk-typescript/releases/) | [v1.0.0](https://github.com/serverlessworkflow/specification) |
38+
| [v1.0.\*](https://github.com/serverlessworkflow/sdk-typescript/releases/) | [v1.0.0](https://github.com/serverlessworkflow/specification) |
1839

1940
> [!WARNING]
2041
> Previous versions of the SDK were published with a typo in the scope:
@@ -56,6 +77,9 @@ The SDK includes a validation function to check if objects conform to the expect
5677

5778
The `validate` function is directly exported and can be used as `validate('Workflow', workflowObject)`.
5879

80+
### Other Tools
81+
The SDK also ships tools to build directed graph and MermaidJS flowcharts from a workflow.
82+
5983
## Getting Started
6084

6185
### Installation
@@ -223,7 +247,7 @@ Validation can be achieved in two ways: via the `validate` function or the insta
223247
```typescript
224248
import { Classes, validate } from '@serverlessworkflow/sdk';
225249

226-
// const workflowDefinition = <Your preferred method>;
250+
const workflowDefinition = /* <Your preferred method> */;
227251
try {
228252
if (workflowDefinition instanceof Classes.Workflow) {
229253
workflowDefinition.validate();
@@ -237,6 +261,91 @@ catch (ex) {
237261
}
238262
```
239263

264+
#### Generate a directed graph
265+
A [directed graph](https://en.wikipedia.org/wiki/Directed_graph) of a workflow can be generated using the `buildGraph` function:
266+
267+
```typescript
268+
import { buildGraph } from '@serverlessworkflow/sdk';
269+
270+
const workflowDefinition = {
271+
document: {
272+
dsl: '1.0.0',
273+
name: 'using-plain-object',
274+
version: '1.0.0',
275+
namespace: 'default',
276+
},
277+
do: [
278+
{
279+
step1: {
280+
set: {
281+
variable: 'my first workflow',
282+
},
283+
},
284+
},
285+
],
286+
};
287+
const graph = buildGraph(workflowDefinition);
288+
/*{
289+
id: 'root',
290+
type: 'root',
291+
label: undefined,
292+
parent: null,
293+
nodes: [...], // length 3 - root entry node, step1 node, root exit node
294+
edges: [...], // length 2 - entry to step1, step1 to exit
295+
entryNode: {...}, // root entry node
296+
exitNode: {...} // root exit node
297+
}*/
298+
```
299+
300+
#### Generate a MermaidJS flowchart
301+
Generating a [MermaidJS](https://mermaid.js.org/) flowchart can be achieved in two ways: using the `convertToMermaidCode` or the legacy `MermaidDiagram` class.
302+
303+
```typescript
304+
import { convertToMermaidCode, MermaidDiagram } from '@serverlessworkflow/sdk';
305+
306+
const workflowDefinition = {
307+
document: {
308+
dsl: '1.0.0',
309+
name: 'using-plain-object',
310+
version: '1.0.0',
311+
namespace: 'default',
312+
},
313+
do: [
314+
{
315+
step1: {
316+
set: {
317+
variable: 'my first workflow',
318+
},
319+
},
320+
},
321+
],
322+
};
323+
const mermaidCode = convertToMermaidCode(workflowDefinition) /* or new MermaidDiagram(workflowDefinition).sourceCode() */;
324+
/*
325+
flowchart TD
326+
root-entry-node(( ))
327+
root-exit-node((( )))
328+
/do/0/step1["step1"]
329+
/do/0/step1 --> root-exit-node
330+
root-entry-node --> /do/0/step1
331+
332+
333+
classDef hidden display: none;
334+
*/
335+
```
336+
337+
```mermaid
338+
flowchart TD
339+
root-entry-node(( ))
340+
root-exit-node((( )))
341+
/do/0/step1["step1"]
342+
/do/0/step1 --> root-exit-node
343+
root-entry-node --> /do/0/step1
344+
345+
346+
classDef hidden display: none;
347+
```
348+
240349
### Building Locally
241350

242351
To build the project and run tests locally, use the following commands:

examples/browser/mermaid.html

+105-25
Original file line numberDiff line numberDiff line change
@@ -9,41 +9,121 @@
99
</head>
1010

1111
<body>
12+
<p>YAML or JSON:</p>
13+
<textarea id="input" rows="50" cols="100"></textarea>
14+
<div id="diagram-container"></div>
1215
<pre id="output"></pre>
1316
<script src="../../dist/umd/index.umd.js"></script>
14-
<pre class="mermaid" id="diagram-container"></div>
1517
<script type="module">
1618
import mermaid from 'https://cdn.jsdelivr.net/npm/mermaid@11/dist/mermaid.esm.min.mjs';
17-
(() => {
19+
(async () => {
1820
const { Classes, Specification, validate, convertToMermaidCode } = serverWorkflowSdk;
1921
const workflowDefinition = {
20-
document: {
21-
dsl: '1.0.0',
22-
name: 'using-plain-object',
23-
version: '1.0.0',
24-
namespace: 'default',
22+
"document": {
23+
"dsl": "1.0.0",
24+
"namespace": "examples",
25+
"name": "accumulate-room-readings",
26+
"version": "0.1.0"
27+
},
28+
"do": [
29+
{
30+
"consumeReading": {
31+
"listen": {
32+
"to": {
33+
"all": [
34+
{
35+
"with": {
36+
"source": "https://my.home.com/sensor",
37+
"type": "my.home.sensors.temperature"
38+
},
39+
"correlate": {
40+
"roomId": {
41+
"from": ".roomid"
42+
}
43+
}
44+
},
45+
{
46+
"with": {
47+
"source": "https://my.home.com/sensor",
48+
"type": "my.home.sensors.humidity"
49+
},
50+
"correlate": {
51+
"roomId": {
52+
"from": ".roomid"
53+
}
54+
}
55+
}
56+
]
57+
}
58+
},
59+
"output": {
60+
"as": ".data.reading"
61+
}
62+
}
63+
},
64+
{
65+
"logReading": {
66+
"for": {
67+
"each": "reading",
68+
"in": ".readings"
2569
},
26-
do: [
70+
"do": [
2771
{
28-
step1: {
29-
set: {
30-
variable: 'my first workflow',
31-
},
32-
},
33-
},
34-
],
35-
}/* as Specification.Workflow // <-- If you're using TypeScript*/;
36-
try {
37-
validate('Workflow', workflowDefinition);
38-
const diagramContainerEl = document.getElementById('diagram-container');
39-
const mermaidCode = convertToMermaidCode(workflowDefinition);
40-
diagramContainerEl.innerText = mermaidCode;
41-
document.getElementById('output').innerHTML = `--- YAML ---\n${Classes.Workflow.serialize(workflowDefinition)}\n\n--- JSON ---\n${Classes.Workflow.serialize(workflowDefinition, 'json')}\n\n--- MERMAID ---\n${mermaidCode}`;
42-
mermaid.initialize({ startOnLoad: true });
72+
"callOrderService": {
73+
"call": "openapi",
74+
"with": {
75+
"document": {
76+
"endpoint": "http://myorg.io/ordersservices.json"
77+
},
78+
"operationId": "logreading"
79+
}
80+
}
81+
}
82+
]
4383
}
44-
catch (ex) {
45-
console.error('Invalid workflow', ex);
84+
},
85+
{
86+
"generateReport": {
87+
"call": "openapi",
88+
"with": {
89+
"document": {
90+
"endpoint": "http://myorg.io/ordersservices.json"
91+
},
92+
"operationId": "produceReport"
93+
}
4694
}
95+
}
96+
],
97+
"timeout": {
98+
"after": {
99+
"hours": 1
100+
}
101+
}
102+
}/* as Specification.Workflow // <-- If you're using TypeScript*/;
103+
const diagramContainerEl = document.getElementById('diagram-container');
104+
const inputTextarea = document.getElementById('input');
105+
const processWorkflow = async () => {
106+
try {
107+
const workflow = Classes.Workflow.deserialize(inputTextarea.value);
108+
const mermaidCode = convertToMermaidCode(workflow);
109+
document.getElementById('output').innerHTML = `--- YAML ---\n${Classes.Workflow.serialize(workflow)}\n\n--- JSON ---\n${Classes.Workflow.serialize(workflow, 'json')}\n\n--- MERMAID ---\n${mermaidCode}`;
110+
mermaid.initialize({ startOnLoad: false });
111+
const { svg, bindFunctions } = await mermaid.render('sw-diagram', mermaidCode);
112+
diagramContainerEl.innerHTML = svg;
113+
}
114+
catch (ex) {
115+
console.error('Invalid workflow', ex);
116+
}
117+
};
118+
let debounceHandle;
119+
inputTextarea.addEventListener('keyup', () => {
120+
if (debounceHandle) {
121+
clearTimeout(debounceHandle);
122+
}
123+
debounceHandle = setTimeout(processWorkflow, 300);
124+
});
125+
inputTextarea.value = JSON.stringify(workflowDefinition, null, 4);
126+
await processWorkflow();
47127
})();
48128
</script>
49129
</body>

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@serverlessworkflow/sdk",
3-
"version": "1.0.0",
3+
"version": "1.0.1",
44
"schemaVersion": "1.0.0",
55
"description": "Typescript SDK for Serverless Workflow Specification",
66
"main": "umd/index.umd.min.js",

0 commit comments

Comments
 (0)