Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .rat-excludes
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,5 @@ pnpm-lock.yaml
pnpm-workspace.yaml
repo/graph.dot
repo/repo.iml
**/__snapshots__/**

5 changes: 4 additions & 1 deletion packages/serverless-workflow-diagram-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,9 @@
"build:storybook": "pnpm clean:storybook && storybook build --output-dir ./dist-storybook"
},
"dependencies": {
"@xyflow/react": "catalog:"
"@serverlessworkflow/sdk": "catalog:",
"@xyflow/react": "catalog:",
"js-yaml": "catalog:"
},
"devDependencies": {
"@chromatic-com/storybook": "catalog:",
Expand All @@ -50,6 +52,7 @@
"@testing-library/jest-dom": "catalog:",
"@testing-library/react": "catalog:",
"@testing-library/user-event": "catalog:",
"@types/js-yaml": "catalog:",
"@types/node": "catalog:",
"@types/react": "catalog:",
"@types/react-dom": "catalog:",
Expand Down
15 changes: 11 additions & 4 deletions packages/serverless-workflow-diagram-editor/src/core/README.md
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
<!--
Copyright 2021-Present The Serverless Workflow Specification Authors

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Expand All @@ -15,4 +15,11 @@
-->

# core
Core package agnostic from the rendering library and its types.

Core package agnostic from the rendering library and its types.

## Modules

### workflowSdk.ts

Abstraction layer over the `@serverlessworkflow/sdk`. This is the only place in the diagram editor that imports from the SDK directly keeping the rest of the editor decoupled from SDK implementation details.
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,4 @@
* limitations under the License.
*/

import { expect, describe, it } from "vitest";

describe("sampleTest", () => {
it("Testing...", () => {
expect(true).toBeTruthy();
});
});
export * from "./workflowSdk";
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

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

export type WorkflowParseResult = {
model: Specification.Workflow | null;
errors: Error[];
};

export function validateWorkflow(model: Specification.Workflow): Error[] {
try {
validate("Workflow", model);
return [];
} catch (err) {
// TODO: Parse individual validation errors from the SDK into separate Error objects when we are ready to render them.
return [err instanceof Error ? err : new Error(String(err))];
}
}

