Skip to content

Commit 9760570

Browse files
committed
Fixed builders output type
- Fixed the builders output type - Added more samples Signed-off-by: Jean-Baptiste Bianchi <[email protected]>
1 parent fc5defd commit 9760570

File tree

177 files changed

+1912
-1090
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

177 files changed

+1912
-1090
lines changed

README.md

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@ The fluent builders wrap the core classes and provide a fluent API for construct
4949

5050
The fluent builders are directly exported as `*<desired-type>*Builder`, e.g., `workflowBuilder`.
5151

52+
By default, built objects are self-validated and self-normalized. `BuildOptions` can be passed to the `build()` method to disable validation or normalization.
53+
5254
### Validation Function
5355
The SDK includes a validation function to check if objects conform to the expected schema. This function ensures that your workflow objects are correctly structured and meet the required specifications.
5456

examples/browser/index.html renamed to examples/browser/using-class.html

+3-3
Original file line numberDiff line numberDiff line change
@@ -9,15 +9,15 @@
99
</head>
1010

1111
<body>
12-
<div id="output"></div>
12+
<pre id="output"></pre>
1313
<script src="../../dist/umd/index.umd.js"></script>
1414
<script type="text/javascript">
1515
(() => {
1616
const { Classes: { Workflow } } = serverWorkflowSdk;
1717
const workflow = new Workflow({
1818
document: {
1919
dsl: '1.0.0-alpha5',
20-
name: 'test',
20+
name: 'using-class',
2121
version: '1.0.0',
2222
namespace: 'default',
2323
},
@@ -33,7 +33,7 @@
3333
});
3434
try {
3535
workflow.validate();
36-
document.getElementById('output').innerHTML = workflow.serialize('json');
36+
document.getElementById('output').innerHTML = `--- YAML ---\n${workflow.serialize()}\n\n--- JSON ---\n${workflow.serialize('json')}`;
3737
} catch (ex) {
3838
console.error('Invalid workflow', ex);
3939
}
+36
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<title>Serveless Workflow</title>
7+
<base href="/">
8+
<meta content="width=device-width, initial-scale=1" name="viewport">
9+
</head>
10+
11+
<body>
12+
<pre id="output"></pre>
13+
<script src="../../dist/umd/index.umd.js"></script>
14+
<script type="text/javascript">
15+
(() => {
16+
const { workflowBuilder, documentBuilder, taskListBuilder, setTaskBuilder } = serverWorkflowSdk;
17+
try {
18+
const workflow = workflowBuilder()
19+
.document(documentBuilder().dsl('1.0.0-alpha5').name('using-fluent-api').version('1.0.0').namespace('default').build())
20+
.do(
21+
taskListBuilder()
22+
.push({
23+
step1: setTaskBuilder().set({ foo: 'bar' }).build(),
24+
})
25+
.build(),
26+
)
27+
.build();
28+
document.getElementById('output').innerHTML = `--- YAML ---\n${workflow.serialize()}\n\n--- JSON ---\n${workflow.serialize('json')}`;
29+
} catch (ex) {
30+
console.error('Invalid workflow', ex);
31+
}
32+
})();
33+
</script>
34+
</body>
35+
36+
</html>

examples/browser/using-json.html

+40
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<title>Serveless Workflow</title>
7+
<base href="/">
8+
<meta content="width=device-width, initial-scale=1" name="viewport">
9+
</head>
10+
11+
<body>
12+
<pre id="output"></pre>
13+
<script src="../../dist/umd/index.umd.js"></script>
14+
<script type="text/javascript">
15+
(() => {
16+
const { Classes: { Workflow } } = serverWorkflowSdk;
17+
const myJsonWorkflow = `{
18+
"document": {
19+
"dsl": "1.0.0-alpha5",
20+
"name": "using-json",
21+
"version": "1.0.0",
22+
"namespace": "default"
23+
},
24+
"do": [
25+
{
26+
"step1": {
27+
"set": {
28+
"variable": "my first workflow"
29+
}
30+
}
31+
}
32+
]
33+
}`;
34+
const workflow = Workflow.deserialize(myJsonWorkflow);
35+
document.getElementById('output').innerHTML = `--- YAML ---\n${workflow.serialize()}\n\n--- JSON ---\n${workflow.serialize('json')}`;
36+
})();
37+
</script>
38+
</body>
39+
40+
</html>
+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
<!doctype html>
2+
<html lang="en">
3+
4+
<head>
5+
<meta charset="utf-8">
6+
<title>Serveless Workflow</title>
7+
<base href="/">
8+
<meta content="width=device-width, initial-scale=1" name="viewport">
9+
</head>
10+
11+
<body>
12+
<pre id="output"></pre>
13+
<script src="../../dist/umd/index.umd.js"></script>
14+
<script type="text/javascript">
15+
(() => {
16+
const { Classes, Specification, validate } = serverWorkflowSdk;
17+
const workflowDefinition = {
18+
document: {
19+
dsl: '1.0.0-alpha5',
20+
name: 'using-plain-object',
21+
version: '1.0.0',
22+
namespace: 'default',
23+
},
24+
do: [
25+
{
26+
step1: {
27+
set: {
28+
variable: 'my first workflow',
29+
},
30+
},
31+
},
32+
],
33+
}/* as Specification.Workflow // <-- If you're using TypeScript*/;
34+
try {
35+
validate('Workflow', workflowDefinition);
36+
document.getElementById('output').innerHTML = `--- YAML ---\n${Classes.Workflow.serialize(workflowDefinition)}\n\n--- JSON ---\n${Classes.Workflow.serialize(workflowDefinition, 'json')}`;
37+
}
38+
catch (ex) {
39+
console.error('Invalid workflow', ex);
40+
}
41+
})();
42+
</script>
43+
</body>
44+
45+
</html>

examples/node/index.ts renamed to examples/node/using-class.ts

+3-3
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,13 @@
1313
* See the License for the specific language governing permissions and
1414
* limitations under the License.
1515
*/
16-
import { Classes } from '../../dist';
16+
import { Classes } from /*'@serverlessworkflow/sdk';*/ '../../dist';
1717
const { Workflow } = Classes;
1818

1919
const workflow = new Workflow({
2020
document: {
2121
dsl: '1.0.0-alpha5',
22-
name: 'test',
22+
name: 'using-class',
2323
version: '1.0.0',
2424
namespace: 'default',
2525
},
@@ -36,7 +36,7 @@ const workflow = new Workflow({
3636

3737
try {
3838
workflow.validate();
39-
console.log(workflow.serialize('json'));
39+
console.log(`--- YAML ---\n${workflow.serialize()}\n\n--- JSON ---\n${workflow.serialize('json')}`);
4040
} catch (ex) {
4141
console.error('Invalid workflow', ex);
4242
}

examples/node/using-fluent-api.ts

+39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
/*
2+
* Copyright 2021-Present The Serverless Workflow Specification Authors
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
import {
17+
documentBuilder,
18+
setTaskBuilder,
19+
taskListBuilder,
20+
workflowBuilder,
21+
} from /*'@serverlessworkflow/sdk';*/ '../../dist';
22+
23+
try {
24+
const workflow = workflowBuilder()
25+
.document(
26+
documentBuilder().dsl('1.0.0-alpha5').name('using-fluent-api').version('1.0.0').namespace('default').build(),
27+
)
28+
.do(
29+
taskListBuilder()
30+
.push({
31+
step1: setTaskBuilder().set({ foo: 'bar' }).build(),
32+
})
33+
.build(),
34+
)
35+
.build();
36+
console.log(`--- YAML ---\n${workflow.serialize()}\n\n--- JSON ---\n${workflow.serialize('json')}`);
37+
} catch (ex) {
38+
console.error('Invalid workflow', ex);
39+
}

examples/node/using-json.ts

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
import { Classes } from /*'@serverlessworkflow/sdk';*/ '../../dist';
2+
3+
const myJsonWorkflow = `
4+
{
5+
"document": {
6+
"dsl": "1.0.0-alpha5",
7+
"name": "using-json",
8+
"version": "1.0.0",
9+
"namespace": "default"
10+
},
11+
"do": [
12+
{
13+
"step1": {
14+
"set": {
15+
"variable": "my first workflow"
16+
}
17+
}
18+
}
19+
]
20+
}`;
21+
const workflow = Classes.Workflow.deserialize(myJsonWorkflow);
22+
console.log(`--- YAML ---\n${workflow.serialize()}\n\n--- JSON ---\n${workflow.serialize('json')}`);

examples/node/using-plain-object.ts

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { Classes, Specification, validate } from /*'@serverlessworkflow/sdk';*/ '../../dist';
2+
3+
const workflowDefinition = {
4+
document: {
5+
dsl: '1.0.0-alpha5',
6+
name: 'using-plain-object',
7+
version: '1.0.0',
8+
namespace: 'default',
9+
},
10+
do: [
11+
{
12+
step1: {
13+
set: {
14+
variable: 'my first workflow',
15+
},
16+
},
17+
},
18+
],
19+
} as Specification.Workflow;
20+
try {
21+
validate('Workflow', workflowDefinition);
22+
console.log(
23+
`--- YAML ---\n${Classes.Workflow.serialize(workflowDefinition)}\n\n--- JSON ---\n${Classes.Workflow.serialize(workflowDefinition, 'json')}`,
24+
);
25+
} catch (ex) {
26+
console.error('Invalid workflow', ex);
27+
}

src/lib/builder.ts

+22-19
Original file line numberDiff line numberDiff line change
@@ -32,28 +32,28 @@ export type BuildOptions = {
3232
/**
3333
* The type of the underlying function called on `build()` for objects
3434
*/
35-
export type BuildingFunction<T> = (model: Partial<T>, options: BuildOptions) => T;
35+
export type BuildingFunction<TSpec, TBuilt> = (model: Partial<TSpec>, options: BuildOptions) => TBuilt;
3636

3737
/**
3838
* The type of the underlying function called on `build()` for arrays
3939
*/
40-
export type ArrayBuildingFunction<T> = (model: Array<T>, options: BuildOptions) => Array<T>;
40+
export type ArrayBuildingFunction<TSpec, TBuilt> = (model: Array<TSpec>, options: BuildOptions) => TBuilt;
4141

4242
/**
4343
* Represents a fluent builder proxy for an object
4444
*/
45-
export type Builder<T> = {
46-
build: (option?: BuildOptions) => T;
45+
export type Builder<TSpec, TBuilt = TSpec> = {
46+
build: (option?: BuildOptions) => TBuilt;
4747
} & {
48-
[K in keyof T]-?: (arg: T[K]) => Builder<T>;
48+
[K in keyof TSpec]-?: (arg: TSpec[K]) => Builder<TSpec, TBuilt>;
4949
};
5050

5151
/**
5252
* Represents a fluent builder proxy for an array
5353
*/
54-
export type ArrayBuilder<T> = {
55-
push: (item: T) => ArrayBuilder<T>;
56-
build: (option?: BuildOptions) => Array<T>;
54+
export type ArrayBuilder<TSpec, TBuilt> = {
55+
push: (item: TSpec) => ArrayBuilder<TSpec, TBuilt>;
56+
build: (option?: BuildOptions) => TBuilt;
5757
};
5858

5959
/**
@@ -62,24 +62,27 @@ export type ArrayBuilder<T> = {
6262
* @param options The build options
6363
* @returns
6464
*/
65-
function defaultBuildingFn<T>(model: Partial<T>, options: BuildOptions): T {
65+
function defaultBuildingFn<TSpec, TBuilt = TSpec>(model: Partial<TSpec>, options: BuildOptions): TBuilt {
6666
// prevents @typescript-eslint/no-unused-vars ...
6767
if (options.validate == null) {
6868
options.validate = true;
6969
}
7070
if (options.normalize == null) {
7171
options.normalize = true;
7272
}
73-
return model as T;
73+
return model as TBuilt;
7474
}
7575

7676
/**
7777
* A factory for fluent builders that proxy properties assignations and can validate against schema on build()
7878
* @param buildingFn The function used to validate and produce the object on build()
7979
* @returns A fluent builder
8080
*/
81-
export function builder<T>(model: Partial<T> = {}, buildingFn: BuildingFunction<T> = defaultBuildingFn): Builder<T> {
82-
const proxy = new Proxy({} as Builder<T>, {
81+
export function builder<TSpec, TBuilt = TSpec>(
82+
model: Partial<TSpec> = {},
83+
buildingFn: BuildingFunction<TSpec, TBuilt> = defaultBuildingFn,
84+
): Builder<TSpec, TBuilt> {
85+
const proxy = new Proxy({} as Builder<TSpec, TBuilt>, {
8386
get: (_, prop) => {
8487
if (prop === 'build') {
8588
return (options?: BuildOptions) => {
@@ -93,7 +96,7 @@ export function builder<T>(model: Partial<T> = {}, buildingFn: BuildingFunction<
9396
return buildingFn(model, options);
9497
};
9598
}
96-
return (value: unknown): Builder<T> => {
99+
return (value: unknown): Builder<TSpec, TBuilt> => {
97100
(model as any)[prop.toString()] = value;
98101
return proxy;
99102
};
@@ -110,14 +113,14 @@ export function builder<T>(model: Partial<T> = {}, buildingFn: BuildingFunction<
110113
* @param buildingFn The function used to validate and produce the object on build()
111114
* @returns A fluent builder
112115
*/
113-
export function arrayBuilder<T>(
114-
model: Array<T> = [],
115-
buildingFn: ArrayBuildingFunction<T> = defaultBuildingFn,
116-
): ArrayBuilder<T> {
116+
export function arrayBuilder<TSpec, TBuilt>(
117+
model: Array<TSpec> = [],
118+
buildingFn: ArrayBuildingFunction<TSpec, TBuilt> = defaultBuildingFn,
119+
): ArrayBuilder<TSpec, TBuilt> {
117120
if (model != null && !Array.isArray(model)) {
118121
throw new Error(`The provided model should be an array`);
119122
}
120-
const proxy = new Proxy({} as ArrayBuilder<T>, {
123+
const proxy = new Proxy({} as ArrayBuilder<TSpec, TBuilt>, {
121124
get: (_, prop) => {
122125
if (prop === 'build') {
123126
return (options?: BuildOptions) => {
@@ -132,7 +135,7 @@ export function arrayBuilder<T>(
132135
};
133136
}
134137
if (prop === 'push') {
135-
return (value: T): ArrayBuilder<T> => {
138+
return (value: TSpec): ArrayBuilder<TSpec, TBuilt> => {
136139
model.push(value);
137140
return proxy;
138141
};

0 commit comments

Comments
 (0)