Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make do an array #895

Merged
Show file tree
Hide file tree
Changes from 23 commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
0765c3a
feat: update schema
matthias-pichler Jun 11, 2024
3166c14
docs: update examples
matthias-pichler Jun 11, 2024
ff4fb6e
fix: do not allow more properties in tasks
matthias-pichler Jun 12, 2024
27ee947
lint: format examples
matthias-pichler Jun 12, 2024
5ad9f30
refactor: add node: module prefix
matthias-pichler Jun 12, 2024
36bb47d
refactor: update testing to more idiomatic JS
matthias-pichler Jun 12, 2024
2709fb5
refactor: update test file to use .each
matthias-pichler Jun 12, 2024
3b7d4ab
feat: switch to json schema 2020
matthias-pichler Jun 12, 2024
470709a
refactor: rename index.test
matthias-pichler Jun 12, 2024
06fa87a
test: add invalid workflows
matthias-pichler Jun 12, 2024
1e6d28c
refactor: switch back to composite wording
matthias-pichler Jun 12, 2024
270deb1
docs: update dsl
matthias-pichler Jun 12, 2024
f658c7d
refactor: rename branch to fork
matthias-pichler Jun 12, 2024
50b93c2
test: add tests for examples in dsl-reference
matthias-pichler Jun 12, 2024
9f422f5
Merge branch 'main' into matthias-pichler/invalid-do-switch-894
matthias-pichler Jun 12, 2024
b27cb7b
fix: rename composite ctk tests
matthias-pichler Jun 12, 2024
10cab1c
fix: update export heading
matthias-pichler Jun 12, 2024
2bf543b
fox: remove trailing +
matthias-pichler Jun 12, 2024
89d1432
fix: apply suggestions
matthias-pichler Jun 12, 2024
c91754a
refactor: split schema of fork and do
matthias-pichler Jun 12, 2024
51f786b
test: split ctk features
matthias-pichler Jun 12, 2024
5f698ee
docs: split do & fork in dsl
matthias-pichler Jun 12, 2024
b5c71f1
fix: update workflow schema id
matthias-pichler Jun 12, 2024
a2b21f0
docs: remove requirement of 2 subtasks
matthias-pichler Jun 12, 2024
57cf769
Merge branch 'main' into matthias-pichler/invalid-do-switch-894
matthias-pichler Jun 13, 2024
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
14 changes: 13 additions & 1 deletion .ci/validation/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

3 changes: 2 additions & 1 deletion .ci/validation/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
"dependencies": {
"ajv": "^8.12.0",
"ajv-formats": "^2.1.1",
"js-yaml": "^4.1.0"
"js-yaml": "^4.1.0",
"marked": "^13.0.0"
}
}
53 changes: 53 additions & 0 deletions .ci/validation/src/dsl.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
/*
* Copyright 2023-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 { SWSchemaValidator } from "./index";
import fs from "node:fs";
import path from "node:path";
import marked from "marked";

SWSchemaValidator.prepareSchemas();

const dslReferencePath = path.join(
__dirname,
"..",
"..",
"..",
"dsl-reference.md"
);

describe(`Verify every example in the dsl docs`, () => {
const workflows = marked
.lexer(fs.readFileSync(dslReferencePath, SWSchemaValidator.defaultEncoding))
.filter((item): item is marked.Tokens.Code => item.type === "code")
.filter((item) => item.lang === "yaml")
.map((item) => item.text)
.map((text) => SWSchemaValidator.yamlToJSON(text))
.filter((workflow) => typeof workflow === "object")
.filter((workflow) => "document" in workflow)
.filter((workflow) => "dsl" in workflow.document);

test.each(workflows)("$document.name", (workflow) => {
const results = SWSchemaValidator.validateSchema(workflow);
if (results?.errors) {
console.warn(
`Schema validation on workflow ${workflow.document.name} failed with: `,
JSON.stringify(results.errors, null, 2)
);
}
expect(results?.valid).toBeTruthy();
});
});
Original file line number Diff line number Diff line change
Expand Up @@ -15,33 +15,35 @@
*/

import { SWSchemaValidator } from "./index";
import fs from "fs";
import { join } from "path";
import fs from "node:fs";
import path from "node:path";
matthias-pichler marked this conversation as resolved.
Show resolved Hide resolved

SWSchemaValidator.prepareSchemas();

const examplePath = "../../../examples";

