Skip to content

Commit af67060

Browse files
committed
Refactor SDK wrapper to use Classes.Workflow directly instead of deserialize
Signed-off-by: Lorna-Kelly <lornakelly88@gmail.com>
1 parent e52a590 commit af67060

5 files changed

Lines changed: 241 additions & 155 deletions

File tree

packages/serverless-workflow-diagram-editor/src/core/workflowSdk.ts

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +14,42 @@
1414
* limitations under the License.
1515
*/
1616

17+
import yaml from "js-yaml";
1718
import { Classes, Specification, validate } from "@serverlessworkflow/sdk";
1819

19-
export type WorkflowParseResult =
20-
| { success: true; workflow: Specification.Workflow }
21-
| { success: false; error: Error };
20+
export type WorkflowParseResult = {
21+
model: Specification.Workflow | null;
22+
errors: Error[];
23+
};
2224

23-
export function deserializeWorkflow(text: string): Specification.Workflow {
24-
return Classes.Workflow.deserialize(text);
25-
}
26-
27-
export function validateWorkflow(workflow: Specification.Workflow): void {
28-
validate("Workflow", workflow);
25+
export function validateWorkflow(model: Specification.Workflow): Error[] {
26+
try {
27+
validate("Workflow", model);
28+
return [];
29+
} catch (err) {
30+
// TODO: Parse individual validation errors from the SDK into separate Error objects when we are ready to render them.
31+
return [err instanceof Error ? err : new Error(String(err))];
32+
}
2933
}
3034

3135
export function parseWorkflow(text: string): WorkflowParseResult {
36+
let raw: Partial<Specification.Workflow>;
37+
3238
try {
33-
const workflow = deserializeWorkflow(text);
34-
validateWorkflow(workflow);
35-
return { success: true, workflow };
39+
raw = yaml.load(text, { schema: yaml.DEFAULT_SCHEMA }) as Partial<Specification.Workflow>;
3640
} catch (err) {
3741
return {
38-
success: false,
39-
error: err instanceof Error ? err : new Error(String(err)),
42+
model: null,
43+
errors: [err instanceof Error ? err : new Error(String(err))],
4044
};
4145
}
46+
47+
if (raw == null || typeof raw !== "object") {
48+
return { model: null, errors: [new Error("Not a valid workflow object")] };
49+
}
50+
51+
const model = new Classes.Workflow(raw) as Specification.Workflow;
52+
const errors = validateWorkflow(model);
53+
54+
return { model, errors };
4255
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html
2+
3+
exports[`parseWorkflow > parses valid 'JSON' and returns model with no errors 1`] = `
4+
{
5+
"errors": [],
6+
"model": t {
7+
"do": t [
8+
t {
9+
"step1": t {
10+
"set": t {
11+
"variable": "my first workflow",
12+
},
13+
},
14+
},
15+
],
16+
"document": t {
17+
"dsl": "1.0.0",
18+
"name": "valid-workflow-json",
19+
"namespace": "default",
20+
"version": "1.0.0",
21+
},
22+
},
23+
}
24+
`;
25+
26+
exports[`parseWorkflow > parses valid 'YAML' and returns model with no errors 1`] = `
27+
{
28+
"errors": [],
29+
"model": t {
30+
"do": t [
31+
t {
32+
"step1": t {
33+
"set": t {
34+
"variable": "my first workflow",
35+
},
36+
},
37+
},
38+
],
39+
"document": t {
40+
"dsl": "1.0.0",
41+
"name": "valid-workflow-yaml",
42+
"namespace": "default",
43+
"version": "1.0.0",
44+
},
45+
},
46+
}
47+
`;
48+
49+
exports[`parseWorkflow > returns model and errors for invalid but parseable 'JSON' 1`] = `
50+
{
51+
"errors": [
52+
[Error: 'Workflow' is invalid - The DSL version of the workflow 'undefined' doesn't match the supported version of the SDK '1.0.0'.],
53+
],
54+
"model": t {
55+
"do": t [
56+
t {
57+
"step1": t {
58+
"set": t {
59+
"variable": "my first invalid json workflow",
60+
},
61+
},
62+
},
63+
],
64+
},
65+
}
66+
`;
67+
68+
exports[`parseWorkflow > returns model and errors for invalid but parseable 'YAML' 1`] = `
69+
{
70+
"errors": [
71+
[Error: 'Workflow' is invalid - The DSL version of the workflow 'undefined' doesn't match the supported version of the SDK '1.0.0'.],
72+
],
73+
"model": t {
74+
"do": t [
75+
t {
76+
"step1": t {
77+
"set": t {
78+
"variable": "my first invalid yaml workflow",
79+
},
80+
},
81+
},
82+
],
83+
},
84+
}
85+
`;
86+
87+
exports[`validateWorkflow > returns errors for an invalid workflow 1`] = `
88+
[
89+
[Error: 'Workflow' is invalid - The DSL version of the workflow 'undefined' doesn't match the supported version of the SDK '1.0.0'.],
90+
]
91+
`;
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
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+
17+
import { describe, it, expect } from "vitest";
18+
import { parseWorkflow, validateWorkflow } from "../../src/core";
19+
import {
20+
BASIC_VALID_WORKFLOW_YAML,
21+
BASIC_VALID_WORKFLOW_JSON,
22+
BASIC_INVALID_WORKFLOW_YAML,
23+
BASIC_INVALID_WORKFLOW_JSON,
24+
} from "../fixtures/workflows";
25+
import { Classes, Specification } from "@serverlessworkflow/sdk";
26+
27+
describe("parseWorkflow", () => {
28+
it.each([
29+
{ format: "YAML", input: BASIC_VALID_WORKFLOW_YAML, expectedName: "valid-workflow-yaml" },
30+
{ format: "JSON", input: BASIC_VALID_WORKFLOW_JSON, expectedName: "valid-workflow-json" },
31+
])("parses valid $format and returns model with no errors", ({ input, expectedName }) => {
32+
const result = parseWorkflow(input);
33+
expect(result.errors).toHaveLength(0);
34+
expect(result.model?.document?.name).toBe(expectedName);
35+
expect(result).toMatchSnapshot();
36+
});
37+
38+
it.each([
39+
{ format: "YAML", input: BASIC_INVALID_WORKFLOW_YAML },
40+
{ format: "JSON", input: BASIC_INVALID_WORKFLOW_JSON },
41+
])("returns model and errors for invalid but parseable $format", ({ input }) => {
42+
const result = parseWorkflow(input);
43+
expect(result.model).not.toBeNull();
44+
expect(result.errors).toHaveLength(1);
45+
expect(result).toMatchSnapshot();
46+
});
47+
48+
it.each([
49+
{ description: "empty string", input: "" },
50+
{ description: "non-object YAML", input: "just a string" },
51+
{ description: "numeric YAML", input: "42" },
52+
])("returns null model with error for $description", ({ input }) => {
53+
const result = parseWorkflow(input);
54+
expect(result.model).toBeNull();
55+
expect(result.errors[0].message).toBe("Not a valid workflow object");
56+
});
57+
58+
it("returns null model with errors for unparseable text", () => {
59+
const result = parseWorkflow("}{not yaml or json}{");
60+
expect(result.model).toBeNull();
61+
expect(result.errors).toHaveLength(1);
62+
});
63+
});
64+
65+
describe("validateWorkflow", () => {
66+
it("returns empty array for a valid workflow", () => {
67+
const valid = new Classes.Workflow({
68+
document: { dsl: "1.0.0", name: "valid-workflow", version: "1.0.0", namespace: "default" },
69+
do: [{ step1: { set: { variable: "value" } } }],
70+
}) as Specification.Workflow;
71+
const errors = validateWorkflow(valid);
72+
expect(errors).toHaveLength(0);
73+
});
74+
75+
it("returns errors for an invalid workflow", () => {
76+
const invalid = new Classes.Workflow({
77+
do: [{ step1: { set: { variable: "value" } } }],
78+
}) as Specification.Workflow;
79+
const errors = validateWorkflow(invalid);
80+
expect(errors).toHaveLength(1);
81+
expect(errors).toMatchSnapshot();
82+
});
83+
});

packages/serverless-workflow-diagram-editor/tests/core/workflowSdk.test.ts

Lines changed: 0 additions & 139 deletions
This file was deleted.

0 commit comments

Comments
 (0)