export function parseWorkflow(text: string): WorkflowParseResult {
let raw: Partial<Specification.Workflow>;

try {
raw = yaml.load(text, { schema: yaml.DEFAULT_SCHEMA }) as Partial<Specification.Workflow>;
} catch (err) {
return {
model: null,
errors: [err instanceof Error ? err : new Error(String(err))],
};
}

if (raw == null || typeof raw !== "object") {
return { model: null, errors: [new Error("Not a valid workflow object")] };
}

const model = new Classes.Workflow(raw) as Specification.Workflow;
const errors = validateWorkflow(model);

return { model, errors };
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
// Vitest Snapshot v1, https://vitest.dev/guide/snapshot.html

exports[`parseWorkflow > parses valid 'JSON' and returns model with no errors 1`] = `
{
"errors": [],
"model": t {
"do": t [
t {
"step1": t {
"set": t {
"variable": "my first workflow",
},
},
},
],
"document": t {
"dsl": "1.0.0",
"name": "valid-workflow-json",
"namespace": "default",
"version": "1.0.0",
},
},
}
`;

exports[`parseWorkflow > parses valid 'YAML' and returns model with no errors 1`] = `
{
"errors": [],
"model": t {
"do": t [
t {
"step1": t {
"set": t {
"variable": "my first workflow",
},
},
},
],
"document": t {
"dsl": "1.0.0",
"name": "valid-workflow-yaml",
"namespace": "default",
"version": "1.0.0",
},
},
}
`;

exports[`parseWorkflow > returns model and errors for invalid but parseable 'JSON' 1`] = `
{
"errors": [
[Error: 'Workflow' is invalid - The DSL version of the workflow 'undefined' doesn't match the supported version of the SDK '1.0.0'.],
],
"model": t {
"do": t [
t {
"step1": t {
"set": t {
"variable": "my first invalid json workflow",
},
},
},
],
},
}
`;

exports[`parseWorkflow > returns model and errors for invalid but parseable 'YAML' 1`] = `
{
"errors": [
[Error: 'Workflow' is invalid - The DSL version of the workflow 'undefined' doesn't match the supported version of the SDK '1.0.0'.],
],
"model": t {
"do": t [
t {
"step1": t {
"set": t {
"variable": "my first invalid yaml workflow",
},
},
},
],
},
}
`;

exports[`validateWorkflow > returns errors for an invalid workflow 1`] = `
[
[Error: 'Workflow' is invalid - The DSL version of the workflow 'undefined' doesn't match the supported version of the SDK '1.0.0'.],
]
`;
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

import { describe, it, expect } from "vitest";
import { parseWorkflow, validateWorkflow } from "../../src/core";
import {
BASIC_VALID_WORKFLOW_YAML,
BASIC_VALID_WORKFLOW_JSON,
BASIC_INVALID_WORKFLOW_YAML,
BASIC_INVALID_WORKFLOW_JSON,
} from "../fixtures/workflows";
import { Classes, Specification } from "@serverlessworkflow/sdk";

describe("parseWorkflow", () => {
it.each([
{ format: "YAML", input: BASIC_VALID_WORKFLOW_YAML, expectedName: "valid-workflow-yaml" },
{ format: "JSON", input: BASIC_VALID_WORKFLOW_JSON, expectedName: "valid-workflow-json" },
])("parses valid $format and returns model with no errors", ({ input, expectedName }) => {
const result = parseWorkflow(input);
expect(result.errors).toHaveLength(0);
expect(result.model?.document?.name).toBe(expectedName);
expect(result).toMatchSnapshot();
});

it.each([
{ format: "YAML", input: BASIC_INVALID_WORKFLOW_YAML },
{ format: "JSON", input: BASIC_INVALID_WORKFLOW_JSON },
])("returns model and errors for invalid but parseable $format", ({ input }) => {
const result = parseWorkflow(input);
expect(result.model).not.toBeNull();
expect(result.errors).toHaveLength(1);
expect(result).toMatchSnapshot();
});

it.each([
{ description: "empty string", input: "" },
{ description: "non-object YAML", input: "just a string" },
{ description: "numeric YAML", input: "42" },
])("returns null model with error for $description", ({ input }) => {
const result = parseWorkflow(input);
expect(result.model).toBeNull();
expect(result.errors[0].message).toBe("Not a valid workflow object");
});

it("returns null model with errors for unparseable text", () => {
const result = parseWorkflow("}{not yaml or json}{");
expect(result.model).toBeNull();
expect(result.errors).toHaveLength(1);
});
});

describe("validateWorkflow", () => {
it("returns empty array for a valid workflow", () => {
const valid = new Classes.Workflow({
document: { dsl: "1.0.0", name: "valid-workflow", version: "1.0.0", namespace: "default" },
do: [{ step1: { set: { variable: "value" } } }],
}) as Specification.Workflow;
const errors = validateWorkflow(valid);
expect(errors).toHaveLength(0);
});

it("returns errors for an invalid workflow", () => {
const invalid = new Classes.Workflow({
do: [{ step1: { set: { variable: "value" } } }],
}) as Specification.Workflow;
const errors = validateWorkflow(invalid);
expect(errors).toHaveLength(1);
expect(errors).toMatchSnapshot();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
/*
* Copyright 2021-Present The Serverless Workflow Specification Authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

/*
* Workflow test fixtures for parsing, validation, and error handling tests.
* Includes valid and invalid workflow definitions in YAML and JSON formats.
*/

export const BASIC_VALID_WORKFLOW_YAML = `
document:
dsl: 1.0.0
name: valid-workflow-yaml
version: 1.0.0
namespace: default
do:
- step1:
set:
variable: 'my first workflow'
`;

export const BASIC_VALID_WORKFLOW_JSON = JSON.stringify({
document: {
dsl: "1.0.0",
name: "valid-workflow-json",
version: "1.0.0",
namespace: "default",
},
do: [
{
step1: {
set: {
variable: "my first workflow",
},
},
},
],
});

// Missing required 'document' field
export const BASIC_INVALID_WORKFLOW_YAML = `
do:
- step1:
set:
variable: 'my first invalid yaml workflow'
`;

// Missing required 'document' field
export const BASIC_INVALID_WORKFLOW_JSON = JSON.stringify({
do: [
{
step1: {
set: {
variable: "my first invalid json workflow",
},
},
},
],
});
Loading
Loading