describe(`Verify every example in the repository`, () => {
fs.readdirSync(join(__dirname, examplePath), {
encoding: SWSchemaValidator.defaultEncoding,
recursive: false,
withFileTypes: true,
}).forEach((file) => {
if (file.isFile() && file.name.endsWith(".yaml")) {
test(`Example ${file.name}`, () => {
const workflow = SWSchemaValidator.toJSON(
join(__dirname, `${examplePath}/${file.name}`)
);
const results = SWSchemaValidator.validateSchema(workflow);
if (results?.errors != null) {
console.warn(
`Schema validation on ${file.name} failed with: `,
JSON.stringify(results.errors, null, 2)
);
}
expect(results?.valid).toBeTruthy();
});
const examples = fs
.readdirSync(path.join(__dirname, examplePath), {
encoding: SWSchemaValidator.defaultEncoding,
recursive: false,
withFileTypes: true,
})
.filter((file) => file.isFile())
.filter((file) => file.name.endsWith(".yaml"))
.map((file) => file.name);

test.each(examples)("Example %s", (file) => {
matthias-pichler marked this conversation as resolved.
Show resolved Hide resolved
const workflow = SWSchemaValidator.loadAsJSON(
path.join(__dirname, `${examplePath}/${file}`)
);
const results = SWSchemaValidator.validateSchema(workflow);
if (results?.errors) {
console.warn(
`Schema validation on ${file} failed with: `,
JSON.stringify(results.errors, null, 2)
);
}
expect(results?.valid).toBeTruthy();
});
});
52 changes: 30 additions & 22 deletions .ci/validation/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,54 +14,62 @@
* limitations under the License.
*/

import fs from "fs";
import Ajv from "ajv";
import fs from "node:fs";
import Ajv from "ajv/dist/2020";
matthias-pichler marked this conversation as resolved.
Show resolved Hide resolved
import addFormats from "ajv-formats";
import { join } from "path";
import path from "node:path";
import yaml = require("js-yaml");

export module SWSchemaValidator {
const ajv = new Ajv({ strict: false, allowUnionTypes: true });
addFormats(ajv);

const workflowSchemaId =
"https://serverlessworkflow.io/schemas/1.0.0-alpha1/workflow.json";
"https://serverlessworkflow.io/schemas/1.0.0-alpha1/workflow.yaml";
const schemaPath = "../../../schema";
export const defaultEncoding = "utf-8";

export function prepareSchemas() {
fs.readdirSync(join(__dirname, schemaPath), {
const files = fs.readdirSync(path.join(__dirname, schemaPath), {
encoding: defaultEncoding,
recursive: false,
withFileTypes: true,
}).forEach((file) => {
if (file.isFile()) {
ajv.addSchema(syncReadSchema(file.name));
}
});

files
.filter((file) => file.isFile())
.forEach((file) => {
ajv.addSchema(syncReadSchema(file.name));
});
}

function syncReadSchema(filename: string) {
return toJSON(join(__dirname, `${schemaPath}/${filename}`));
function syncReadSchema(filename: string): any {
return loadAsJSON(path.join(__dirname, `${schemaPath}/${filename}`));
}

export function toJSON(filename: string) {
const yamlObj = yaml.load(fs.readFileSync(filename, defaultEncoding), {
export function loadAsJSON(filename: string): any {
return yamlToJSON(fs.readFileSync(filename, defaultEncoding));
}

export function yamlToJSON(yamlStr: string): any {
const yamlObj = yaml.load(yamlStr, {
json: true,
});
return JSON.parse(JSON.stringify(yamlObj, null, 2));
return structuredClone(yamlObj);
matthias-pichler marked this conversation as resolved.
Show resolved Hide resolved
}

export function validateSchema(workflow: JSON) {
export function validateSchema(workflow: Record<string, unknown>) {
matthias-pichler marked this conversation as resolved.
Show resolved Hide resolved
const validate = ajv.getSchema(workflowSchemaId);
if (validate != undefined) {
const isValid = validate(workflow);
return {
valid: isValid,
errors: validate.errors,
};

if (!validate) {
throw new Error(`Failed to validate schema on workflow`);
}
throw new Error(`Failed to validate schema on workflow`);

const isValid = validate(workflow);
return {
valid: isValid,
errors: validate.errors,
};
}
}

Expand Down
43 changes: 43 additions & 0 deletions .ci/validation/src/invalid.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/*
* Copyright 2023-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 { SWSchemaValidator } from "./index";
import fs from "node:fs";
import path from "node:path";

SWSchemaValidator.prepareSchemas();

const invalidPath = "../test/fixtures/invalid";

describe(`Check that invalid workflows are rejected`, () => {
const examples = fs
.readdirSync(path.join(__dirname, invalidPath), {
encoding: SWSchemaValidator.defaultEncoding,
recursive: false,
withFileTypes: true,
})
.filter((file) => file.isFile())
.filter((file) => file.name.endsWith(".yaml"))
.map((file) => file.name);

test.each(examples)("Example %s", (file) => {
const workflow = SWSchemaValidator.loadAsJSON(
path.join(__dirname, `${invalidPath}/${file}`)
);
const results = SWSchemaValidator.validateSchema(workflow);
expect(results?.valid).toBeFalsy();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
document:
dsl: 1.0.0-alpha1
namespace: examples
name: two-tasks-in-one-item
version: 1.0.0-alpha1
do:
- getPet:
call: http
with:
method: get
endpoint: https://petstore.swagger.io/v2/pet/{petId}
foo: bar
14 changes: 14 additions & 0 deletions .ci/validation/test/fixtures/invalid/two-tasks-in-one-item.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
document:
dsl: 1.0.0-alpha1
namespace: examples
name: two-tasks-in-one-item
version: 1.0.0-alpha1
do:
- getPet:
call: http
with:
method: get
endpoint: https://petstore.swagger.io/v2/pet/{petId}
setMessage:
set:
message: "Looking for {petId}"
31 changes: 31 additions & 0 deletions ctk/features/branch.feature
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
Feature: Composite Task
As an implementer of the workflow DSL
I want to ensure that composite tasks can be executed within the workflow
So that my implementation conforms to the expected behavior

# Tests composite tasks With competing concurrent sub tasks
Scenario: Fork Task With Competing Concurrent Sub Tasks
Given a workflow with definition:
"""yaml
document:
dsl: 1.0.0-alpha1
namespace: default
name: fork
do:
- branchWithCompete:
fork:
compete: true
branches:
- setRed:
set:
colors: ${ .colors + ["red"] }
- setGreen:
set:
colors: ${ .colors + ["green"] }
- setBlue:
set:
colors: ${ .colors + ["blue"] }
"""
When the workflow is executed
Then the workflow should complete
And the workflow output should have a 'colors' property containing 1 items
Loading
Loading