From e0316ecc721096a3fef16100db8d79e891952c3d Mon Sep 17 00:00:00 2001 From: Jean-Baptiste Bianchi Date: Wed, 13 Dec 2023 18:52:09 +0100 Subject: [PATCH] Enabled examples README templating Signed-off-by: Jean-Baptiste Bianchi --- .ci/examples-readme-hydration/.gitignore | 1 + .ci/examples-readme-hydration/index.js | 38 + .../package-lock.json | 24 + .ci/examples-readme-hydration/package.json | 15 + .github/workflows/examples-readme-check.yml | 21 + .../workflows/examples-readme-hydration.yml | 63 + examples/README.md | 5431 +++++++++-------- examples/README_TEMPLATE.md | 1829 ++++++ ...ngs.json => accumulate-room-readings.json} | 0 ...t.json => applicant-request-decision.json} | 0 ...il.json => async-function-invocation.json} | 0 ...mer.json => async-subflow-invocation.json} | 0 .../{booklending.json => book-lending.json} | 0 ...ck.json => car-vitals-checks-subflow.json} | 0 ...kcarvitals.json => car-vitals-checks.json} | 0 ...box.json => check-inbox-periodically.json} | 0 ...son => continuing-as-a-new-execution.json} | 0 ...reeting.json => event-based-greeting.json} | 0 ...on => event-based-service-invocation.json} | 0 ...tate.json => event-based-transitions.json} | 0 ...ter.json => filling-a-glass-of-water.json} | 0 ...json => finalize-college-application.json} | 0 ...nbid.json => handle-car-auction-bids.json} | 0 .../{helloworld.json => hello-world.json} | 0 .../{jobmonitoring.json => monitor-job.json} | 0 ....json => monitor-patient-vital-signs.json} | 0 ...board.json => new-patient-onboarding.json} | 0 ...allelexec.json => parallel-execution.json} | 0 ...son => perform-customer-credit-check.json} | 0 ...actions.json => process-transactions.json} | 0 ...isionorders.json => provision-orders.json} | 0 ...rder.json => purchase-order-deadline.json} | 0 ...using-function-and-event-definitions.json} | 0 ...nd-cloudevent-on-workflow-completion.json} | 0 ...oblems.json => solving-math-problems.json} | 0 35 files changed, 4809 insertions(+), 2613 deletions(-) create mode 100644 .ci/examples-readme-hydration/.gitignore create mode 100644 .ci/examples-readme-hydration/index.js create mode 100644 .ci/examples-readme-hydration/package-lock.json create mode 100644 .ci/examples-readme-hydration/package.json create mode 100644 .github/workflows/examples-readme-check.yml create mode 100644 .github/workflows/examples-readme-hydration.yml create mode 100644 examples/README_TEMPLATE.md rename examples/{roomreadings.json => accumulate-room-readings.json} (100%) rename examples/{applicantrequest.json => applicant-request-decision.json} (100%) rename examples/{sendcustomeremail.json => async-function-invocation.json} (100%) rename examples/{onboardcustomer.json => async-subflow-invocation.json} (100%) rename examples/{booklending.json => book-lending.json} (100%) rename examples/{vitalscheck.json => car-vitals-checks-subflow.json} (100%) rename examples/{checkcarvitals.json => car-vitals-checks.json} (100%) rename examples/{checkinbox.json => check-inbox-periodically.json} (100%) rename examples/{notifycustomerworkflow.json => continuing-as-a-new-execution.json} (100%) rename examples/{eventbasedgreeting.json => event-based-greeting.json} (100%) rename examples/{vetappointmentworkflow.json => event-based-service-invocation.json} (100%) rename examples/{eventbasedswitchstate.json => event-based-transitions.json} (100%) rename examples/{fillglassofwater.json => filling-a-glass-of-water.json} (100%) rename examples/{finalizecollegeapplication.json => finalize-college-application.json} (100%) rename examples/{handlecarauctionbid.json => handle-car-auction-bids.json} (100%) rename examples/{helloworld.json => hello-world.json} (100%) rename examples/{jobmonitoring.json => monitor-job.json} (100%) rename examples/{patientvitalsworkflow.json => monitor-patient-vital-signs.json} (100%) rename examples/{patientonboard.json => new-patient-onboarding.json} (100%) rename examples/{parallelexec.json => parallel-execution.json} (100%) rename examples/{customercreditcheck.json => perform-customer-credit-check.json} (100%) rename examples/{customerbankingtransactions.json => process-transactions.json} (100%) rename examples/{provisionorders.json => provision-orders.json} (100%) rename examples/{order.json => purchase-order-deadline.json} (100%) rename examples/{paymentconfirmation.json => reusing-function-and-event-definitions.json} (100%) rename examples/{sendcloudeventonprovision.json => send-cloudevent-on-workflow-completion.json} (100%) rename examples/{solvemathproblems.json => solving-math-problems.json} (100%) diff --git a/.ci/examples-readme-hydration/.gitignore b/.ci/examples-readme-hydration/.gitignore new file mode 100644 index 00000000..b512c09d --- /dev/null +++ b/.ci/examples-readme-hydration/.gitignore @@ -0,0 +1 @@ +node_modules \ No newline at end of file diff --git a/.ci/examples-readme-hydration/index.js b/.ci/examples-readme-hydration/index.js new file mode 100644 index 00000000..716c640d --- /dev/null +++ b/.ci/examples-readme-hydration/index.js @@ -0,0 +1,38 @@ +const fs = require('fs'); +const path = require('path'); +const YAML = require('yaml'); + +const examplesPath = path.resolve(__dirname, '../../examples/'); +const readMeTemplatePath = path.resolve(examplesPath, 'README_TEMPLATE.md'); +const readMeOutputPath = path.resolve(examplesPath, 'README.md'); +const extractor = //g; +const disclaimer = ` +`; + +(async () => { + let readMe = await fs.promises.readFile(readMeTemplatePath, 'utf8'); + const includes = readMe.matchAll(extractor); + for await(let include of includes) { + const fileName = include[1]; + const format = include[2]; + let fileContent = await fs.promises.readFile(path.resolve(examplesPath, fileName), 'utf8'); + if (format === 'yaml') { + try { + const schema = JSON.parse(fileContent); + fileContent = YAML.stringify(schema); + } + catch(ex) { + console.error('Enable to parse JSON or convert it to YAML, output as it is.', ex); + } + } + readMe = readMe.replace(include[0], fileContent); + }; + await fs.promises.writeFile(readMeOutputPath, disclaimer + readMe, { encoding: 'utf8', flag: 'w' }); +})(); \ No newline at end of file diff --git a/.ci/examples-readme-hydration/package-lock.json b/.ci/examples-readme-hydration/package-lock.json new file mode 100644 index 00000000..f2b43996 --- /dev/null +++ b/.ci/examples-readme-hydration/package-lock.json @@ -0,0 +1,24 @@ +{ + "name": "examples-readme-hydration", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "examples-readme-hydration", + "version": "0.1.0", + "license": "ISC", + "dependencies": { + "yaml": "^2.3.4" + } + }, + "node_modules/yaml": { + "version": "2.3.4", + "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.3.4.tgz", + "integrity": "sha512-8aAvwVUSHpfEqTQ4w/KMlf3HcRdt50E5ODIQJBw1fQ5RL34xabzxtUlzTXVqc4rkZsPbvrXKWnABCD7kWSmocA==", + "engines": { + "node": ">= 14" + } + } + } +} diff --git a/.ci/examples-readme-hydration/package.json b/.ci/examples-readme-hydration/package.json new file mode 100644 index 00000000..0607ff7b --- /dev/null +++ b/.ci/examples-readme-hydration/package.json @@ -0,0 +1,15 @@ +{ + "name": "examples-readme-hydration", + "version": "0.1.0", + "description": "Builds ./examples/README.md based on ./examples/README_TEMPLATE.md", + "main": "src/index.js", + "scripts": { + "start": "node ./index.js" + }, + "keywords": ["cncf", "serverless", "workflow", "specification"], + "author": "CNCF Serverless Workflow Specification", + "license": "ISC", + "dependencies": { + "yaml": "^2.3.4" + } +} diff --git a/.github/workflows/examples-readme-check.yml b/.github/workflows/examples-readme-check.yml new file mode 100644 index 00000000..da149e4b --- /dev/null +++ b/.github/workflows/examples-readme-check.yml @@ -0,0 +1,21 @@ +name: Check examples README manual updates + +on: + pull_request: + paths: + - 'examples/README.md' + +jobs: + verification: + runs-on: ubuntu-latest + steps: + - if: contains(github.head_ref, 'automation-examples-readme') + name: pass + run: echo "The update is made by the bot, as expected." + + - if: contains(github.head_ref, 'automation-examples-readme') == false + name: fail + run: | + echo "The file examples/README.md should not be manually edited !" + echo "Please update examples/README_TEMPLATE.md instead" + exit 1 diff --git a/.github/workflows/examples-readme-hydration.yml b/.github/workflows/examples-readme-hydration.yml new file mode 100644 index 00000000..e9e2fb47 --- /dev/null +++ b/.github/workflows/examples-readme-hydration.yml @@ -0,0 +1,63 @@ +name: Examples README hydration + +on: + pull_request: + types: + - closed + branches: [ 'main'] + paths: + - 'examples/**/*' + - '!examples/README.md' + +jobs: + build: + if: github.repository == 'serverlessworkflow/specification' && github.event.pull_request.merged == true + runs-on: ubuntu-latest + permissions: + contents: write + pull-requests: write + steps: + - uses: actions/checkout@v4 + with: + token: ${{ secrets.BOT_PAT }} + + + - uses: actions/setup-node@v4 + with: + node-version: 21 + + - name: Import GPG key + id: import-gpg + uses: crazy-max/ghaction-import-gpg@v6 + with: + gpg_private_key: ${{ secrets.BOT_GPG_PRIVATE_KEY }} + git_config_global: true + git_user_signingkey: true + git_commit_gpgsign: true + git_tag_gpgsign: true + + - run: | + set -e + + # Reset origin + git remote set-url origin https://${{ secrets.BOT_USERNAME }}:${{ secrets.BOT_PAT }}@github.com/${{ github.repository }}.git + git checkout ${{ github.ref_name }} + + # Create a new git branch + git checkout -b automation-examples-readme-${{ github.event.pull_request.number }} + + # Install & run JS scripts + cd .ci/examples-readme-hydration/ + npm ci + npm start + + # Commit & push changes + git config user.name '${{ secrets.BOT_USERNAME }}' + git config user.email '${{ secrets.BOT_EMAIL }}' + git commit -S -a -m 'Rebuilt examples README.md' + git push origin automation-examples-readme-${{ github.event.pull_request.number }} + + # Create a PR on GH + gh pr create -B main -H automation-examples-readme-${{ github.event.pull_request.number }} --title '[From CI] Rebuilt examples README' --body 'Automatic hydration of examples README.md' + env: + GITHUB_TOKEN: ${{ secrets.BOT_PAT }} diff --git a/examples/README.md b/examples/README.md index 0bf7f396..1d815d42 100644 --- a/examples/README.md +++ b/examples/README.md @@ -1,3 +1,7 @@ + # Examples Provides Serverless Workflow language examples @@ -66,41 +70,40 @@ data output, which is: ```json { -"id": "helloworld", -"version": "1.0.0", -"specVersion": "0.8", -"name": "Hello World Workflow", -"description": "Inject Hello World", -"start": "Hello State", -"states":[ - { - "name":"Hello State", - "type":"inject", - "data": { - "result": "Hello World!" - }, - "end": true - } -] -} -``` + "id": "helloworld", + "version": "1.0.0", + "specVersion": "0.8", + "name": "Hello World Workflow", + "description": "Inject Hello World", + "start": "hello-state", + "states": [ + { + "name": "hello-state", + "type": "inject", + "data": { + "result": "Hello World!" + }, + "end": true + } + ] +}``` ```yaml id: helloworld -version: '1.0.0' -specVersion: '0.8' +version: 1.0.0 +specVersion: "0.8" name: Hello World Workflow description: Inject Hello World -start: Hello State +start: hello-state states: -- name: Hello State - type: inject - data: - result: Hello World! - end: true + - name: hello-state + type: inject + data: + result: Hello World! + end: true ``` @@ -150,65 +153,68 @@ Which is added to the states data and becomes the workflow data output. ```json { -"id": "greeting", -"version": "1.0.0", -"specVersion": "0.8", -"name": "Greeting Workflow", -"description": "Greet Someone", -"start": "Greet", -"functions": [ - { - "name": "greetingFunction", - "operation": "file://myapis/greetingapis.json#greeting" - } -], -"states":[ - { - "name":"Greet", - "type":"operation", - "actions":[ - { - "functionRef": { - "refName": "greetingFunction", - "arguments": { - "name": "${ .person.name }" - } - }, - "actionDataFilter": { - "results": "${ {greeting: .greeting} }" - } + "id": "greeting", + "version": "1.0.0", + "specVersion": "0.8", + "name": "greeting-workflow", + "description": "Greet Someone", + "start": "greet", + "functions": [ + { + "name": "greeting-function", + "type": "openapi", + "operation": "file://myapis/greetingapis.json#greeting" } - ], - "end": true - } -] -} -``` + ], + "states": [ + { + "name": "greet", + "type": "operation", + "actions": [ + { + "name": "greet-action", + "functionRef": { + "refName": "greeting-function", + "arguments": { + "name": "${ .person.name }" + } + }, + "actionDataFilter": { + "results": "${ {greeting: .greeting} }" + } + } + ], + "end": true + } + ] +}``` ```yaml id: greeting -version: '1.0.0' -specVersion: '0.8' -name: Greeting Workflow +version: 1.0.0 +specVersion: "0.8" +name: greeting-workflow description: Greet Someone -start: Greet +start: greet functions: -- name: greetingFunction - operation: file://myapis/greetingapis.json#greeting + - name: greeting-function + type: openapi + operation: file://myapis/greetingapis.json#greeting states: -- name: Greet - type: operation - actions: - - functionRef: - refName: greetingFunction - arguments: - name: "${ .person.name }" - actionDataFilter: - results: "${ {greeting: .greeting} }" - end: true + - name: greet + type: operation + actions: + - name: greet-action + functionRef: + refName: greeting-function + arguments: + name: ${ .person.name } + actionDataFilter: + results: "${ {greeting: .greeting} }" + end: true ``` @@ -295,89 +301,94 @@ filters what is selected to be the state data output which then becomes the work ```json { -"id": "eventbasedgreeting", -"version": "1.0.0", -"specVersion": "0.8", -"name": "Event Based Greeting Workflow", -"description": "Event Based Greeting", -"start": "Greet", -"events": [ - { - "name": "GreetingEvent", - "type": "greetingEventType", - "source": "greetingEventSource" - } -], -"functions": [ - { - "name": "greetingFunction", - "operation": "file://myapis/greetingapis.json#greeting" - } -], -"states":[ - { - "name":"Greet", - "type":"event", - "onEvents": [{ - "eventRefs": ["GreetingEvent"], - "eventDataFilter": { - "data": "${ .greet }", - "toStateData": "${ .greet }" - }, - "actions":[ - { - "functionRef": { - "refName": "greetingFunction", - "arguments": { - "name": "${ .greet.name }" - } - } - } - ] - }], - "stateDataFilter": { - "output": "${ .payload.greeting }" - }, - "end": true - } -] -} -``` + "id": "eventbasedgreeting", + "version": "1.0.0", + "specVersion": "0.8", + "name": "Event Based Greeting Workflow", + "description": "Event Based Greeting", + "start": "greet", + "events": [ + { + "name": "greeting-event", + "type": "greetingEventType", + "source": "greetingEventSource" + } + ], + "functions": [ + { + "name": "greeting-function", + "operation": "file://myapis/greetingapis.json#greeting" + } + ], + "states": [ + { + "name": "greet", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "greeting-event" + ], + "eventDataFilter": { + "data": "${ .greet }", + "toStateData": "${ .greet }" + }, + "actions": [ + { + "name": "greet-action", + "functionRef": { + "refName": "greeting-function", + "arguments": { + "name": "${ .greet.name }" + } + } + } + ] + } + ], + "stateDataFilter": { + "output": "${ .payload.greeting }" + }, + "end": true + } + ] +}``` ```yaml id: eventbasedgreeting -version: '1.0.0' -specVersion: '0.8' +version: 1.0.0 +specVersion: "0.8" name: Event Based Greeting Workflow description: Event Based Greeting -start: Greet +start: greet events: -- name: GreetingEvent - type: greetingEventType - source: greetingEventSource + - name: greeting-event + type: greetingEventType + source: greetingEventSource functions: -- name: greetingFunction - operation: file://myapis/greetingapis.json#greeting + - name: greeting-function + operation: file://myapis/greetingapis.json#greeting states: -- name: Greet - type: event - onEvents: - - eventRefs: - - GreetingEvent - eventDataFilter: - data: "${ .greet }" - toStateData: "${ .greet }" - actions: - - functionRef: - refName: greetingFunction - arguments: - name: "${ .greet.name }" - stateDataFilter: - output: "${ .payload.greeting }" - end: true + - name: greet + type: event + onEvents: + - eventRefs: + - greeting-event + eventDataFilter: + data: ${ .greet } + toStateData: ${ .greet } + actions: + - name: greet-action + functionRef: + refName: greeting-function + arguments: + name: ${ .greet.name } + stateDataFilter: + output: ${ .payload.greeting } + end: true ``` @@ -423,71 +434,72 @@ result of the workflow execution. ```json { -"id": "solvemathproblems", -"version": "1.0.0", -"specVersion": "0.8", -"name": "Solve Math Problems Workflow", -"description": "Solve math problems", -"start": "Solve", -"functions": [ -{ - "name": "solveMathExpressionFunction", - "operation": "http://myapis.org/mapthapis.json#solveExpression" -} -], -"states":[ -{ - "name":"Solve", - "type":"foreach", - "inputCollection": "${ .expressions }", - "iterationParam": "singleexpression", - "outputCollection": "${ .results }", - "actions":[ - { - "functionRef": { - "refName": "solveMathExpressionFunction", - "arguments": { - "expression": "${ .singleexpression }" - } - } - } - ], - "stateDataFilter": { - "output": "${ .results }" - }, - "end": true -} -] -} -``` + "id": "solvemathproblems", + "version": "1.0.0", + "specVersion": "0.8", + "name": "solve-math-problems-workflow", + "description": "Solve math problems", + "start": "solve", + "functions": [ + { + "name": "solve-math-exp-func", + "operation": "http://myapis.org/mapthapis.json#solveExpression" + } + ], + "states": [ + { + "name": "solve", + "type": "foreach", + "inputCollection": "${ .expressions }", + "iterationParam": "singleexpression", + "outputCollection": "${ .results }", + "actions": [ + { + "name": "solve-action", + "functionRef": { + "refName": "solve-math-exp-func", + "arguments": { + "expression": "${ .singleexpression }" + } + } + } + ], + "stateDataFilter": { + "output": "${ .results }" + }, + "end": true + } + ] +}``` ```yaml id: solvemathproblems -version: '1.0.0' -specVersion: '0.8' -name: Solve Math Problems Workflow +version: 1.0.0 +specVersion: "0.8" +name: solve-math-problems-workflow description: Solve math problems -start: Solve +start: solve functions: -- name: solveMathExpressionFunction - operation: http://myapis.org/mapthapis.json#solveExpression + - name: solve-math-exp-func + operation: http://myapis.org/mapthapis.json#solveExpression states: -- name: Solve - type: foreach - inputCollection: "${ .expressions }" - iterationParam: singleexpression - outputCollection: "${ .results }" - actions: - - functionRef: - refName: solveMathExpressionFunction - arguments: - expression: "${ .singleexpression }" - stateDataFilter: - output: "${ .results }" - end: true + - name: solve + type: foreach + inputCollection: ${ .expressions } + iterationParam: singleexpression + outputCollection: ${ .results } + actions: + - name: solve-action + functionRef: + refName: solve-math-exp-func + arguments: + expression: ${ .singleexpression } + stateDataFilter: + output: ${ .results } + end: true ``` @@ -520,59 +532,66 @@ to finish execution before it can transition (end workflow execution in this cas ```json { -"id": "parallelexec", -"version": "1.0.0", -"specVersion": "0.8", -"name": "Parallel Execution Workflow", -"description": "Executes two branches in parallel", -"start": "ParallelExec", -"states":[ - { - "name": "ParallelExec", - "type": "parallel", - "completionType": "allOf", - "branches": [ - { - "name": "ShortDelayBranch", - "actions": [{ - "subFlowRef": "shortdelayworkflowid" - }] - }, + "id": "parallelexec", + "version": "1.0.0", + "specVersion": "0.8", + "name": "parallel-execution", + "description": "Executes two branches in parallel", + "start": "parallelexec", + "states": [ { - "name": "LongDelayBranch", - "actions": [{ - "subFlowRef": "longdelayworkflowid" - }] + "name": "parallelexec", + "type": "parallel", + "completionType": "allOf", + "branches": [ + { + "name": "short-delay-branch", + "actions": [ + { + "name": "short-delay-action", + "subFlowRef": "shortdelayworkflowid" + } + ] + }, + { + "name": "long-delay-branch", + "actions": [ + { + "name": "short-delay-action", + "subFlowRef": "longdelayworkflowid" + } + ] + } + ], + "end": true } - ], - "end": true - } -] -} -``` + ] +}``` ```yaml id: parallelexec -version: '1.0.0' -specVersion: '0.8' -name: Parallel Execution Workflow +version: 1.0.0 +specVersion: "0.8" +name: parallel-execution description: Executes two branches in parallel -start: ParallelExec +start: parallelexec states: -- name: ParallelExec - type: parallel - completionType: allOf - branches: - - name: ShortDelayBranch - actions: - - subFlowRef: shortdelayworkflowid - - name: LongDelayBranch - actions: - - subFlowRef: longdelayworkflowid - end: true + - name: parallelexec + type: parallel + completionType: allOf + branches: + - name: short-delay-branch + actions: + - name: short-delay-action + subFlowRef: shortdelayworkflowid + - name: long-delay-branch + actions: + - name: short-delay-action + subFlowRef: longdelayworkflowid + end: true ``` @@ -610,62 +629,63 @@ does not wait for its results. ```json { - "id": "sendcustomeremail", - "version": "1.0.0", - "specVersion": "0.8", - "name": "Send customer email workflow", - "description": "Send email to a customer", - "start": "Send Email", - "functions": [ - { - "name": "emailFunction", - "operation": "file://myapis/emailapis.json#sendEmail" - } - ], - "states":[ - { - "name":"Send Email", - "type":"operation", - "actions":[ - { - "functionRef": { - "invoke": "async", - "refName": "emailFunction", - "arguments": { - "customer": "${ .customer }" - } - } - } - ], - "end": true - } - ] -} -``` + "id": "sendcustomeremail", + "version": "1.0.0", + "specVersion": "0.8", + "name": "send-customer-email-workflow", + "description": "Send email to a customer", + "start": "send-email", + "functions": [ + { + "name": "email-function", + "operation": "file://myapis/emailapis.json#sendEmail" + } + ], + "states": [ + { + "name": "send-email", + "type": "operation", + "actions": [ + { + "name": "send-email-action", + "functionRef": { + "invoke": "async", + "refName": "email-function", + "arguments": { + "customer": "${ .customer }" + } + } + } + ], + "end": true + } + ] +}``` ```yaml id: sendcustomeremail -version: '1.0.0' -specVersion: '0.8' -name: Send customer email workflow +version: 1.0.0 +specVersion: "0.8" +name: send-customer-email-workflow description: Send email to a customer -start: Send Email +start: send-email functions: - - name: emailFunction - operation: file://myapis/emailapis.json#sendEmail + - name: email-function + operation: file://myapis/emailapis.json#sendEmail states: - - name: Send Email - type: operation - actions: - - functionRef: - invoke: async - refName: emailFunction - arguments: - customer: "${ .customer }" - end: true + - name: send-email + type: operation + actions: + - name: send-email-action + functionRef: + invoke: async + refName: email-function + arguments: + customer: ${ .customer } + end: true ``` @@ -701,52 +721,53 @@ property to `continue`. ```json { - "id": "onboardcustomer", - "version": "1.0.0", - "specVersion": "0.8", - "name": "Onboard Customer", - "description": "Onboard a Customer", - "start": "Onboard", - "states":[ - { - "name":"Onboard", - "type":"operation", - "actions":[ - { - "subFlowRef": { - "invoke": "async", - "onParentComplete": "continue", - "workflowId": "customeronboardingworkflow", - "version": "1.0" - } - } - ], - "end": true - } - ] -} -``` + "id": "onboardcustomer", + "version": "1.0.0", + "specVersion": "0.8", + "name": "onboard-customer", + "description": "Onboard a Customer", + "start": "onboard", + "states": [ + { + "name": "onboard", + "type": "operation", + "actions": [ + { + "name": "onboard-action", + "subFlowRef": { + "invoke": "async", + "onParentComplete": "continue", + "workflowId": "customeronboardingworkflow", + "version": "1.0.0" + } + } + ], + "end": true + } + ] +}``` ```yaml id: onboardcustomer -version: '1.0.0' -specVersion: '0.8' -name: Onboard Customer +version: 1.0.0 +specVersion: "0.8" +name: onboard-customer description: Onboard a Customer -start: Onboard +start: onboard states: - - name: Onboard - type: operation - actions: - - subFlowRef: - invoke: async - onParentComplete: continue - workflowId: customeronboardingworkflow - version: '1.0.0' - end: true + - name: onboard + type: operation + actions: + - name: onboard-action + subFlowRef: + invoke: async + onParentComplete: continue + workflowId: customeronboardingworkflow + version: 1.0.0 + end: true ``` @@ -783,123 +804,132 @@ period, the workflow transitions to the "HandleNoVisaDecision" state. ```json { -"id": "eventbasedswitchstate", -"version": "1.0.0", -"specVersion": "0.8", -"name": "Event Based Switch Transitions", -"description": "Event Based Switch Transitions", -"start": "CheckVisaStatus", -"events": [ -{ - "name": "visaApprovedEvent", - "type": "VisaApproved", - "source": "visaCheckSource" -}, -{ - "name": "visaRejectedEvent", - "type": "VisaRejected", - "source": "visaCheckSource" -} -], -"states":[ - { - "name":"CheckVisaStatus", - "type":"switch", - "eventConditions": [ + "id": "eventbasedswitchstate", + "version": "1.0.0", + "specVersion": "0.8", + "name": "event-based-switch-transitions", + "description": "Event Based Switch Transitions", + "start": "checkvisastatus", + "events": [ { - "eventRef": "visaApprovedEvent", - "transition": "HandleApprovedVisa" + "name": "visa-approved-event", + "type": "VisaApproved", + "source": "visaCheckSource" }, { - "eventRef": "visaRejectedEvent", - "transition": "HandleRejectedVisa" + "name": "visa-rejected-event", + "type": "VisaRejected", + "source": "visaCheckSource" } - ], - "timeouts": { - "eventTimeout": "PT1H" - }, - "defaultCondition": { - "transition": "HandleNoVisaDecision" - } - }, - { - "name": "HandleApprovedVisa", - "type": "operation", - "actions": [ - { - "subFlowRef": "handleApprovedVisaWorkflowID" - } ], - "end": true - }, - { - "name": "HandleRejectedVisa", - "type": "operation", - "actions": [ + "states": [ { - "subFlowRef": "handleRejectedVisaWorkflowID" - } - ], - "end": true - }, - { - "name": "HandleNoVisaDecision", - "type": "operation", - "actions": [ + "name": "checkvisastatus", + "type": "switch", + "eventConditions": [ + { + "eventRef": "visa-approved-event", + "transition": "handle-approved-visa", + "name": "approved-condition" + }, + { + "eventRef": "visa-rejected-event", + "transition": "handle-rejected-visa", + "name": "rejected-condition" + } + ], + "timeouts": { + "eventTimeout": "PT1H" + }, + "defaultCondition": { + "transition": "handle-no-visa-decision" + } + }, { - "subFlowRef": "handleNoVisaDecisionWorkflowId" - } - ], - "end": true - } -] -} -``` - - - - -```yaml -id: eventbasedswitchstate -version: '1.0.0' -specVersion: '0.8' -name: Event Based Switch Transitions + "name": "handle-approved-visa", + "type": "operation", + "actions": [ + { + "name": "handle-approved-action", + "subFlowRef": "handleApprovedVisaWorkflowID" + } + ], + "end": true + }, + { + "name": "handle-rejected-visa", + "type": "operation", + "actions": [ + { + "name": "handle-rejected-action", + "subFlowRef": "handleRejectedVisaWorkflowID" + } + ], + "end": true + }, + { + "name": "handle-no-visa-decision", + "type": "operation", + "actions": [ + { + "name": "handle-novisa-action", + "subFlowRef": "handleNoVisaDecisionWorkflowId" + } + ], + "end": true + } + ] +}``` + + + + +```yaml +id: eventbasedswitchstate +version: 1.0.0 +specVersion: "0.8" +name: event-based-switch-transitions description: Event Based Switch Transitions -start: CheckVisaStatus +start: checkvisastatus events: -- name: visaApprovedEvent - type: VisaApproved - source: visaCheckSource -- name: visaRejectedEvent - type: VisaRejected - source: visaCheckSource + - name: visa-approved-event + type: VisaApproved + source: visaCheckSource + - name: visa-rejected-event + type: VisaRejected + source: visaCheckSource states: -- name: CheckVisaStatus - type: switch - eventConditions: - - eventRef: visaApprovedEvent - transition: HandleApprovedVisa - - eventRef: visaRejectedEvent - transition: HandleRejectedVisa - timeouts: - eventTimeout: PT1H - defaultCondition: - transition: HandleNoVisaDecision -- name: HandleApprovedVisa - type: operation - actions: - - subFlowRef: handleApprovedVisaWorkflowID - end: true -- name: HandleRejectedVisa - type: operation + - name: checkvisastatus + type: switch + eventConditions: + - eventRef: visa-approved-event + transition: handle-approved-visa + name: approved-condition + - eventRef: visa-rejected-event + transition: handle-rejected-visa + name: rejected-condition + timeouts: + eventTimeout: PT1H + defaultCondition: + transition: handle-no-visa-decision + - name: handle-approved-visa + type: operation actions: - - subFlowRef: handleRejectedVisaWorkflowID - end: true -- name: HandleNoVisaDecision - type: operation + - name: handle-approved-action + subFlowRef: handleApprovedVisaWorkflowID + end: true + - name: handle-rejected-visa + type: operation actions: - - subFlowRef: handleNoVisaDecisionWorkflowId - end: true + - name: handle-rejected-action + subFlowRef: handleRejectedVisaWorkflowID + end: true + - name: handle-no-visa-decision + type: operation + actions: + - name: handle-novisa-action + subFlowRef: handleNoVisaDecisionWorkflowId + end: true ``` @@ -945,103 +975,110 @@ If the applicants age is over 18 we start the application (subflow action). Othe ```json { - "id": "applicantrequest", - "version": "1.0.0", - "specVersion": "0.8", - "name": "Applicant Request Decision Workflow", - "description": "Determine if applicant request is valid", - "start": "CheckApplication", - "functions": [ - { - "name": "sendRejectionEmailFunction", - "operation": "http://myapis.org/applicationapi.json#emailRejection" - } - ], - "states":[ - { - "name":"CheckApplication", - "type":"switch", - "dataConditions": [ - { - "condition": "${ .applicants | .age >= 18 }", - "transition": "StartApplication" - }, - { - "condition": "${ .applicants | .age < 18 }", - "transition": "RejectApplication" + "id": "applicantrequest", + "version": "1.0.0", + "specVersion": "0.8", + "name": "applicant-request-decision-workflow", + "description": "Determine if applicant request is valid", + "start": "check-application", + "functions": [ + { + "name": "send-rejection-email-function", + "operation": "http://myapis.org/applicationapi.json#emailRejection" + } + ], + "states": [ + { + "name": "check-application", + "type": "switch", + "dataConditions": [ + { + "condition": "${ .applicants | .age >= 18 }", + "transition": "start-application", + "name": "adult-condition" + }, + { + "condition": "${ .applicants | .age < 18 }", + "transition": "reject-application", + "name": "minor-condition" + } + ], + "defaultCondition": { + "transition": "reject-application" } - ], - "defaultCondition": { - "transition": "RejectApplication" - } - }, - { - "name": "StartApplication", - "type": "operation", - "actions": [ - { - "subFlowRef": "startApplicationWorkflowId" - } - ], - "end": true - }, - { - "name":"RejectApplication", - "type":"operation", - "actionMode":"sequential", - "actions":[ - { - "functionRef": { - "refName": "sendRejectionEmailFunction", - "arguments": { - "applicant": "${ .applicant }" - } - } - } - ], - "end": true - } - ] -} -``` + }, + { + "name": "start-application", + "type": "operation", + "actions": [ + { + "name": "start-app-action", + "subFlowRef": "startApplicationWorkflowId" + } + ], + "end": true + }, + { + "name": "reject-application", + "type": "operation", + "actionMode": "sequential", + "actions": [ + { + "name": "send-reject-action", + "functionRef": { + "refName": "send-rejection-email-function", + "arguments": { + "applicant": "${ .applicant }" + } + } + } + ], + "end": true + } + ] +}``` ```yaml id: applicantrequest -version: '1.0.0' -specVersion: '0.8' -name: Applicant Request Decision Workflow +version: 1.0.0 +specVersion: "0.8" +name: applicant-request-decision-workflow description: Determine if applicant request is valid -start: CheckApplication +start: check-application functions: -- name: sendRejectionEmailFunction - operation: http://myapis.org/applicationapi.json#emailRejection + - name: send-rejection-email-function + operation: http://myapis.org/applicationapi.json#emailRejection states: -- name: CheckApplication - type: switch - dataConditions: - - condition: "${ .applicants | .age >= 18 }" - transition: StartApplication - - condition: "${ .applicants | .age < 18 }" - transition: RejectApplication - defaultCondition: - transition: RejectApplication -- name: StartApplication - type: operation - actions: - - subFlowRef: startApplicationWorkflowId - end: true -- name: RejectApplication - type: operation - actionMode: sequential - actions: - - functionRef: - refName: sendRejectionEmailFunction - arguments: - applicant: "${ .applicant }" - end: true + - name: check-application + type: switch + dataConditions: + - condition: ${ .applicants | .age >= 18 } + transition: start-application + name: adult-condition + - condition: ${ .applicants | .age < 18 } + transition: reject-application + name: minor-condition + defaultCondition: + transition: reject-application + - name: start-application + type: operation + actions: + - name: start-app-action + subFlowRef: startApplicationWorkflowId + end: true + - name: reject-application + type: operation + actionMode: sequential + actions: + - name: send-reject-action + functionRef: + refName: send-rejection-email-function + arguments: + applicant: ${ .applicant } + end: true ``` @@ -1089,163 +1126,172 @@ The data output of the workflow contains the information of the exception caught ```json { -"id": "provisionorders", -"version": "1.0.0", -"specVersion": "0.8", -"name": "Provision Orders", -"description": "Provision Orders and handle errors thrown", -"start": "ProvisionOrder", -"functions": [ - { - "name": "provisionOrderFunction", - "operation": "http://myapis.org/provisioningapi.json#doProvision" - } -], -"errors": [ - { - "name": "Missing order id" - }, - { - "name": "Missing order item" - }, - { - "name": "Missing order quantity" - } -], -"states":[ - { - "name":"ProvisionOrder", - "type":"operation", - "actionMode":"sequential", - "actions":[ - { - "functionRef": { - "refName": "provisionOrderFunction", - "arguments": { - "order": "${ .order }" - } - } - } + "id": "provisionorders", + "version": "1.0.0", + "specVersion": "0.8", + "name": "provision-orders", + "description": "Provision Orders and handle errors thrown", + "start": "provision-order", + "functions": [ + { + "name": "provision-order-function", + "operation": "http://myapis.org/provisioningapi.json#doProvision" + } ], - "stateDataFilter": { - "output": "${ .exceptions }" - }, - "transition": "ApplyOrder", - "onErrors": [ - { - "errorRef": "Missing order id", - "transition": "MissingId" - }, - { - "errorRef": "Missing order item", - "transition": "MissingItem" - }, - { - "errorRef": "Missing order quantity", - "transition": "MissingQuantity" - } + "errors": [ + { + "name": "missing-order-id" + }, + { + "name": "missing-order-item" + }, + { + "name": "missing-order-quantity" + } + ], + "states": [ + { + "name": "provision-order", + "type": "operation", + "actionMode": "sequential", + "actions": [ + { + "name": "provision-action", + "functionRef": { + "refName": "provision-order-function", + "arguments": { + "order": "${ .order }" + } + } + } + ], + "stateDataFilter": { + "output": "${ .exceptions }" + }, + "transition": "apply-order", + "onErrors": [ + { + "errorRef": "missing-order-id", + "transition": "missing-id" + }, + { + "errorRef": "missing-order-item", + "transition": "missing-item" + }, + { + "errorRef": "missing-order-quantity", + "transition": "missing-quantity" + } + ] + }, + { + "name": "missing-id", + "type": "operation", + "actions": [ + { + "name": "missing-action", + "subFlowRef": "handleMissingIdExceptionWorkflow" + } + ], + "end": true + }, + { + "name": "missing-item", + "type": "operation", + "actions": [ + { + "name": "missing-item", + "subFlowRef": "handleMissingItemExceptionWorkflow" + } + ], + "end": true + }, + { + "name": "missing-quantity", + "type": "operation", + "actions": [ + { + "name": "missing-quantity", + "subFlowRef": "handleMissingQuantityExceptionWorkflow" + } + ], + "end": true + }, + { + "name": "apply-order", + "type": "operation", + "actions": [ + { + "name": "apply-order", + "subFlowRef": "applyOrderWorkflowId" + } + ], + "end": true + } ] -}, -{ - "name": "MissingId", - "type": "operation", - "actions": [ - { - "subFlowRef": "handleMissingIdExceptionWorkflow" - } - ], - "end": true -}, -{ - "name": "MissingItem", - "type": "operation", - "actions": [ - { - "subFlowRef": "handleMissingItemExceptionWorkflow" - } - ], - "end": true -}, -{ - "name": "MissingQuantity", - "type": "operation", - "actions": [ - { - "subFlowRef": "handleMissingQuantityExceptionWorkflow" - } - ], - "end": true -}, -{ - "name": "ApplyOrder", - "type": "operation", - "actions": [ - { - "subFlowRef": "applyOrderWorkflowId" - } - ], - "end": true -} -] -} -``` +}``` ```yaml id: provisionorders -version: '1.0.0' -specVersion: '0.8' -name: Provision Orders +version: 1.0.0 +specVersion: "0.8" +name: provision-orders description: Provision Orders and handle errors thrown -start: ProvisionOrder +start: provision-order functions: - - name: provisionOrderFunction - operation: http://myapis.org/provisioningapi.json#doProvision + - name: provision-order-function + operation: http://myapis.org/provisioningapi.json#doProvision errors: - - name: Missing order id - - name: Missing order item - - name: Missing order quantity + - name: missing-order-id + - name: missing-order-item + - name: missing-order-quantity states: - - name: ProvisionOrder - type: operation - actionMode: sequential - actions: - - functionRef: - refName: provisionOrderFunction - arguments: - order: "${ .order }" - stateDataFilter: - output: "${ .exceptions }" - transition: ApplyOrder - onErrors: - - errorRef: Missing order id - transition: MissingId - - errorRef: Missing order item - transition: MissingItem - - errorRef: Missing order quantity - transition: MissingQuantity - - name: MissingId - type: operation - actions: - - subFlowRef: handleMissingIdExceptionWorkflow - end: true - - name: MissingItem - type: operation - actions: - - subFlowRef: handleMissingItemExceptionWorkflow - end: true - - name: MissingQuantity - type: operation - actions: - - subFlowRef: handleMissingQuantityExceptionWorkflow - end: true - - name: ApplyOrder - type: operation - actions: - - subFlowRef: applyOrderWorkflowId - end: true + - name: provision-order + type: operation + actionMode: sequential + actions: + - name: provision-action + functionRef: + refName: provision-order-function + arguments: + order: ${ .order } + stateDataFilter: + output: ${ .exceptions } + transition: apply-order + onErrors: + - errorRef: missing-order-id + transition: missing-id + - errorRef: missing-order-item + transition: missing-item + - errorRef: missing-order-quantity + transition: missing-quantity + - name: missing-id + type: operation + actions: + - name: missing-action + subFlowRef: handleMissingIdExceptionWorkflow + end: true + - name: missing-item + type: operation + actions: + - name: missing-item + subFlowRef: handleMissingItemExceptionWorkflow + end: true + - name: missing-quantity + type: operation + actions: + - name: missing-quantity + subFlowRef: handleMissingQuantityExceptionWorkflow + end: true + - name: apply-order + type: operation + actions: + - name: apply-order + subFlowRef: applyOrderWorkflowId + end: true ``` @@ -1285,211 +1331,222 @@ In the case job submission raises a runtime error, we transition to an Operation ```json { - "id": "jobmonitoring", - "version": "1.0.0", - "specVersion": "0.8", - "name": "Job Monitoring", - "description": "Monitor finished execution of a submitted job", - "start": "SubmitJob", - "functions": [ - { - "name": "submitJob", - "operation": "http://myapis.org/monitorapi.json#doSubmit" - }, - { - "name": "checkJobStatus", - "operation": "http://myapis.org/monitorapi.json#checkStatus" - }, - { - "name": "reportJobSuceeded", - "operation": "http://myapis.org/monitorapi.json#reportSucceeded" - }, - { - "name": "reportJobFailed", - "operation": "http://myapis.org/monitorapi.json#reportFailure" - } - ], - "states":[ - { - "name":"SubmitJob", - "type":"operation", - "actionMode":"sequential", - "actions":[ - { - "functionRef": { - "refName": "submitJob", - "arguments": { - "name": "${ .job.name }" - } - }, - "actionDataFilter": { - "results": "${ .jobuid }" - } - } - ], - "stateDataFilter": { - "output": "${ .jobuid }" - }, - "transition": "WaitForCompletion" - }, - { - "name": "WaitForCompletion", - "type": "sleep", - "duration": "PT5S", - "transition": "GetJobStatus" - }, - { - "name":"GetJobStatus", - "type":"operation", - "actionMode":"sequential", - "actions":[ - { - "functionRef": { - "refName": "checkJobStatus", - "arguments": { - "name": "${ .jobuid }" - } - }, - "actionDataFilter": { - "results": "${ .jobstatus }" - } - } - ], - "stateDataFilter": { - "output": "${ .jobstatus }" - }, - "transition": "DetermineCompletion" - }, - { - "name":"DetermineCompletion", - "type":"switch", - "dataConditions": [ - { - "condition": "${ .jobStatus == \"SUCCEEDED\" }", - "transition": "JobSucceeded" - }, - { - "condition": "${ .jobStatus == \"FAILED\" }", - "transition": "JobFailed" - } + "id": "jobmonitoring", + "version": "1.0.0", + "specVersion": "0.8", + "name": "jobmonitoring", + "description": "Monitor finished execution of a submitted job", + "start": "submit-job", + "functions": [ + { + "name": "submit-job", + "operation": "http://myapis.org/monitorapi.json#doSubmit" + }, + { + "name": "check-job-status", + "operation": "http://myapis.org/monitorapi.json#checkStatus" + }, + { + "name": "report-job-suceeded", + "operation": "http://myapis.org/monitorapi.json#reportSucceeded" + }, + { + "name": "report-job-failed", + "operation": "http://myapis.org/monitorapi.json#reportFailure" + } ], - "defaultCondition": { - "transition": "WaitForCompletion" - } - }, - { - "name":"JobSucceeded", - "type":"operation", - "actionMode":"sequential", - "actions":[ - { - "functionRef": { - "refName": "reportJobSuceeded", - "arguments": { - "name": "${ .jobuid }" + "states": [ + { + "name": "submit-job", + "type": "operation", + "actionMode": "sequential", + "actions": [ + { + "name": "submit-job", + "functionRef": { + "refName": "submit-job", + "arguments": { + "name": "${ .job.name }" + } + }, + "actionDataFilter": { + "results": "${ .jobuid }" + } + } + ], + "stateDataFilter": { + "output": "${ .jobuid }" + }, + "transition": "wait-for-completion" + }, + { + "name": "wait-for-completion", + "type": "sleep", + "duration": "PT5S", + "transition": "get-job-status" + }, + { + "name": "get-job-status", + "type": "operation", + "actionMode": "sequential", + "actions": [ + { + "name": "get-job-status", + "functionRef": { + "refName": "check-job-status", + "arguments": { + "name": "${ .jobuid }" + } + }, + "actionDataFilter": { + "results": "${ .jobstatus }" + } + } + ], + "stateDataFilter": { + "output": "${ .jobstatus }" + }, + "transition": "determine-completion" + }, + { + "name": "determine-completion", + "type": "switch", + "dataConditions": [ + { + "condition": "${ .jobStatus == \"SUCCEEDED\" }", + "transition": "job-succeeded", + "name": "succeed" + }, + { + "condition": "${ .jobStatus == \"FAILED\" }", + "transition": "job-failed", + "name": "failed" + } + ], + "defaultCondition": { + "transition": "wait-for-completion" } + }, + { + "name": "job-succeeded", + "type": "operation", + "actionMode": "sequential", + "actions": [ + { + "name": "job-succeeded", + "functionRef": { + "refName": "report-job-suceeded", + "arguments": { + "name": "${ .jobuid }" + } + } + } + ], + "end": true + }, + { + "name": "job-failed", + "type": "operation", + "actionMode": "sequential", + "actions": [ + { + "name": "job-failed", + "functionRef": { + "refName": "report-job-failed", + "arguments": { + "name": "${ .jobuid }" + } + } + } + ], + "end": true } - } - ], - "end": true - }, - { - "name":"JobFailed", - "type":"operation", - "actionMode":"sequential", - "actions":[ - { - "functionRef": { - "refName": "reportJobFailed", - "arguments": { - "name": "${ .jobuid }" - } - } - } - ], - "end": true - } - ] -} -``` + ] +}``` ```yaml id: jobmonitoring -version: '1.0.0' -specVersion: '0.8' -name: Job Monitoring +version: 1.0.0 +specVersion: "0.8" +name: jobmonitoring description: Monitor finished execution of a submitted job -start: SubmitJob +start: submit-job functions: - - name: submitJob - operation: http://myapis.org/monitorapi.json#doSubmit - - name: checkJobStatus - operation: http://myapis.org/monitorapi.json#checkStatus - - name: reportJobSuceeded - operation: http://myapis.org/monitorapi.json#reportSucceeded - - name: reportJobFailed - operation: http://myapis.org/monitorapi.json#reportFailure + - name: submit-job + operation: http://myapis.org/monitorapi.json#doSubmit + - name: check-job-status + operation: http://myapis.org/monitorapi.json#checkStatus + - name: report-job-suceeded + operation: http://myapis.org/monitorapi.json#reportSucceeded + - name: report-job-failed + operation: http://myapis.org/monitorapi.json#reportFailure states: - - name: SubmitJob - type: operation - actionMode: sequential - actions: - - functionRef: - refName: submitJob - arguments: - name: "${ .job.name }" - actionDataFilter: - results: "${ .jobuid }" - stateDataFilter: - output: "${ .jobuid }" - transition: WaitForCompletion - - name: WaitForCompletion - type: sleep - duration: PT5S - transition: GetJobStatus - - name: GetJobStatus - type: operation - actionMode: sequential - actions: - - functionRef: - refName: checkJobStatus - arguments: - name: "${ .jobuid }" - actionDataFilter: - results: "${ .jobstatus }" - stateDataFilter: - output: "${ .jobstatus }" - transition: DetermineCompletion - - name: DetermineCompletion - type: switch - dataConditions: - - condition: ${ .jobStatus == "SUCCEEDED" } - transition: JobSucceeded - - condition: ${ .jobStatus == "FAILED" } - transition: JobFailed - defaultCondition: - transition: WaitForCompletion - - name: JobSucceeded - type: operation - actionMode: sequential - actions: - - functionRef: - refName: reportJobSuceeded - arguments: - name: "${ .jobuid }" - end: true - - name: JobFailed - type: operation - actionMode: sequential - actions: - - functionRef: - refName: reportJobFailed - arguments: - name: "${ .jobuid }" - end: true + - name: submit-job + type: operation + actionMode: sequential + actions: + - name: submit-job + functionRef: + refName: submit-job + arguments: + name: ${ .job.name } + actionDataFilter: + results: ${ .jobuid } + stateDataFilter: + output: ${ .jobuid } + transition: wait-for-completion + - name: wait-for-completion + type: sleep + duration: PT5S + transition: get-job-status + - name: get-job-status + type: operation + actionMode: sequential + actions: + - name: get-job-status + functionRef: + refName: check-job-status + arguments: + name: ${ .jobuid } + actionDataFilter: + results: ${ .jobstatus } + stateDataFilter: + output: ${ .jobstatus } + transition: determine-completion + - name: determine-completion + type: switch + dataConditions: + - condition: ${ .jobStatus == "SUCCEEDED" } + transition: job-succeeded + name: succeed + - condition: ${ .jobStatus == "FAILED" } + transition: job-failed + name: failed + defaultCondition: + transition: wait-for-completion + - name: job-succeeded + type: operation + actionMode: sequential + actions: + - name: job-succeeded + functionRef: + refName: report-job-suceeded + arguments: + name: ${ .jobuid } + end: true + - name: job-failed + type: operation + actionMode: sequential + actions: + - name: job-failed + functionRef: + refName: report-job-failed + arguments: + name: ${ .jobuid } + end: true ``` @@ -1576,83 +1633,86 @@ CloudEvent upon completion of the workflow could look like: ```json { -"id": "sendcloudeventonprovision", -"version": "1.0.0", -"specVersion": "0.8", -"name": "Send CloudEvent on provision completion", -"start": "ProvisionOrdersState", -"events": [ -{ - "name": "provisioningCompleteEvent", - "type": "provisionCompleteType", - "kind": "produced" -} -], -"functions": [ -{ - "name": "provisionOrderFunction", - "operation": "http://myapis.org/provisioning.json#doProvision" -} -], -"states": [ -{ - "name": "ProvisionOrdersState", - "type": "foreach", - "inputCollection": "${ .orders }", - "iterationParam": "singleorder", - "outputCollection": "${ .provisionedOrders }", - "actions": [ - { - "functionRef": { - "refName": "provisionOrderFunction", - "arguments": { - "order": "${ .singleorder }" + "id": "sendcloudeventonprovision", + "version": "1.0.0", + "specVersion": "0.8", + "name": "sendcloudeventonprovision", + "start": "provision-orders-state", + "events": [ + { + "name": "provisioning-complete-event", + "type": "provisionCompleteType", + "kind": "produced" + } + ], + "functions": [ + { + "name": "provision-order-function", + "operation": "http://myapis.org/provisioning.json#doProvision" + } + ], + "states": [ + { + "name": "provision-orders-state", + "type": "foreach", + "inputCollection": "${ .orders }", + "iterationParam": "singleorder", + "outputCollection": "${ .provisionedOrders }", + "actions": [ + { + "name": "provision-order-function", + "functionRef": { + "refName": "provision-order-function", + "arguments": { + "order": "${ .singleorder }" + } + } } + ], + "end": { + "produceEvents": [ + { + "eventRef": "provisioning-complete-event", + "data": "${ .provisionedOrders }" + } + ] } } - ], - "end": { - "produceEvents": [{ - "eventRef": "provisioningCompleteEvent", - "data": "${ .provisionedOrders }" - }] - } -} -] -} -``` + ] +}``` ```yaml id: sendcloudeventonprovision -version: '1.0.0' -specVersion: '0.8' -name: Send CloudEvent on provision completion -start: ProvisionOrdersState +version: 1.0.0 +specVersion: "0.8" +name: sendcloudeventonprovision +start: provision-orders-state events: -- name: provisioningCompleteEvent - type: provisionCompleteType - kind: produced + - name: provisioning-complete-event + type: provisionCompleteType + kind: produced functions: -- name: provisionOrderFunction - operation: http://myapis.org/provisioning.json#doProvision + - name: provision-order-function + operation: http://myapis.org/provisioning.json#doProvision states: -- name: ProvisionOrdersState - type: foreach - inputCollection: "${ .orders }" - iterationParam: singleorder - outputCollection: "${ .provisionedOrders }" - actions: - - functionRef: - refName: provisionOrderFunction - arguments: - order: "${ .singleorder }" - end: - produceEvents: - - eventRef: provisioningCompleteEvent - data: "${ .provisionedOrders }" + - name: provision-orders-state + type: foreach + inputCollection: ${ .orders } + iterationParam: singleorder + outputCollection: ${ .provisionedOrders } + actions: + - name: provision-order-function + functionRef: + refName: provision-order-function + arguments: + order: ${ .singleorder } + end: + produceEvents: + - eventRef: provisioning-complete-event + data: ${ .provisionedOrders } ``` @@ -1709,163 +1769,182 @@ have the matching patient id. ```json { -"id": "patientVitalsWorkflow", -"name": "Monitor Patient Vitals", -"version": "1.0.0", -"specVersion": "0.8", -"start": "MonitorVitals", -"events": [ -{ - "name": "HighBodyTemperature", - "type": "org.monitor.highBodyTemp", - "source": "monitoringSource", - "correlation": [ - { - "contextAttributeName": "patientId" - } - ] -}, -{ - "name": "HighBloodPressure", - "type": "org.monitor.highBloodPressure", - "source": "monitoringSource", - "correlation": [ - { - "contextAttributeName": "patientId" - } - ] -}, -{ - "name": "HighRespirationRate", - "type": "org.monitor.highRespirationRate", - "source": "monitoringSource", - "correlation": [ - { - "contextAttributeName": "patientId" - } - ] -} -], -"functions": [ -{ - "name": "callPulmonologist", - "operation": "http://myapis.org/patientapis.json#callPulmonologist" -}, -{ - "name": "sendTylenolOrder", - "operation": "http://myapis.org/patientapis.json#tylenolOrder" -}, -{ - "name": "callNurse", - "operation": "http://myapis.org/patientapis.json#callNurse" -} -], -"states": [ -{ -"name": "MonitorVitals", -"type": "event", -"exclusive": true, -"onEvents": [{ - "eventRefs": ["HighBodyTemperature"], - "actions": [{ - "functionRef": { - "refName": "sendTylenolOrder", - "arguments": { - "patientid": "${ .patientId }" + "id": "patientVitalsWorkflow", + "name": "patientVitalsWorkflow", + "version": "1.0.0", + "specVersion": "0.8", + "start": "monitor-vitals", + "events": [ + { + "name": "high-body-temperature", + "type": "org.monitor.highBodyTemp", + "source": "monitoringSource", + "correlation": [ + { + "contextAttributeName": "patientId" } - } - }] - }, - { - "eventRefs": ["HighBloodPressure"], - "actions": [{ - "functionRef": { - "refName": "callNurse", - "arguments": { - "patientid": "${ .patientId }" + ] + }, + { + "name": "high-blood-pressure", + "type": "org.monitor.highBloodPressure", + "source": "monitoringSource", + "correlation": [ + { + "contextAttributeName": "patientId" } - } - }] - }, - { - "eventRefs": ["HighRespirationRate"], - "actions": [{ - "functionRef": { - "refName": "callPulmonologist", - "arguments": { - "patientid": "${ .patientId }" + ] + }, + { + "name": "high-respiration-rate", + "type": "org.monitor.highRespirationRate", + "source": "monitoringSource", + "correlation": [ + { + "contextAttributeName": "patientId" + } + ] + } + ], + "functions": [ + { + "name": "call-pulmonologist", + "operation": "http://myapis.org/patientapis.json#callPulmonologist" + }, + { + "name": "send-tylenol-order", + "operation": "http://myapis.org/patientapis.json#tylenolOrder" + }, + { + "name": "call-nurse", + "operation": "http://myapis.org/patientapis.json#callNurse" + } + ], + "states": [ + { + "name": "monitor-vitals", + "type": "event", + "exclusive": true, + "onEvents": [ + { + "eventRefs": [ + "high-body-temperature" + ], + "actions": [ + { + "name": "send-tylenol-order", + "functionRef": { + "refName": "send-tylenol-order", + "arguments": { + "patientid": "${ .patientId }" + } + } + } + ] + }, + { + "eventRefs": [ + "high-blood-pressure" + ], + "actions": [ + { + "name": "call-nurse", + "functionRef": { + "refName": "call-nurse", + "arguments": { + "patientid": "${ .patientId }" + } + } + } + ] + }, + { + "eventRefs": [ + "high-respiration-rate" + ], + "actions": [ + { + "name": "call-pulmonologist", + "functionRef": { + "refName": "call-pulmonologist", + "arguments": { + "patientid": "${ .patientId }" + } + } + } + ] } + ], + "end": { + "terminate": true } - }] - } -], -"end": { - "terminate": true -} -}] -} -``` + } + ] +}``` ```yaml id: patientVitalsWorkflow -name: Monitor Patient Vitals -version: '1.0.0' -specVersion: '0.8' -start: MonitorVitals +name: patientVitalsWorkflow +version: 1.0.0 +specVersion: "0.8" +start: monitor-vitals events: -- name: HighBodyTemperature - type: org.monitor.highBodyTemp - source: monitoringSource - correlation: - - contextAttributeName: patientId -- name: HighBloodPressure - type: org.monitor.highBloodPressure - source: monitoringSource - correlation: - - contextAttributeName: patientId -- name: HighRespirationRate - type: org.monitor.highRespirationRate - source: monitoringSource - correlation: - - contextAttributeName: patientId + - name: high-body-temperature + type: org.monitor.highBodyTemp + source: monitoringSource + correlation: + - contextAttributeName: patientId + - name: high-blood-pressure + type: org.monitor.highBloodPressure + source: monitoringSource + correlation: + - contextAttributeName: patientId + - name: high-respiration-rate + type: org.monitor.highRespirationRate + source: monitoringSource + correlation: + - contextAttributeName: patientId functions: -- name: callPulmonologist - operation: http://myapis.org/patientapis.json#callPulmonologist -- name: sendTylenolOrder - operation: http://myapis.org/patientapis.json#tylenolOrder -- name: callNurse - operation: http://myapis.org/patientapis.json#callNurse + - name: call-pulmonologist + operation: http://myapis.org/patientapis.json#callPulmonologist + - name: send-tylenol-order + operation: http://myapis.org/patientapis.json#tylenolOrder + - name: call-nurse + operation: http://myapis.org/patientapis.json#callNurse states: -- name: MonitorVitals - type: event - exclusive: true - onEvents: - - eventRefs: - - HighBodyTemperature - actions: - - functionRef: - refName: sendTylenolOrder - arguments: - patientid: "${ .patientId }" - - eventRefs: - - HighBloodPressure - actions: - - functionRef: - refName: callNurse - arguments: - patientid: "${ .patientId }" - - eventRefs: - - HighRespirationRate - actions: - - functionRef: - refName: callPulmonologist - arguments: - patientid: "${ .patientId }" - end: - terminate: true + - name: monitor-vitals + type: event + exclusive: true + onEvents: + - eventRefs: + - high-body-temperature + actions: + - name: send-tylenol-order + functionRef: + refName: send-tylenol-order + arguments: + patientid: ${ .patientId } + - eventRefs: + - high-blood-pressure + actions: + - name: call-nurse + functionRef: + refName: call-nurse + arguments: + patientid: ${ .patientId } + - eventRefs: + - high-respiration-rate + actions: + - name: call-pulmonologist + functionRef: + refName: call-pulmonologist + arguments: + patientid: ${ .patientId } + end: + terminate: true ``` @@ -1904,125 +1983,126 @@ when all three of these events happened (in no particular order). ```json { -"id": "finalizeCollegeApplication", -"name": "Finalize College Application", -"version": "1.0.0", -"specVersion": "0.8", -"start": "FinalizeApplication", -"events": [ -{ - "name": "ApplicationSubmitted", - "type": "org.application.submitted", - "source": "applicationsource", - "correlation": [ - { - "contextAttributeName": "applicantId" - } - ] -}, -{ - "name": "SATScoresReceived", - "type": "org.application.satscores", - "source": "applicationsource", - "correlation": [ - { - "contextAttributeName": "applicantId" - } - ] -}, -{ - "name": "RecommendationLetterReceived", - "type": "org.application.recommendationLetter", - "source": "applicationsource", - "correlation": [ - { - "contextAttributeName": "applicantId" - } - ] -} -], -"functions": [ -{ - "name": "finalizeApplicationFunction", - "operation": "http://myapis.org/collegeapplicationapi.json#finalize" -} -], -"states": [ -{ - "name": "FinalizeApplication", - "type": "event", - "exclusive": false, - "onEvents": [ - { - "eventRefs": [ - "ApplicationSubmitted", - "SATScoresReceived", - "RecommendationLetterReceived" - ], - "actions": [ - { - "functionRef": { - "refName": "finalizeApplicationFunction", - "arguments": { - "student": "${ .applicantId }" - } - } + "id": "finalize-college-application", + "name": "finalizeCollegeApplication", + "version": "1.0.0", + "specVersion": "0.8", + "start": "finalize-application", + "events": [ + { + "name": "application-submitted", + "type": "org.application.submitted", + "source": "applicationsource", + "correlation": [ + { + "contextAttributeName": "applicantId" + } + ] + }, + { + "name": "sat-scores-received", + "type": "org.application.satscores", + "source": "applicationsource", + "correlation": [ + { + "contextAttributeName": "applicantId" + } + ] + }, + { + "name": "recommendation-letter-received", + "type": "org.application.recommendationLetter", + "source": "applicationsource", + "correlation": [ + { + "contextAttributeName": "applicantId" } ] } ], - "end": { - "terminate": true - } -} -] -} -``` + "functions": [ + { + "name": "finalize-application-function", + "operation": "http://myapis.org/collegeapplicationapi.json#finalize" + } + ], + "states": [ + { + "name": "finalize-application", + "type": "event", + "exclusive": false, + "onEvents": [ + { + "eventRefs": [ + "application-submitted", + "sat-scores-received", + "recommendation-letter-received" + ], + "actions": [ + { + "name": "finalize-application", + "functionRef": { + "refName": "finalize-application-function", + "arguments": { + "student": "${ .applicantId }" + } + } + } + ] + } + ], + "end": { + "terminate": true + } + } + ] +}``` ```yaml -id: finalizeCollegeApplication -name: Finalize College Application -version: '1.0.0' -specVersion: '0.8' -start: FinalizeApplication +id: finalize-college-application +name: finalizeCollegeApplication +version: 1.0.0 +specVersion: "0.8" +start: finalize-application events: -- name: ApplicationSubmitted - type: org.application.submitted - source: applicationsource - correlation: - - contextAttributeName: applicantId -- name: SATScoresReceived - type: org.application.satscores - source: applicationsource - correlation: - - contextAttributeName: applicantId -- name: RecommendationLetterReceived - type: org.application.recommendationLetter - source: applicationsource - correlation: - - contextAttributeName: applicantId + - name: application-submitted + type: org.application.submitted + source: applicationsource + correlation: + - contextAttributeName: applicantId + - name: sat-scores-received + type: org.application.satscores + source: applicationsource + correlation: + - contextAttributeName: applicantId + - name: recommendation-letter-received + type: org.application.recommendationLetter + source: applicationsource + correlation: + - contextAttributeName: applicantId functions: -- name: finalizeApplicationFunction - operation: http://myapis.org/collegeapplicationapi.json#finalize + - name: finalize-application-function + operation: http://myapis.org/collegeapplicationapi.json#finalize states: -- name: FinalizeApplication - type: event - exclusive: false - onEvents: - - eventRefs: - - ApplicationSubmitted - - SATScoresReceived - - RecommendationLetterReceived - actions: - - functionRef: - refName: finalizeApplicationFunction - arguments: - student: "${ .applicantId }" - end: - terminate: true + - name: finalize-application + type: event + exclusive: false + onEvents: + - eventRefs: + - application-submitted + - sat-scores-received + - recommendation-letter-received + actions: + - name: finalize-application + functionRef: + refName: finalize-application-function + arguments: + student: ${ .applicantId } + end: + terminate: true ``` @@ -2119,84 +2199,89 @@ And for denied credit check, for example: "id": "customercreditcheck", "version": "1.0.0", "specVersion": "0.8", - "name": "Customer Credit Check Workflow", + "name": "customercreditcheck", "description": "Perform Customer Credit Check", - "start": "CheckCredit", + "start": "check-credit", "functions": [ { - "name": "creditCheckFunction", + "name": "credit-check-function", "operation": "http://myapis.org/creditcheckapi.json#doCreditCheck" }, { - "name": "sendRejectionEmailFunction", + "name": "send-rejection-email-function", "operation": "http://myapis.org/creditcheckapi.json#rejectionEmail" } ], "events": [ { - "name": "CreditCheckCompletedEvent", + "name": "credit-check-completed-event", "type": "creditCheckCompleteType", "source": "creditCheckSource", "correlation": [ - { - "contextAttributeName": "customerId" - } - ] + { + "contextAttributeName": "customerId" + } + ] } ], "states": [ { - "name": "CheckCredit", + "name": "check-credit", "type": "callback", "action": { + "name": "check-credit", "functionRef": { - "refName": "callCreditCheckMicroservice", + "refName": "call-credit-check-microservice", "arguments": { "customer": "${ .customer }" } } }, - "eventRef": "CreditCheckCompletedEvent", + "eventRef": "credit-check-completed-event", "timeouts": { - "stateExecTimeout": "PT15M" + "stateExecTimeout": "PT15M" }, - "transition": "EvaluateDecision" + "transition": "evaluate-decision" }, { - "name": "EvaluateDecision", + "name": "evaluate-decision", "type": "switch", "dataConditions": [ { "condition": "${ .creditCheck | .decision == \"Approved\" }", - "transition": "StartApplication" + "transition": "start-application", + "name": "start-application" }, { "condition": "${ .creditCheck | .decision == \"Denied\" }", - "transition": "RejectApplication" + "transition": "reject-application", + "name": "reject-application" } ], "defaultCondition": { - "transition": "RejectApplication" + "transition": "reject-application" } }, { - "name": "StartApplication", + "name": "start-application", "type": "operation", "actions": [ - { - "subFlowRef": "startApplicationWorkflowId" - } + { + "name": "start-application", + "subFlowRef": "startApplicationWorkflowId" + } ], "end": true }, { - "name": "RejectApplication", + "name": "reject-application", "type": "operation", "actionMode": "sequential", "actions": [ { + "name": "reject-application", "functionRef": { - "refName": "sendRejectionEmailFunction", + "refName": "send-rejection-email-function", "arguments": { "applicant": "${ .customer }" } @@ -2206,65 +2291,69 @@ And for denied credit check, for example: "end": true } ] -} -``` +}``` ```yaml id: customercreditcheck -version: '1.0.0' -specVersion: '0.8' -name: Customer Credit Check Workflow +version: 1.0.0 +specVersion: "0.8" +name: customercreditcheck description: Perform Customer Credit Check -start: CheckCredit +start: check-credit functions: -- name: creditCheckFunction - operation: http://myapis.org/creditcheckapi.json#doCreditCheck -- name: sendRejectionEmailFunction - operation: http://myapis.org/creditcheckapi.json#rejectionEmail + - name: credit-check-function + operation: http://myapis.org/creditcheckapi.json#doCreditCheck + - name: send-rejection-email-function + operation: http://myapis.org/creditcheckapi.json#rejectionEmail events: -- name: CreditCheckCompletedEvent - type: creditCheckCompleteType - source: creditCheckSource - correlation: - - contextAttributeName: customerId + - name: credit-check-completed-event + type: creditCheckCompleteType + source: creditCheckSource + correlation: + - contextAttributeName: customerId states: -- name: CheckCredit - type: callback - action: - functionRef: - refName: callCreditCheckMicroservice - arguments: - customer: "${ .customer }" - eventRef: CreditCheckCompletedEvent - timeouts: - stateExecTimeout: PT15M - transition: EvaluateDecision -- name: EvaluateDecision - type: switch - dataConditions: - - condition: ${ .creditCheck | .decision == "Approved" } - transition: StartApplication - - condition: ${ .creditCheck | .decision == "Denied" } - transition: RejectApplication - defaultCondition: - transition: RejectApplication -- name: StartApplication - type: operation - actions: - - subFlowRef: startApplicationWorkflowId - end: true -- name: RejectApplication - type: operation - actionMode: sequential - actions: - - functionRef: - refName: sendRejectionEmailFunction - arguments: - applicant: "${ .customer }" - end: true + - name: check-credit + type: callback + action: + name: check-credit + functionRef: + refName: call-credit-check-microservice + arguments: + customer: ${ .customer } + eventRef: credit-check-completed-event + timeouts: + stateExecTimeout: PT15M + transition: evaluate-decision + - name: evaluate-decision + type: switch + dataConditions: + - condition: ${ .creditCheck | .decision == "Approved" } + transition: start-application + name: start-application + - condition: ${ .creditCheck | .decision == "Denied" } + transition: reject-application + name: reject-application + defaultCondition: + transition: reject-application + - name: start-application + type: operation + actions: + - name: start-application + subFlowRef: startApplicationWorkflowId + end: true + - name: reject-application + type: operation + actionMode: sequential + actions: + - name: reject-application + functionRef: + refName: send-rejection-email-function + arguments: + applicant: ${ .customer } + end: true ``` @@ -2321,84 +2410,89 @@ Bidding is done via an online application and bids are received as events are as ```json { - "id": "handleCarAuctionBid", + "id": "handle-car-auction-bid", "version": "1.0.0", "specVersion": "0.8", - "name": "Car Auction Bidding Workflow", + "name": "handle-car-auction-bid", "description": "Store a single bid whole the car auction is active", "start": { - "stateName": "StoreCarAuctionBid", - "schedule": "R/PT2H" + "stateName": "store-car-auction-bid", + "schedule": "R/PT2H" }, "functions": [ { - "name": "StoreBidFunction", + "name": "store-bid-function", "operation": "http://myapis.org/carauctionapi.json#storeBid" } ], "events": [ { - "name": "CarBidEvent", + "name": "car-bid-event", "type": "carBidMadeType", "source": "carBidEventSource" } ], "states": [ { - "name": "StoreCarAuctionBid", - "type": "event", - "exclusive": true, - "onEvents": [ - { - "eventRefs": ["CarBidEvent"], - "actions": [{ - "functionRef": { - "refName": "StoreBidFunction", - "arguments": { - "bid": "${ .bid }" + "name": "store-car-auction-bid", + "type": "event", + "exclusive": true, + "onEvents": [ + { + "eventRefs": [ + "car-bid-event" + ], + "actions": [ + { + "name": "car-bid-event", + "functionRef": { + "refName": "store-bid-function", + "arguments": { + "bid": "${ .bid }" + } + } } - } - }] - } - ], - "end": true + ] + } + ], + "end": true } ] -} -``` +}``` ```yaml -id: handleCarAuctionBid -version: '1.0.0' -specVersion: '0.8' -name: Car Auction Bidding Workflow +id: handle-car-auction-bid +version: 1.0.0 +specVersion: "0.8" +name: handle-car-auction-bid description: Store a single bid whole the car auction is active start: - stateName: StoreCarAuctionBid + stateName: store-car-auction-bid schedule: R/PT2H functions: -- name: StoreBidFunction - operation: http://myapis.org/carauctionapi.json#storeBid + - name: store-bid-function + operation: http://myapis.org/carauctionapi.json#storeBid events: -- name: CarBidEvent - type: carBidMadeType - source: carBidEventSource + - name: car-bid-event + type: carBidMadeType + source: carBidEventSource states: -- name: StoreCarAuctionBid - type: event - exclusive: true - onEvents: - - eventRefs: - - CarBidEvent - actions: - - functionRef: - refName: StoreBidFunction - arguments: - bid: "${ .bid }" - end: true + - name: store-car-auction-bid + type: event + exclusive: true + onEvents: + - eventRefs: + - car-bid-event + actions: + - name: car-bid-event + functionRef: + refName: store-bid-function + arguments: + bid: ${ .bid } + end: true ``` @@ -2450,95 +2544,98 @@ The results of the inbox service called is expected to be for example: ```json { -"id": "checkInbox", -"name": "Check Inbox Workflow", -"version": "1.0.0", -"specVersion": "0.8", -"description": "Periodically Check Inbox", -"start": { - "stateName": "CheckInbox", - "schedule": { - "cron": "0 0/15 * * * ?" - } -}, -"functions": [ - { - "name": "checkInboxFunction", - "operation": "http://myapis.org/inboxapi.json#checkNewMessages" - }, - { - "name": "sendTextFunction", - "operation": "http://myapis.org/inboxapi.json#sendText" - } -], -"states": [ - { - "name": "CheckInbox", - "type": "operation", - "actionMode": "sequential", - "actions": [ - { - "functionRef": "checkInboxFunction" - } - ], - "transition": "SendTextForHighPriority" + "id": "check-inbox", + "name": "check-inbox", + "version": "1.0.0", + "specVersion": "0.8", + "description": "Periodically Check Inbox", + "start": { + "stateName": "check-inbox", + "schedule": { + "cron": "0 0/15 * * * ?" + } }, - { - "name": "SendTextForHighPriority", - "type": "foreach", - "inputCollection": "${ .messages }", - "iterationParam": "singlemessage", - "actions": [ - { - "functionRef": { - "refName": "sendTextFunction", - "arguments": { - "message": "${ .singlemessage }" + "functions": [ + { + "name": "check-inbox-function", + "operation": "http://myapis.org/inboxapi.json#checkNewMessages" + }, + { + "name": "send-text-function", + "operation": "http://myapis.org/inboxapi.json#sendText" + } + ], + "states": [ + { + "name": "check-inbox", + "type": "operation", + "actionMode": "sequential", + "actions": [ + { + "name":"check-inbox", + "functionRef": "check-inbox-function" + } + ], + "transition": "send-text-for-high-priority" + }, + { + "name": "send-text-for-high-priority", + "type": "foreach", + "inputCollection": "${ .messages }", + "iterationParam": "singlemessage", + "actions": [ + { + "name": "send-text-for-high-priority", + "functionRef": { + "refName": "send-text-function", + "arguments": { + "message": "${ .singlemessage }" + } } } - } - ], - "end": true - } -] -} -``` + ], + "end": true + } + ] +}``` ```yaml -id: checkInbox -name: Check Inbox Workflow +id: check-inbox +name: check-inbox +version: 1.0.0 +specVersion: "0.8" description: Periodically Check Inbox -version: '1.0.0' -specVersion: '0.8' start: - stateName: CheckInbox + stateName: check-inbox schedule: cron: 0 0/15 * * * ? functions: -- name: checkInboxFunction - operation: http://myapis.org/inboxapi.json#checkNewMessages -- name: sendTextFunction - operation: http://myapis.org/inboxapi.json#sendText + - name: check-inbox-function + operation: http://myapis.org/inboxapi.json#checkNewMessages + - name: send-text-function + operation: http://myapis.org/inboxapi.json#sendText states: -- name: CheckInbox - type: operation - actionMode: sequential - actions: - - functionRef: checkInboxFunction - transition: SendTextForHighPriority -- name: SendTextForHighPriority - type: foreach - inputCollection: "${ .messages }" - iterationParam: singlemessage - actions: - - functionRef: - refName: sendTextFunction - arguments: - message: "${ .singlemessage }" - end: true + - name: check-inbox + type: operation + actionMode: sequential + actions: + - name: check-inbox + functionRef: check-inbox-function + transition: send-text-for-high-priority + - name: send-text-for-high-priority + type: foreach + inputCollection: ${ .messages } + iterationParam: singlemessage + actions: + - name: send-text-for-high-priority + functionRef: + refName: send-text-function + arguments: + message: ${ .singlemessage } + end: true ``` @@ -2593,21 +2690,21 @@ For this example we assume that the workflow instance is started given the follo ```json { - "id": "VetAppointmentWorkflow", - "name": "Vet Appointment Workflow", + "id": "vet-appointment-workflow", + "name": "vet-appointment-workflow", "description": "Vet service call via events", "version": "1.0.0", "specVersion": "0.8", - "start": "MakeVetAppointmentState", + "start": "make-vet-appointment-state", "events": [ { - "name": "MakeVetAppointment", + "name": "make-vet-appointment", "source": "VetServiceSource", "type": "events.vet.appointments", "kind": "produced" }, { - "name": "VetAppointmentInfo", + "name": "vet-appointment-info", "source": "VetServiceSource", "type": "events.vet.appointments", "kind": "consumed" @@ -2615,15 +2712,15 @@ For this example we assume that the workflow instance is started given the follo ], "states": [ { - "name": "MakeVetAppointmentState", + "name": "make-vet-appointment-state", "type": "operation", "actions": [ { - "name": "MakeAppointmentAction", + "name": "make-appointment-action", "eventRef": { - "produceEventRef": "MakeVetAppointment", - "data": "${ .patientInfo }", - "consumeEventRef": "VetAppointmentInfo" + "produceEventRef": "make-vet-appointment", + "data": "${ .patientInfo }", + "consumeEventRef": "vet-appointment-info" }, "actionDataFilter": { "results": "${ .appointmentInfo }" @@ -2631,47 +2728,46 @@ For this example we assume that the workflow instance is started given the follo } ], "timeouts": { - "actionExecTimeout": "PT15M" + "actionExecTimeout": "PT15M" }, "end": true } ] -} -``` +}``` ```yaml -id: VetAppointmentWorkflow -name: Vet Appointment Workflow +id: vet-appointment-workflow +name: vet-appointment-workflow description: Vet service call via events -version: '1.0.0' -specVersion: '0.8' -start: MakeVetAppointmentState +version: 1.0.0 +specVersion: "0.8" +start: make-vet-appointment-state events: - - name: MakeVetAppointment - source: VetServiceSource - type: events.vet.appointments - kind: produced - - name: VetAppointmentInfo - source: VetServiceSource - type: events.vet.appointments - kind: consumed + - name: make-vet-appointment + source: VetServiceSource + type: events.vet.appointments + kind: produced + - name: vet-appointment-info + source: VetServiceSource + type: events.vet.appointments + kind: consumed states: - - name: MakeVetAppointmentState - type: operation - actions: - - name: MakeAppointmentAction - eventRef: - produceEventRef: MakeVetAppointment - data: "${ .patientInfo }" - consumeEventRef: VetAppointmentInfo - actionDataFilter: - results: "${ .appointmentInfo }" - timeouts: - actionExecTimeout: PT15M - end: true + - name: make-vet-appointment-state + type: operation + actions: + - name: make-appointment-action + eventRef: + produceEventRef: make-vet-appointment + data: ${ .patientInfo } + consumeEventRef: vet-appointment-info + actionDataFilter: + results: ${ .appointmentInfo } + timeouts: + actionExecTimeout: PT15M + end: true ``` @@ -2746,159 +2842,166 @@ In our workflow definition then we can reference these files rather than definin ```json { - "id": "paymentconfirmation", - "version": "1.0.0", - "specVersion": "0.8", - "name": "Payment Confirmation Workflow", - "description": "Performs Payment Confirmation", - "functions": "functiondefs.json", - "events": "eventdefs.yml", - "states": [ - { - "name": "PaymentReceived", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "PaymentReceivedEvent" - ], - "actions": [ - { - "name": "checkfunds", - "functionRef": { - "refName": "checkFundsAvailability", - "arguments": { - "account": "${ .accountId }", - "paymentamount": "${ .payment.amount }" + "id": "paymentconfirmation", + "version": "1.0.0", + "specVersion": "0.8", + "name": "paymentconfirmation", + "description": "Performs Payment Confirmation", + "functions": "file://functiondefs.json", + "events": "file://eventdefs.yml", + "states": [ + { + "name": "payment-received", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "payment-received-event" + ], + "actions": [ + { + "name": "checkfunds", + "functionRef": { + "refName": "check-funds-availability", + "arguments": { + "account": "${ .accountId }", + "paymentamount": "${ .payment.amount }" + } + } + } + ] } - } - } - ] - } - ], - "transition": "ConfirmBasedOnFunds" - }, - { - "name": "ConfirmBasedOnFunds", - "type": "switch", - "dataConditions": [ + ], + "transition": "confirm-based-on-funds" + }, { - "condition": "${ .funds | .available == \"true\" }", - "transition": "SendPaymentSuccess" + "name": "confirm-based-on-funds", + "type": "switch", + "dataConditions": [ + { + "condition": "${ .funds | .available == \"true\" }", + "transition": "send-payment-success", + "name": "success" + }, + { + "condition": "${ .funds | .available == \"false\" }", + "transition": "send-insufficient-results", + "name": "failed" + } + ], + "defaultCondition": { + "transition": "send-payment-success" + } }, { - "condition": "${ .funds | .available == \"false\" }", - "transition": "SendInsufficientResults" - } - ], - "defaultCondition": { - "transition": "SendPaymentSuccess" - } - }, - { - "name": "SendPaymentSuccess", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "sendSuccessEmail", - "arguments": { - "applicant": "${ .customer }" + "name": "send-payment-success", + "type": "operation", + "actions": [ + { + "name": "send-payment-success", + "functionRef": { + "refName": "send-success-email", + "arguments": { + "applicant": "${ .customer }" + } + } + } + ], + "end": { + "produceEvents": [ + { + "eventRef": "confirmation-completed-event", + "data": "${ .payment }" + } + ] } - } - } - ], - "end": { - "produceEvents": [ - { - "eventRef": "ConfirmationCompletedEvent", - "data": "${ .payment }" - } - ] - } - }, - { - "name": "SendInsufficientResults", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "sendInsufficientFundsEmail", - "arguments": { - "applicant": "${ .customer }" + }, + { + "name": "send-insufficient-results", + "type": "operation", + "actions": [ + { + "name": "send-insufficient-results", + "functionRef": { + "refName": "send-insufficient-funds-email", + "arguments": { + "applicant": "${ .customer }" + } + } + } + ], + "end": { + "produceEvents": [ + { + "eventRef": "confirmation-completed-event", + "data": "${ .payment }" + } + ] } - } } - ], - "end": { - "produceEvents": [ - { - "eventRef": "ConfirmationCompletedEvent", - "data": "${ .payment }" - } - ] - } - } - ] -} -``` + ] +}``` ```yaml id: paymentconfirmation -version: '1.0.0' -specVersion: '0.8' -name: Payment Confirmation Workflow +version: 1.0.0 +specVersion: "0.8" +name: paymentconfirmation description: Performs Payment Confirmation -functions: functiondefs.json -events: eventdefs.yml +functions: file://functiondefs.json +events: file://eventdefs.yml states: -- name: PaymentReceived - type: event - onEvents: - - eventRefs: - - PaymentReceivedEvent + - name: payment-received + type: event + onEvents: + - eventRefs: + - payment-received-event + actions: + - name: checkfunds + functionRef: + refName: check-funds-availability + arguments: + account: ${ .accountId } + paymentamount: ${ .payment.amount } + transition: confirm-based-on-funds + - name: confirm-based-on-funds + type: switch + dataConditions: + - condition: ${ .funds | .available == "true" } + transition: send-payment-success + name: success + - condition: ${ .funds | .available == "false" } + transition: send-insufficient-results + name: failed + defaultCondition: + transition: send-payment-success + - name: send-payment-success + type: operation actions: - - name: checkfunds - functionRef: - refName: checkFundsAvailability - arguments: - account: "${ .accountId }" - paymentamount: "${ .payment.amount }" - transition: ConfirmBasedOnFunds -- name: ConfirmBasedOnFunds - type: switch - dataConditions: - - condition: "${ .funds | .available == \"true\" }" - transition: SendPaymentSuccess - - condition: "${ .funds | .available == \"false\" }" - transition: SendInsufficientResults - defaultCondition: - transition: SendPaymentSuccess -- name: SendPaymentSuccess - type: operation - actions: - - functionRef: - refName: sendSuccessEmail - arguments: - applicant: "${ .customer }" - end: - produceEvents: - - eventRef: ConfirmationCompletedEvent - data: "${ .payment }" -- name: SendInsufficientResults - type: operation - actions: - - functionRef: - refName: sendInsufficientFundsEmail - arguments: - applicant: "${ .customer }" - end: - produceEvents: - - eventRef: ConfirmationCompletedEvent - data: "${ .payment }" + - name: send-payment-success + functionRef: + refName: send-success-email + arguments: + applicant: ${ .customer } + end: + produceEvents: + - eventRef: confirmation-completed-event + data: ${ .payment } + - name: send-insufficient-results + type: operation + actions: + - name: send-insufficient-results + functionRef: + refName: send-insufficient-funds-email + arguments: + applicant: ${ .customer } + end: + produceEvents: + - eventRef: confirmation-completed-event + data: ${ .payment } ``` @@ -2949,135 +3052,146 @@ If the retries are not successful, we want to just gracefully end workflow execu ```json { - "id": "patientonboarding", - "name": "Patient Onboarding Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "Onboard", - "states": [ - { - "name": "Onboard", - "type": "event", - "onEvents": [ - { - "eventRefs": [ - "NewPatientEvent" - ], - "actions": [ - { - "functionRef": "StorePatient", - "retryRef": "ServicesNotAvailableRetryStrategy", - "retryableErrors": ["ServiceNotAvailable"] - }, - { - "functionRef": "AssignDoctor", - "retryRef": "ServicesNotAvailableRetryStrategy", - "retryableErrors": ["ServiceNotAvailable"] - }, - { - "functionRef": "ScheduleAppt", - "retryRef": "ServicesNotAvailableRetryStrategy", - "retryableErrors": ["ServiceNotAvailable"] - } - ] + "id": "patientonboarding", + "name": "patientonboarding", + "version": "1.0.0", + "specVersion": "0.8", + "start": "onboard", + "states": [ + { + "name": "onboard", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "new-patient-event" + ], + "actions": [ + { + "name": "store-patient", + "functionRef": "store-patient", + "retryRef": "services-not-available-retry-strategy", + "retryableErrors": [ + "service-not-available" + ] + }, + { + "name": "assign-doctor", + "functionRef": "assign-doctor", + "retryRef": "services-not-available-retry-strategy", + "retryableErrors": [ + "service-not-available" + ] + }, + { + "name": "schedule-appt", + "functionRef": "schedule-appt", + "retryRef": "services-not-available-retry-strategy", + "retryableErrors": [ + "service-not-available" + ] + } + ] + } + ], + "onErrors": [ + { + "errorRef": "service-not-available", + "end": true + } + ], + "end": true } - ], - "onErrors": [ + ], + "events": [ { - "errorRef": "ServiceNotAvailable", - "end": true + "name": "store-patient", + "type": "new.patients.event", + "source": "newpatient/+" } - ], - "end": true - } - ], - "events": [ - { - "name": "StorePatient", - "type": "new.patients.event", - "source": "newpatient/+" - } - ], - "functions": [ - { - "name": "StoreNewPatientInfo", - "operation": "api/services.json#addPatient" - }, - { - "name": "AssignDoctor", - "operation": "api/services.json#assignDoctor" - }, - { - "name": "ScheduleAppt", - "operation": "api/services.json#scheduleAppointment" - } - ], - "errors": [ - { - "name": "ServiceNotAvailable", - "code": "503" - } - ], - "retries": [ - { - "name": "ServicesNotAvailableRetryStrategy", - "delay": "PT3S", - "maxAttempts": 10 - } - ] -} -``` + ], + "functions": [ + { + "name": "store-new-patient-info", + "operation": "api/services.json#addPatient" + }, + { + "name": "assign-doctor", + "operation": "api/services.json#assignDoctor" + }, + { + "name": "schedule-appt", + "operation": "api/services.json#scheduleAppointment" + } + ], + "errors": [ + { + "name": "service-not-available", + "code": "503" + } + ], + "retries": [ + { + "name": "services-not-available-retry-strategy", + "delay": "PT3S", + "maxAttempts": 10 + } + ] +}``` ```yaml id: patientonboarding -name: Patient Onboarding Workflow -version: '1.0.0' -specVersion: '0.8' -start: Onboard +name: patientonboarding +version: 1.0.0 +specVersion: "0.8" +start: onboard states: - - name: Onboard - type: event - onEvents: - - eventRefs: - - NewPatientEvent - actions: - - functionRef: StorePatient - retryRef: ServicesNotAvailableRetryStrategy - retryableErrors: - - ServiceNotAvailable - - functionRef: AssignDoctor - retryRef: ServicesNotAvailableRetryStrategy - retryableErrors: - - ServiceNotAvailable - - functionRef: ScheduleAppt - retryRef: ServicesNotAvailableRetryStrategy - retryableErrors: - - ServiceNotAvailable - onErrors: - - errorRef: ServiceNotAvailable - end: true - end: true + - name: onboard + type: event + onEvents: + - eventRefs: + - new-patient-event + actions: + - name: store-patient + functionRef: store-patient + retryRef: services-not-available-retry-strategy + retryableErrors: + - service-not-available + - name: assign-doctor + functionRef: assign-doctor + retryRef: services-not-available-retry-strategy + retryableErrors: + - service-not-available + - name: schedule-appt + functionRef: schedule-appt + retryRef: services-not-available-retry-strategy + retryableErrors: + - service-not-available + onErrors: + - errorRef: service-not-available + end: true + end: true events: - - name: StorePatient - type: new.patients.event - source: newpatient/+ + - name: store-patient + type: new.patients.event + source: newpatient/+ functions: - - name: StoreNewPatientInfo - operation: api/services.json#addPatient - - name: AssignDoctor - operation: api/services.json#assignDoctor - - name: ScheduleAppt - operation: api/services.json#scheduleAppointment + - name: store-new-patient-info + operation: api/services.json#addPatient + - name: assign-doctor + operation: api/services.json#assignDoctor + - name: schedule-appt + operation: api/services.json#scheduleAppointment errors: - - name: ServiceNotAvailable - code: '503' + - name: service-not-available + code: "503" retries: - - name: ServicesNotAvailableRetryStrategy - delay: PT3S - maxAttempts: 10 + - name: services-not-available-retry-strategy + delay: PT3S + maxAttempts: 10 ``` @@ -3121,250 +3235,264 @@ This example shows the use of the workflow [execTimeout definition](../specifica ```json { - "id": "order", - "name": "Purchase Order Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "StartNewOrder", - "timeouts": { - "workflowExecTimeout": { - "duration": "PT30D", - "runBefore": "CancelOrder" - } - }, - "states": [ - { - "name": "StartNewOrder", - "type": "event", - "onEvents": [ - { - "eventRefs": ["OrderCreatedEvent"], - "actions": [ - { - "functionRef": { - "refName": "LogNewOrderCreated" - } - } - ] + "id": "order", + "name": "order", + "description": "Purchase Order Workflow", + "version": "1.0.0", + "specVersion": "0.8", + "start": "start-new-order", + "timeouts": { + "workflowExecTimeout": { + "duration": "PT30D", + "runBefore": "CancelOrder" } - ], - "transition": { - "nextState": "WaitForOrderConfirmation" - } }, - { - "name": "WaitForOrderConfirmation", - "type": "event", - "onEvents": [ + "states": [ { - "eventRefs": ["OrderConfirmedEvent"], - "actions": [ - { - "functionRef": { - "refName": "LogOrderConfirmed" - } + "name": "start-new-order", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "order-created-event" + ], + "actions": [ + { + "name": "log-new-order-created", + "functionRef": { + "refName": "log-new-order-created" + } + } + ] + } + ], + "transition": { + "nextState": "wait-for-order-confirmation" } - ] - } - ], - "transition": { - "nextState": "WaitOrderShipped" - } - }, - { - "name": "WaitOrderShipped", - "type": "event", - "onEvents": [ + }, { - "eventRefs": ["ShipmentSentEvent"], - "actions": [ - { - "functionRef": { - "refName": "LogOrderShipped" - } + "name": "wait-for-order-confirmation", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "order-confirmed-event" + ], + "actions": [ + { + "name": "log-order-confirmed", + "functionRef": { + "refName": "log-order-confirmed" + } + } + ] + } + ], + "transition": { + "nextState": "wait-order-shipped" } - ] - } - ], - "end": { - "terminate": true, - "produceEvents": [ - { - "eventRef": "OrderFinishedEvent" - } - ] - } - }, - { - "name": "CancelOrder", - "type": "operation", - "actions": [ + }, { - "functionRef": { - "refName": "CancelOrder" - } - } - ], - "end": { - "terminate": true, - "produceEvents": [ - { - "eventRef": "OrderCancelledEvent" - } - ] - } - } - ], - "events": [ - { - "name": "OrderCreatedEvent", - "type": "my.company.orders", - "source": "/orders/new", - "correlation": [ + "name": "wait-order-shipped", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "shipment-sent-event" + ], + "actions": [ + { + "name": "log-order-shipped", + "functionRef": { + "refName": "log-order-shipped" + } + } + ] + } + ], + "end": { + "terminate": true, + "produceEvents": [ + { + "eventRef": "order-finished-event" + } + ] + } + }, { - "contextAttributeName": "orderid" + "name": "cancel-order", + "type": "operation", + "actions": [ + { + "name": "cancel-order", + "functionRef": { + "refName": "cancel-order" + } + } + ], + "end": { + "terminate": true, + "produceEvents": [ + { + "eventRef": "order-cancelled-event" + } + ] + } } - ] - }, - { - "name": "OrderConfirmedEvent", - "type": "my.company.orders", - "source": "/orders/confirmed", - "correlation": [ + ], + "events": [ + { + "name": "order-created-event", + "type": "my.company.orders", + "source": "/orders/new", + "correlation": [ + { + "contextAttributeName": "orderid" + } + ] + }, + { + "name": "order-confirmed-event", + "type": "my.company.orders", + "source": "/orders/confirmed", + "correlation": [ + { + "contextAttributeName": "orderid" + } + ] + }, + { + "name": "shipment-sent-event", + "type": "my.company.orders", + "source": "/orders/shipped", + "correlation": [ + { + "contextAttributeName": "orderid" + } + ] + }, + { + "name": "order-finished-event", + "type": "my.company.orders", + "kind": "produced" + }, { - "contextAttributeName": "orderid" + "name": "order-cancelled-event", + "type": "my.company.orders", + "kind": "produced" } - ] - }, - { - "name": "ShipmentSentEvent", - "type": "my.company.orders", - "source": "/orders/shipped", - "correlation": [ + ], + "functions": [ + { + "name": "log-new-order-created", + "operation": "http.myorg.io/ordersservices.json#logcreated" + }, + { + "name": "log-order-confirmed", + "operation": "http.myorg.io/ordersservices.json#logconfirmed" + }, { - "contextAttributeName": "orderid" + "name": "log-order-shipped", + "operation": "http.myorg.io/ordersservices.json#logshipped" + }, + { + "name": "cancel-order", + "operation": "http.myorg.io/ordersservices.json#calcelorder" } - ] - }, - { - "name": "OrderFinishedEvent", - "type": "my.company.orders", - "kind": "produced" - }, - { - "name": "OrderCancelledEvent", - "type": "my.company.orders", - "kind": "produced" - } - ], - "functions": [ - { - "name": "LogNewOrderCreated", - "operation": "http.myorg.io/ordersservices.json#logcreated" - }, - { - "name": "LogOrderConfirmed", - "operation": "http.myorg.io/ordersservices.json#logconfirmed" - }, - { - "name": "LogOrderShipped", - "operation": "http.myorg.io/ordersservices.json#logshipped" - }, - { - "name": "CancelOrder", - "operation": "http.myorg.io/ordersservices.json#calcelorder" - } - ] -} -``` + ] +}``` ```yaml id: order -name: Purchase Order Workflow -version: '1.0.0' -specVersion: '0.8' -start: StartNewOrder - +name: order +description: Purchase Order Workflow +version: 1.0.0 +specVersion: "0.8" +start: start-new-order timeouts: workflowExecTimeout: duration: PT30D runBefore: CancelOrder states: -- name: StartNewOrder - type: event - onEvents: - - eventRefs: - - OrderCreatedEvent - actions: - - functionRef: - refName: LogNewOrderCreated - transition: - nextState: WaitForOrderConfirmation -- name: WaitForOrderConfirmation - type: event - onEvents: - - eventRefs: - - OrderConfirmedEvent - actions: - - functionRef: - refName: LogOrderConfirmed - transition: - nextState: WaitOrderShipped -- name: WaitOrderShipped - type: event - onEvents: - - eventRefs: - - ShipmentSentEvent - actions: - - functionRef: - refName: LogOrderShipped - end: - terminate: true - produceEvents: - - eventRef: OrderFinishedEvent -- name: CancelOrder - type: operation - actions: - - functionRef: - refName: CancelOrder - end: - terminate: true - produceEvents: - - eventRef: OrderCancelledEvent + - name: start-new-order + type: event + onEvents: + - eventRefs: + - order-created-event + actions: + - name: log-new-order-created + functionRef: + refName: log-new-order-created + transition: + nextState: wait-for-order-confirmation + - name: wait-for-order-confirmation + type: event + onEvents: + - eventRefs: + - order-confirmed-event + actions: + - name: log-order-confirmed + functionRef: + refName: log-order-confirmed + transition: + nextState: wait-order-shipped + - name: wait-order-shipped + type: event + onEvents: + - eventRefs: + - shipment-sent-event + actions: + - name: log-order-shipped + functionRef: + refName: log-order-shipped + end: + terminate: true + produceEvents: + - eventRef: order-finished-event + - name: cancel-order + type: operation + actions: + - name: cancel-order + functionRef: + refName: cancel-order + end: + terminate: true + produceEvents: + - eventRef: order-cancelled-event events: -- name: OrderCreatedEvent - type: my.company.orders - source: "/orders/new" - correlation: - - contextAttributeName: orderid -- name: OrderConfirmedEvent - type: my.company.orders - source: "/orders/confirmed" - correlation: - - contextAttributeName: orderid -- name: ShipmentSentEvent - type: my.company.orders - source: "/orders/shipped" - correlation: - - contextAttributeName: orderid -- name: OrderFinishedEvent - type: my.company.orders - kind: produced -- name: OrderCancelledEvent - type: my.company.orders - kind: produced + - name: order-created-event + type: my.company.orders + source: /orders/new + correlation: + - contextAttributeName: orderid + - name: order-confirmed-event + type: my.company.orders + source: /orders/confirmed + correlation: + - contextAttributeName: orderid + - name: shipment-sent-event + type: my.company.orders + source: /orders/shipped + correlation: + - contextAttributeName: orderid + - name: order-finished-event + type: my.company.orders + kind: produced + - name: order-cancelled-event + type: my.company.orders + kind: produced functions: -- name: LogNewOrderCreated - operation: http.myorg.io/ordersservices.json#logcreated -- name: LogOrderConfirmed - operation: http.myorg.io/ordersservices.json#logconfirmed -- name: LogOrderShipped - operation: http.myorg.io/ordersservices.json#logshipped -- name: CancelOrder - operation: http.myorg.io/ordersservices.json#calcelorder + - name: log-new-order-created + operation: http.myorg.io/ordersservices.json#logcreated + - name: log-order-confirmed + operation: http.myorg.io/ordersservices.json#logconfirmed + - name: log-order-shipped + operation: http.myorg.io/ordersservices.json#logshipped + - name: cancel-order + operation: http.myorg.io/ordersservices.json#calcelorder ``` @@ -3405,144 +3533,152 @@ the data for an hour, send report, and so on. ```json { - "id": "roomreadings", - "name": "Room Temp and Humidity Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "ConsumeReading", - "timeouts": { - "workflowExecTimeout": { - "duration": "PT1H", - "runBefore": "GenerateReport" - } - }, - "keepActive": true, - "states": [ - { - "name": "ConsumeReading", - "type": "event", - "onEvents": [ - { - "eventRefs": ["TemperatureEvent", "HumidityEvent"], - "actions": [ - { - "functionRef": { - "refName": "LogReading" - } - } - ], - "eventDataFilter": { - "toStateData": "${ .readings }" - } + "id": "roomreadings", + "name": "roomreadings", + "description": "Room Temp and Humidity Workflow", + "version": "1.0.0", + "specVersion": "0.8", + "start": "consume-reading", + "timeouts": { + "workflowExecTimeout": { + "duration": "PT1H", + "runBefore": "generate-report" } - ], - "end": true }, - { - "name": "GenerateReport", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "ProduceReport", - "arguments": { - "data": "${ .readings }" + "keepActive": true, + "states": [ + { + "name": "consume-reading", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "temperature-event", + "humidity-event" + ], + "actions": [ + { + "name": "log-reading", + "functionRef": { + "refName": "log-reading" + } + } + ], + "eventDataFilter": { + "toStateData": "${ .readings }" + } + } + ], + "end": true + }, + { + "name": "generate-report", + "type": "operation", + "actions": [ + { + "name": "generate-report", + "functionRef": { + "refName": "produce-report", + "arguments": { + "data": "${ .readings }" + } + } + } + ], + "end": { + "terminate": true } - } } - ], - "end": { - "terminate": true - } - } - ], - "events": [ - { - "name": "TemperatureEvent", - "type": "my.home.sensors", - "source": "/home/rooms/+", - "correlation": [ + ], + "events": [ + { + "name": "temperature-event", + "type": "my.home.sensors", + "source": "/home/rooms/+", + "correlation": [ + { + "contextAttributeName": "roomId" + } + ] + }, { - "contextAttributeName": "roomId" + "name": "humidity-event", + "type": "my.home.sensors", + "source": "/home/rooms/+", + "correlation": [ + { + "contextAttributeName": "roomId" + } + ] } - ] - }, - { - "name": "HumidityEvent", - "type": "my.home.sensors", - "source": "/home/rooms/+", - "correlation": [ + ], + "functions": [ + { + "name": "log-reading", + "operation": "http.myorg.io/ordersservices.json#logreading" + }, { - "contextAttributeName": "roomId" + "name": "produce-report", + "operation": "http.myorg.io/ordersservices.json#produceReport" } - ] - } - ], - "functions": [ - { - "name": "LogReading", - "operation": "http.myorg.io/ordersservices.json#logreading" - }, - { - "name": "ProduceReport", - "operation": "http.myorg.io/ordersservices.json#produceReport" - } - ] -} -``` + ] +}``` ```yaml id: roomreadings -name: Room Temp and Humidity Workflow -version: '1.0.0' -specVersion: '0.8' -start: ConsumeReading +name: roomreadings +description: Room Temp and Humidity Workflow +version: 1.0.0 +specVersion: "0.8" +start: consume-reading timeouts: workflowExecTimeout: duration: PT1H - runBefore: GenerateReport + runBefore: generate-report keepActive: true states: -- name: ConsumeReading - type: event - onEvents: - - eventRefs: - - TemperatureEvent - - HumidityEvent + - name: consume-reading + type: event + onEvents: + - eventRefs: + - temperature-event + - humidity-event + actions: + - name: log-reading + functionRef: + refName: log-reading + eventDataFilter: + toStateData: ${ .readings } + end: true + - name: generate-report + type: operation actions: - - functionRef: - refName: LogReading - eventDataFilter: - toStateData: "${ .readings }" - end: true -- name: GenerateReport - type: operation - actions: - - functionRef: - refName: ProduceReport - arguments: - data: "${ .readings }" - end: - terminate: true + - name: generate-report + functionRef: + refName: produce-report + arguments: + data: ${ .readings } + end: + terminate: true events: -- name: TemperatureEvent - type: my.home.sensors - source: "/home/rooms/+" - correlation: - - contextAttributeName: roomId -- name: HumidityEvent - type: my.home.sensors - source: "/home/rooms/+" - correlation: - - contextAttributeName: roomId + - name: temperature-event + type: my.home.sensors + source: /home/rooms/+ + correlation: + - contextAttributeName: roomId + - name: humidity-event + type: my.home.sensors + source: /home/rooms/+ + correlation: + - contextAttributeName: roomId functions: -- name: LogReading - operation: http.myorg.io/ordersservices.json#logreading -- name: ProduceReport - operation: http.myorg.io/ordersservices.json#produceReport + - name: log-reading + operation: http.myorg.io/ordersservices.json#logreading + - name: produce-report + operation: http.myorg.io/ordersservices.json#produceReport ``` @@ -3579,103 +3715,108 @@ We fist define our top-level workflow for this example: ```json { - "id": "checkcarvitals", - "name": "Check Car Vitals Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "WhenCarIsOn", - "states": [ - { - "name": "WhenCarIsOn", - "type": "event", - "onEvents": [ - { - "eventRefs": ["CarTurnedOnEvent"] - } - ], - "transition": "DoCarVitalChecks" - }, - { - "name": "DoCarVitalChecks", - "type": "operation", - "actions": [ - { - "subFlowRef": "vitalscheck", - "sleep": { - "after": "PT1S" - } - } - ], - "transition": "CheckContinueVitalChecks" - }, - { - "name": "CheckContinueVitalChecks", - "type": "switch", - "eventConditions": [ - { - "name": "Car Turned Off Condition", - "eventRef": "CarTurnedOffEvent", - "end": true - } - ], - "defaultCondition": { - "transition": "DoCarVitalChecks" - } - } - ], - "events": [ - { - "name": "CarTurnedOnEvent", - "type": "car.events", - "source": "my/car" - }, - { - "name": "CarTurnedOffEvent", - "type": "car.events", - "source": "my/car" - } - ] -} -``` + "id": "checkcarvitals", + "name": "checkcarvitals", + "description": "Check Car Vitals Workflow", + "version": "1.0.0", + "specVersion": "0.8", + "start": "when-car-is-on", + "states": [ + { + "name": "when-car-is-on", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "car-turned-on-event" + ] + } + ], + "transition": "do-car-vital-checks" + }, + { + "name": "do-car-vital-checks", + "type": "operation", + "actions": [ + { + "name": "do-car-vital-checks", + "subFlowRef": "vitalscheck", + "sleep": { + "after": "PT1S" + } + } + ], + "transition": "check-continue-vital-checks" + }, + { + "name": "check-continue-vital-checks", + "type": "switch", + "eventConditions": [ + { + "name": "car-turned-off-condition", + "eventRef": "car-turned-off-event", + "end": true + } + ], + "defaultCondition": { + "transition": "do-car-vital-checks" + } + } + ], + "events": [ + { + "name": "car-turned-on-event", + "type": "car.events", + "source": "my/car" + }, + { + "name": "car-turned-off-event", + "type": "car.events", + "source": "my/car" + } + ] +}``` ```yaml id: checkcarvitals -name: Check Car Vitals Workflow -version: '1.0.0' -specVersion: '0.8' -start: WhenCarIsOn +name: checkcarvitals +description: Check Car Vitals Workflow +version: 1.0.0 +specVersion: "0.8" +start: when-car-is-on states: - - name: WhenCarIsOn - type: event - onEvents: - - eventRefs: - - CarTurnedOnEvent - transition: DoCarVitalChecks - - name: DoCarVitalChecks - type: operation - actions: - - subFlowRef: vitalscheck - sleep: - after: PT1S - transition: CheckContinueVitalChecks - - name: CheckContinueVitalChecks - type: switch - eventConditions: - - name: Car Turned Off Condition - eventRef: CarTurnedOffEvent - end: true - defaultCondition: - transition: DoCarVitalChecks + - name: when-car-is-on + type: event + onEvents: + - eventRefs: + - car-turned-on-event + transition: do-car-vital-checks + - name: do-car-vital-checks + type: operation + actions: + - name: do-car-vital-checks + subFlowRef: vitalscheck + sleep: + after: PT1S + transition: check-continue-vital-checks + - name: check-continue-vital-checks + type: switch + eventConditions: + - name: car-turned-off-condition + eventRef: car-turned-off-event + end: true + defaultCondition: + transition: do-car-vital-checks events: - - name: CarTurnedOnEvent - type: car.events - source: my/car - - name: CarTurnedOffEvent - type: car.events - source: my/car + - name: car-turned-on-event + type: car.events + source: my/car + - name: car-turned-off-event + type: car.events + source: my/car ``` @@ -3694,59 +3835,99 @@ And then our reusable sub-workflow which performs the checking of our car vitals ```json { - "id": "vitalscheck", - "name": "Car Vitals Check", - "version": "1.0.0", - "specVersion": "0.8", - "start": "CheckVitals", - "states": [ - { - "name": "CheckVitals", - "type": "operation", - "actions": [ - { - "functionRef": "Check Tire Pressure" - }, - { - "functionRef": "Check Oil Pressure" - }, - { - "functionRef": "Check Coolant Level" - }, - { - "functionRef": "Check Battery" - } - ], - "end": { - "produceEvents": [ - { - "eventRef": "DisplayChecksOnDashboard", - "data": "${ .evaluations }" - } + "id": "vitalscheck", + "name": "vitalscheck", + "description": "Car Vitals Check", + "version": "1.0.0", + "specVersion": "0.8", + "start": "check-vitals", + "states": [ + { + "name": "check-vitals", + "type": "operation", + "actions": [ + { + "name": "check-tire-pressure", + "functionRef": "check-tire-pressure" + }, + { + "name": "check-oil-pressure", + "functionRef": "check-oil-pressure" + }, + { + "name": "check-coolant-level", + "functionRef": "check-coolant-level" + }, + { + "name": "check-battery", + "functionRef": "check-battery" + } + ], + "end": { + "produceEvents": [ + { + "eventRef": "display-checks-on-dashboard", + "data": "${ .evaluations }" + } + ] + } + } + ], + "functions": [ + { + "name": "check-tire-pressure", + "operation": "mycarservices.json#checktirepressure" + }, + { + "name": "check-oil-pressure", + "operation": "mycarservices.json#checkoilpressure" + }, + { + "name": "check-coolant-level", + "operation": "mycarservices.json#checkcoolantlevel" + }, + { + "name": "check-battery", + "operation": "mycarservices.json#checkbattery" + } ] +}``` - } - } - ], - "functions": [ - { - "name": "checkTirePressure", - "operation": "mycarservices.json#checktirepressure" - }, - { - "name": "checkOilPressure", - "operation": "mycarservices.json#checkoilpressure" - }, - { - "name": "checkCoolantLevel", - "operation": "mycarservices.json#checkcoolantlevel" - }, - { - "name": "checkBattery", - "operation": "mycarservices.json#checkbattery" - } - ] -} + + + +```yaml +id: vitalscheck +name: vitalscheck +description: Car Vitals Check +version: 1.0.0 +specVersion: "0.8" +start: check-vitals +states: + - name: check-vitals + type: operation + actions: + - name: check-tire-pressure + functionRef: check-tire-pressure + - name: check-oil-pressure + functionRef: check-oil-pressure + - name: check-coolant-level + functionRef: check-coolant-level + - name: check-battery + functionRef: check-battery + end: + produceEvents: + - eventRef: display-checks-on-dashboard + data: ${ .evaluations } +functions: + - name: check-tire-pressure + operation: mycarservices.json#checktirepressure + - name: check-oil-pressure + operation: mycarservices.json#checkoilpressure + - name: check-coolant-level + operation: mycarservices.json#checkcoolantlevel + - name: check-battery + operation: mycarservices.json#checkbattery ``` @@ -3834,249 +4015,264 @@ For the sake of the example we assume the functions and event definitions are de ```json { - "id": "booklending", - "name": "Book Lending Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "Book Lending Request", - "states": [ - { - "name": "Book Lending Request", - "type": "event", - "onEvents": [ - { - "eventRefs": ["Book Lending Request Event"] - } - ], - "transition": "Get Book Status" - }, - { - "name": "Get Book Status", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "Get status for book", - "arguments": { - "bookid": "${ .book.id }" - } - } - } - ], - "transition": "Book Status Decision" - }, - { - "name": "Book Status Decision", - "type": "switch", - "dataConditions": [ - { - "name": "Book is on loan", - "condition": "${ .book.status == \"onloan\" }", - "transition": "Report Status To Lender" - }, - { - "name": "Check is available", - "condition": "${ .book.status == \"available\" }", - "transition": "Check Out Book" - } - ], - "defaultCondition": { - "end": true - } - }, - { - "name": "Report Status To Lender", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "Send status to lender", - "arguments": { - "bookid": "${ .book.id }", - "message": "Book ${ .book.title } is already on loan" - } - } - } - ], - "transition": "Wait for Lender response" - }, - { - "name": "Wait for Lender response", - "type": "switch", - "eventConditions": [ - { - "name": "Hold Book", - "eventRef": "Hold Book Event", - "transition": "Request Hold" - }, - { - "name": "Decline Book Hold", - "eventRef": "Decline Hold Event", - "transition": "Cancel Request" - } - ], - "defaultCondition": { - "end": true - } - }, - { - "name": "Request Hold", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "Request hold for lender", - "arguments": { - "bookid": "${ .book.id }", - "lender": "${ .lender }" - } - } - } - ], - "transition": "Sleep two weeks" - }, - { - "name": "Cancel Request", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "Cancel hold request for lender", - "arguments": { - "bookid": "${ .book.id }", - "lender": "${ .lender }" - } - } - } - ], - "transition": "Sleep two weeks" - }, - { - "name": "Sleep two weeks", - "type": "sleep", - "duration": "PT2W", - "transition": "Get Book Status" - }, - { - "name": "Check Out Book", - "type": "operation", - "actions": [ - { - "functionRef": { - "refName": "Check out book with id", - "arguments": { - "bookid": "${ .book.id }" - } - } - }, - { - "functionRef": { - "refName": "Notify Lender for checkout", - "arguments": { - "bookid": "${ .book.id }", - "lender": "${ .lender }" - } - } - } - ], - "end": true - } - ], - "functions": "file://books/lending/functions.json", - "events": "file://books/lending/events.json" -} -``` + "id": "booklending", + "name": "booklending", + "description": "Book Lending Workflow", + "version": "1.0.0", + "specVersion": "0.8", + "start": "book-lending-request", + "states": [ + { + "name": "book-lending-request", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "book-lending-request-event" + ] + } + ], + "transition": "get-book-status" + }, + { + "name": "get-book-status", + "type": "operation", + "actions": [ + { + "name": "get-book-status", + "functionRef": { + "refName": "get-status-for-book", + "arguments": { + "bookid": "${ .book.id }" + } + } + } + ], + "transition": "book-status-decision" + }, + { + "name": "book-status-decision", + "type": "switch", + "dataConditions": [ + { + "name": "book-is-on-loan", + "condition": "${ .book.status == \"onloan\" }", + "transition": "report-status-to-lender" + }, + { + "name": "check-is-available", + "condition": "${ .book.status == \"available\" }", + "transition": "check-out-book" + } + ], + "defaultCondition": { + "end": true + } + }, + { + "name": "report-status-to-lender", + "type": "operation", + "actions": [ + { + "name": "report-status-to-lender", + "functionRef": { + "refName": "send-status-to-lender", + "arguments": { + "bookid": "${ .book.id }", + "message": "Book ${ .book.title } is already on loan" + } + } + } + ], + "transition": "wait-for-lender-response" + }, + { + "name": "wait-for-lender-response", + "type": "switch", + "eventConditions": [ + { + "name": "hold-book", + "eventRef": "hold-book-event", + "transition": "request-hold" + }, + { + "name": "decline-book-hold", + "eventRef": "decline-hold-event", + "transition": "cancel-request" + } + ], + "defaultCondition": { + "end": true + } + }, + { + "name": "request-hold", + "type": "operation", + "actions": [ + { + "name": "request-hold", + "functionRef": { + "refName": "request-hold-for-lender", + "arguments": { + "bookid": "${ .book.id }", + "lender": "${ .lender }" + } + } + } + ], + "transition": "sleep-two-weeks" + }, + { + "name": "cancel-request", + "type": "operation", + "actions": [ + { + "name": "cancel-request", + "functionRef": { + "refName": "cancel-hold-request-for-lender", + "arguments": { + "bookid": "${ .book.id }", + "lender": "${ .lender }" + } + } + } + ], + "transition": "sleep-two-weeks" + }, + { + "name": "sleep-two-weeks", + "type": "sleep", + "duration": "PT2W", + "transition": "get-book-status" + }, + { + "name": "check-out-book", + "type": "operation", + "actions": [ + { + "name": "check-out-book", + "functionRef": { + "refName": "check-out-book-with-id", + "arguments": { + "bookid": "${ .book.id }" + } + } + }, + { + "name": "notify-lender-for-checkout", + "functionRef": { + "refName": "notify-lender-for-checkout", + "arguments": { + "bookid": "${ .book.id }", + "lender": "${ .lender }" + } + } + } + ], + "end": true + } + ], + "functions": "file://books/lending/functions.json", + "events": "file://books/lending/events.json" +}``` ```yaml id: booklending -name: Book Lending Workflow -version: '1.0.0' -specVersion: '0.8' -start: Book Lending Request +name: booklending +description: Book Lending Workflow +version: 1.0.0 +specVersion: "0.8" +start: book-lending-request states: - - name: Book Lending Request - type: event - onEvents: - - eventRefs: - - Book Lending Request Event - transition: Get Book Status - - name: Get Book Status - type: operation - actions: - - functionRef: - refName: Get status for book - arguments: - bookid: "${ .book.id }" - transition: Book Status Decision - - name: Book Status Decision - type: switch - dataConditions: - - name: Book is on loan - condition: ${ .book.status == "onloan" } - transition: Report Status To Lender - - name: Check is available - condition: ${ .book.status == "available" } - transition: Check Out Book - defaultCondition: - end: true - - name: Report Status To Lender - type: operation - actions: - - functionRef: - refName: Send status to lender - arguments: - bookid: "${ .book.id }" - message: Book ${ .book.title } is already on loan - transition: Wait for Lender response - - name: Wait for Lender response - type: switch - eventConditions: - - name: Hold Book - eventRef: Hold Book Event - transition: Request Hold - - name: Decline Book Hold - eventRef: Decline Hold Event - transition: Cancel Request - defaultCondition: + - name: book-lending-request + type: event + onEvents: + - eventRefs: + - book-lending-request-event + transition: get-book-status + - name: get-book-status + type: operation + actions: + - name: get-book-status + functionRef: + refName: get-status-for-book + arguments: + bookid: ${ .book.id } + transition: book-status-decision + - name: book-status-decision + type: switch + dataConditions: + - name: book-is-on-loan + condition: ${ .book.status == "onloan" } + transition: report-status-to-lender + - name: check-is-available + condition: ${ .book.status == "available" } + transition: check-out-book + defaultCondition: + end: true + - name: report-status-to-lender + type: operation + actions: + - name: report-status-to-lender + functionRef: + refName: send-status-to-lender + arguments: + bookid: ${ .book.id } + message: Book ${ .book.title } is already on loan + transition: wait-for-lender-response + - name: wait-for-lender-response + type: switch + eventConditions: + - name: hold-book + eventRef: hold-book-event + transition: request-hold + - name: decline-book-hold + eventRef: decline-hold-event + transition: cancel-request + defaultCondition: + end: true + - name: request-hold + type: operation + actions: + - name: request-hold + functionRef: + refName: request-hold-for-lender + arguments: + bookid: ${ .book.id } + lender: ${ .lender } + transition: sleep-two-weeks + - name: cancel-request + type: operation + actions: + - name: cancel-request + functionRef: + refName: cancel-hold-request-for-lender + arguments: + bookid: ${ .book.id } + lender: ${ .lender } + transition: sleep-two-weeks + - name: sleep-two-weeks + type: sleep + duration: PT2W + transition: get-book-status + - name: check-out-book + type: operation + actions: + - name: check-out-book + functionRef: + refName: check-out-book-with-id + arguments: + bookid: ${ .book.id } + - name: notify-lender-for-checkout + functionRef: + refName: notify-lender-for-checkout + arguments: + bookid: ${ .book.id } + lender: ${ .lender } end: true - - name: Request Hold - type: operation - actions: - - functionRef: - refName: Request hold for lender - arguments: - bookid: "${ .book.id }" - lender: "${ .lender }" - transition: Sleep two weeks - - name: Cancel Request - type: operation - actions: - - functionRef: - refName: Cancel hold request for lender - arguments: - bookid: "${ .book.id }" - lender: "${ .lender }" - end: true - - name: Sleep two weeks - type: sleep - duration: PT2W - transition: Get Book Status - - name: Check Out Book - type: operation - actions: - - functionRef: - refName: Check out book with id - arguments: - bookid: "${ .book.id }" - - functionRef: - refName: Notify Lender for checkout - arguments: - bookid: "${ .book.id }" - lender: "${ .lender }" - end: true functions: file://books/lending/functions.json events: file://books/lending/events.json ``` @@ -4126,87 +4322,90 @@ Its results are then merged back into the state data according to the "toStateDa ```json { - "id": "fillglassofwater", - "name": "Fill glass of water workflow", - "version": "1.0.0", - "specVersion": "0.8", - "start": "Check if full", - "functions": [ - { - "name": "Increment Current Count Function", - "type": "expression", - "operation": ".counts.current += 1 | .counts.current" - } - ], - "states": [ - { - "name": "Check if full", - "type": "switch", - "dataConditions": [ - { - "name": "Need to fill more", - "condition": "${ .counts.current < .counts.max }", - "transition": "Add Water" - }, - { - "name": "Glass full", - "condition": ".counts.current >= .counts.max", - "end": true - } - ], - "defaultCondition": { - "end": true - } - }, - { - "name": "Add Water", - "type": "operation", - "actions": [ - { - "functionRef": "Increment Current Count Function", - "actionDataFilter": { - "toStateData": ".counts.current" - } - } - ], - "transition": "Check if full" - } - ] -} -``` + "id": "fillglassofwater", + "name": "fillglassofwater", + "description": "Fill glass of water workflow", + "version": "1.0.0", + "specVersion": "0.8", + "start": "check-if-full", + "functions": [ + { + "name": "increment-current-count-function", + "type": "expression", + "operation": ".counts.current += 1 | .counts.current" + } + ], + "states": [ + { + "name": "check-if-full", + "type": "switch", + "dataConditions": [ + { + "name": "need-to-fill-more", + "condition": "${ .counts.current < .counts.max }", + "transition": "add-water" + }, + { + "name": "glass-full", + "condition": ".counts.current >= .counts.max", + "end": true + } + ], + "defaultCondition": { + "end": true + } + }, + { + "name": "add-water", + "type": "operation", + "actions": [ + { + "name": "add-water", + "functionRef": "increment-current-count-function", + "actionDataFilter": { + "toStateData": ".counts.current" + } + } + ], + "transition": "check-if-full" + } + ] +}``` ```yaml id: fillglassofwater -name: Fill glass of water workflow -version: '1.0.0' -specVersion: '0.8' -start: Check if full +name: fillglassofwater +description: Fill glass of water workflow +version: 1.0.0 +specVersion: "0.8" +start: check-if-full functions: - - name: Increment Current Count Function - type: expression - operation: ".counts.current += 1 | .counts.current" + - name: increment-current-count-function + type: expression + operation: .counts.current += 1 | .counts.current states: - - name: Check if full - type: switch - dataConditions: - - name: Need to fill more - condition: "${ .counts.current < .counts.max }" - transition: Add Water - - name: Glass full - condition: ".counts.current >= .counts.max" + - name: check-if-full + type: switch + dataConditions: + - name: need-to-fill-more + condition: ${ .counts.current < .counts.max } + transition: add-water + - name: glass-full + condition: .counts.current >= .counts.max + end: true + defaultCondition: end: true - defaultCondition: - end: true - - name: Add Water - type: operation - actions: - - functionRef: Increment Current Count Function - actionDataFilter: - toStateData: ".counts.current" - transition: Check if full + - name: add-water + type: operation + actions: + - name: add-water + functionRef: increment-current-count-function + actionDataFilter: + toStateData: .counts.current + transition: check-if-full ``` @@ -4497,122 +4696,127 @@ We assume that our workflow input has the runtime-imposed quota: ```json { - "id":"notifycustomerworkflow", - "name":"Notify Customer", - "version":"1.0", - "specVersion": "0.8", - "start":"WaitForCustomerEvent", - "states":[ - { - "name":"WaitForCustomerEvent", - "type":"event", - "onEvents":[ - { - "eventRefs":[ - "CustomerEvent" - ], - "eventDataFilter":{ - "data":"${ .customerId }", - "toStateData":"${ .eventCustomerId }" - }, - "actions":[ - { - "functionRef":{ - "refName":"NotifyCustomerFunction", - "arguments":{ - "customerId":"${ .eventCustomerId }" + "id": "notifycustomerworkflow", + "name": "notifycustomerworkflow", + "description": "Notify Customer", + "version": "1.0.0", + "specVersion": "0.8", + "start": "wait-for-customer-event", + "states": [ + { + "name": "wait-for-customer-event", + "type": "event", + "onEvents": [ + { + "eventRefs": [ + "customer-event" + ], + "eventDataFilter": { + "data": "${ .customerId }", + "toStateData": "${ .eventCustomerId }" + }, + "actions": [ + { + "name": "notify-customer-function", + "functionRef": { + "refName": "notify-customer-function", + "arguments": { + "customerId": "${ .eventCustomerId }" + } + } + } + ] + } + ], + "stateDataFilter": { + "output": "${ .count = .count + 1 }" + }, + "transition": "check-event-quota" + }, + { + "name": "check-event-quota", + "type": "switch", + "dataConditions": [ + { + "name": "ready", + "condition": "${ try(.customerCount) != null and .customerCount > .quota.maxConsumedEvents }", + "end": { + "continueAs": { + "workflowId": "notifycustomerworkflow", + "version": "1.0.0", + "data": "${ del(.customerCount) }" + } + } + } + ], + "defaultCondition": { + "transition": "wait-for-customer-event" + } } - } - } - ] - } - ], - "stateDataFilter":{ - "output":"${ .count = .count + 1 }" - }, - "transition":"CheckEventQuota" - }, - { - "name":"CheckEventQuota", - "type":"switch", - "dataConditions":[ - { - "condition":"${ try(.customerCount) != null and .customerCount > .quota.maxConsumedEvents }", - "end":{ - "continueAs": { - "workflowId": "notifycustomerworkflow", - "version": "1.0.0", - "data": "${ del(.customerCount) }" - } - } - } - ], - "defaultCondition":{ - "transition":"WaitForCustomerEvent" - } - } - ], - "events":[ - { - "name":"CustomerEvent", - "type":"org.events.customerEvent", - "source":"customerSource" - } - ], - "functions":[ - { - "name":"NotifyCustomerFunction", - "operation":"http://myapis.org/customerapis.json#notifyCustomer" - } - ] -} -``` + ], + "events": [ + { + "name": "customer-event", + "type": "org.events.customerEvent", + "source": "customerSource" + } + ], + "functions": [ + { + "name": "notify-customer-function", + "operation": "http://myapis.org/customerapis.json#notifyCustomer" + } + ] +}``` ```yaml id: notifycustomerworkflow -name: Notify Customer -version: '1.0.0' -specVersion: '0.8' -start: WaitForCustomerEvent +name: notifycustomerworkflow +description: Notify Customer +version: 1.0.0 +specVersion: "0.8" +start: wait-for-customer-event states: - - name: WaitForCustomerEvent - type: event - onEvents: - - eventRefs: - - CustomerEvent - eventDataFilter: - data: "${ .customerId }" - toStateData: "${ .eventCustomerId }" - actions: - - functionRef: - refName: NotifyCustomerFunction - arguments: - customerId: "${ .eventCustomerId }" - stateDataFilter: - output: "${ .count = .count + 1 }" - transition: CheckEventQuota - - name: CheckEventQuota - type: switch - dataConditions: - - condition: "${ try(.customerCount) != null and .customerCount > .quota.maxConsumedEvents - }" - end: - continueAs: - workflowId: notifycustomerworkflow - version: '1.0.0' - data: "${ del(.customerCount) }" - defaultCondition: - transition: WaitForCustomerEvent + - name: wait-for-customer-event + type: event + onEvents: + - eventRefs: + - customer-event + eventDataFilter: + data: ${ .customerId } + toStateData: ${ .eventCustomerId } + actions: + - name: notify-customer-function + functionRef: + refName: notify-customer-function + arguments: + customerId: ${ .eventCustomerId } + stateDataFilter: + output: ${ .count = .count + 1 } + transition: check-event-quota + - name: check-event-quota + type: switch + dataConditions: + - name: ready + condition: ${ try(.customerCount) != null and .customerCount > + .quota.maxConsumedEvents } + end: + continueAs: + workflowId: notifycustomerworkflow + version: 1.0.0 + data: ${ del(.customerCount) } + defaultCondition: + transition: wait-for-customer-event events: - - name: CustomerEvent - type: org.events.customerEvent - source: customerSource + - name: customer-event + type: org.events.customerEvent + source: customerSource functions: - - name: NotifyCustomerFunction - operation: http://myapis.org/customerapis.json#notifyCustomer + - name: notify-customer-function + operation: http://myapis.org/customerapis.json#notifyCustomer ``` @@ -4659,81 +4863,82 @@ decide which activity to perform based on the transaction value. ```json { - "id": "customerbankingtransactions", - "name": "Customer Banking Transactions Workflow", - "version": "1.0.0", - "specVersion": "0.8", - "autoRetries": true, - "constants": { - "largetxamount" : 5000 - }, - "states": [ - { - "name": "ProcessTransactions", - "type": "foreach", - "inputCollection": "${ .customer.transactions }", - "iterationParam": "${ .tx }", - "actions": [ - { - "name": "Process Larger Transaction", - "functionRef": "Banking Service - Larger Tx", - "condition": "${ .tx >= $CONST.largetxamount }" + "id": "customerbankingtransactions", + "name": "customerbankingtransactions", + "description": "Customer Banking Transactions Workflow", + "version": "1.0.0", + "specVersion": "0.8", + "autoRetries": true, + "constants": { + "largetxamount": 5000 }, - { - "name": "Process Smaller Transaction", - "functionRef": "Banking Service - Smaller Tx", - "condition": "${ .tx < $CONST.largetxamount }" - } - ], - "end": true - } - ], - "functions": [ - { - "name": "Banking Service - Larger Tx", - "type": "asyncapi", - "operation": "banking.yaml#largerTransation" - }, - { - "name": "Banking Service - Smaller T", - "type": "asyncapi", - "operation": "banking.yaml#smallerTransation" - } - ] -} -``` + "states": [ + { + "name": "process-transactions", + "type": "foreach", + "inputCollection": "${ .customer.transactions }", + "iterationParam": "${ .tx }", + "actions": [ + { + "name": "process-larger-transaction", + "functionRef": "banking-service-larger-tx", + "condition": "${ .tx >= $CONST.largetxamount }" + }, + { + "name": "process-smaller-transaction", + "functionRef": "banking-service-smaller-tx", + "condition": "${ .tx < $CONST.largetxamount }" + } + ], + "end": true + } + ], + "functions": [ + { + "name": "banking-service-larger-tx", + "type": "asyncapi", + "operation": "banking.yaml#largerTransation" + }, + { + "name": "banking-service-smaller-tx", + "type": "asyncapi", + "operation": "banking.yaml#smallerTransation" + } + ] +}``` ```yaml id: customerbankingtransactions -name: Customer Banking Transactions Workflow -version: '1.0.0' -specVersion: '0.8' +name: customerbankingtransactions +description: Customer Banking Transactions Workflow +version: 1.0.0 +specVersion: "0.8" autoRetries: true constants: - largetxamount: 5000 + largetxamount: 5000 states: - - name: ProcessTransactions - type: foreach - inputCollection: "${ .customer.transactions }" - iterationParam: "${ .tx }" - actions: - - name: Process Larger Transaction - functionRef: Banking Service - Larger Tx - condition: "${ .tx >= $CONST.largetxamount }" - - name: Process Smaller Transaction - functionRef: Banking Service - Smaller Tx - condition: "${ .tx < $CONST.largetxamount }" - end: true + - name: process-transactions + type: foreach + inputCollection: ${ .customer.transactions } + iterationParam: ${ .tx } + actions: + - name: process-larger-transaction + functionRef: banking-service-larger-tx + condition: ${ .tx >= $CONST.largetxamount } + - name: process-smaller-transaction + functionRef: banking-service-smaller-tx + condition: ${ .tx < $CONST.largetxamount } + end: true functions: - - name: Banking Service - Larger Tx - type: asyncapi - operation: banking.yaml#largerTransation - - name: Banking Service - Smaller T - type: asyncapi - operation: banking.yaml#smallerTransation + - name: banking-service-larger-tx + type: asyncapi + operation: banking.yaml#largerTransation + - name: banking-service-smaller-tx + type: asyncapi + operation: banking.yaml#smallerTransation ``` diff --git a/examples/README_TEMPLATE.md b/examples/README_TEMPLATE.md new file mode 100644 index 00000000..02112ed9 --- /dev/null +++ b/examples/README_TEMPLATE.md @@ -0,0 +1,1829 @@ +# Examples + +Provides Serverless Workflow language examples + +## Table of Contents + +- [Hello World](#Hello-World-Example) +- [Greeting](#Greeting-Example) +- [Event-based greeting (Event State)](#Event-Based-Greeting-Example) +- [Solving Math Problems (ForEach state)](#Solving-Math-Problems-Example) +- [Parallel Execution](#Parallel-Execution-Example) +- [Async Function Invocation](#Async-Function-Invocation-Example) +- [Async SubFlow Invocation](#Async-SubFlow-Invocation-Example) +- [Event Based Transitions (Event-based Switch)](#Event-Based-Transitions-Example) +- [Applicant Request Decision (Data-based Switch + SubFlows)](#Applicant-Request-Decision-Example) +- [Provision Orders (Error Handling)](#Provision-Orders-Example) +- [Monitor Job for completion (Polling)](#Monitor-Job-Example) +- [Send CloudEvent on Workflow Completion](#Send-CloudEvent-On-Workflow-Completion-Example) +- [Monitor Patient Vital Signs (Event state)](#Monitor-Patient-Vital-Signs-Example) +- [Finalize College Application (Event state)](#Finalize-College-Application-Example) +- [Perform Customer Credit Check (Callback state)](#Perform-Customer-Credit-Check-Example) +- [Handle Car Auction Bids (Scheduled start Event state)](#Handle-Car-Auction-Bids-Example) +- [Check Inbox Periodically (Cron-based Workflow start)](#Check-Inbox-Periodically) +- [Event-based service invocation (Event triggered actions)](#Event-Based-Service-Invocation) +- [Reusing Function and Event Definitions](#Reusing-Function-And-Event-Definitions) +- [New Patient Onboarding (Error checking and Retries)](#New-Patient-Onboarding) +- [Purchase order deadline (ExecTimeout)](#Purchase-order-deadline) +- [Accumulate room readings and create timely reports (ExecTimeout and KeepActive)](#Accumulate-room-readings) +- [Car vitals checks (SubFlow Repeat)](#Car-Vitals-Checks) +- [Book Lending Workflow](#Book-Lending) +- [Filling a glass of water (Expression functions)](#Filling-a-glass-of-water) +- [Online Food Ordering](#Online-Food-Ordering) +- [Continuing as a new Execution](#Continuing-as-a-new-Execution) +- [Process Transactions (Foreach State with conditions)](#Process-Transactions) + +### Hello World Example + +#### Description + +In this simple example we use an [Inject State](../specification.md#Inject-State) to inject +`Hello World` in the states data (as the value of the 'result' property). +After the state execution completes, since it is an end state, its data output becomes the workflow +data output, which is: + +```json +{ + "result": "Hello World" +} +``` + +#### Workflow Diagram + +

+Hello World Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Greeting Example + +#### Description + +This example shows a single [Operation State](../specification.md#operation-state) with one action that calls the "greeting" function. +The workflow data input is assumed to be the name of the person to greet: + +```json +{ + "person": { + "name": "John" + } +} +``` + +The results of the action is assumed to be the greeting for the provided persons name: + +```json +{ + "greeting": "Welcome to Serverless Workflow, John!" +} +``` + +Which is added to the states data and becomes the workflow data output. + +#### Workflow Diagram + +

+Greeting Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Event Based Greeting Example + +#### Description + +This example shows a single [Event State](../specification.md#event-state) with one action that calls the "greeting" function. +The event state consumes cloud events of type "greetingEventType". When an event with this type +is consumed, the Event state performs a single action that calls the defined "greeting" function. + +For the sake of the example we assume that the cloud event we will consume has the format: + +```json +{ + "specversion" : "1.0", + "type" : "greetingEventType", + "source" : "greetingEventSource", + "data" : { + "greet": { + "name": "John" + } + } +} +``` + +The results of the action is assumed to be the full greeting for the provided persons name: + +```json +{ + "payload": { + "greeting": "Welcome to Serverless Workflow, John!" + } +} +``` + +Note that in the workflow definition you can see two filters defined. The event data filter defined inside the consume element: + +```json +{ + "eventDataFilter": { + "data": "${ .data.greet } " + } +} +``` + +which is triggered when the greeting event is consumed. It extracts its "data.greet" of the event data (payload) and +merges it with the state data. + +The second, a state data filter, which is defined on the event state itself: + +```json +{ + "stateDataFilter": { + "output": "${ .payload.greeting }" + } +} +``` + +filters what is selected to be the state data output which then becomes the workflow data output (as it is an end state): + +```text + "Welcome to Serverless Workflow, John!" +``` + +#### Workflow Diagram + +

+Event Based Greeting Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Solving Math Problems Example + +#### Description + +In this example we show how to iterate over data using the [ForEach State](../specification.md#foreach-state). +The state will iterate over a collection of simple math expressions which are +passed in as the workflow data input: + +```json + { + "expressions": ["2+2", "4-1", "10x3", "20/2"] + } +``` + +The ForEach state will execute a single defined operation state for each math expression. The operation +state contains an action which calls a serverless function which actually solves the expression +and returns its result. + +Results of all math expressions are accumulated into the data output of the ForEach state which become the final +result of the workflow execution. + +#### Workflow Diagram + +

+Looping Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Parallel Execution Example + +#### Description + +This example uses a [Parallel State](../specification.md#parallel-state) to execute two branches (simple wait states) at the same time. +The completionType type is set to "allOf", which means the parallel state has to wait for both branches +to finish execution before it can transition (end workflow execution in this case as it is an end state). + +#### Workflow Diagram + +

+Parallel Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +We assume that the two referenced workflows, namely `shortdelayworkflowid` and `longdelayworkflowid` both include a single delay state, +with the `shortdelayworkflowid` workflow delay state defining its `timeDelay` property to be shorter than that of the `longdelayworkflowid` workflows +delay state. + +### Async Function Invocation Example + +#### Description + +This example uses a [Operation State](../specification.md#operation-state) to invoke a function async. +This functions sends an email to a customer. +Async function execution is a "fire-and-forget" type of invocation. The function is invoked and workflow execution +does not wait for its results. + +#### Workflow Diagram + +

+Async Function Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Async SubFlow Invocation Example + +#### Description + +This example uses a [Operation State](../specification.md#operation-state) to invoke a [SubFlow](../specification.md#Subflow-Action) async. +This SubFlow is responsible for performing some customer business logic. +Async SubFlow invocation is a "fire-and-forget" type of invocation. The SubFlow is invoked and workflow execution +does not wait for its results. In addition, we specify that the SubFlow should be allowed to continue its execution +event if the parent workflow completes its own execution. This is done by defining the actions `onParentComplete` +property to `continue`. + +#### Workflow Diagram + +

+Async SubFlow Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +For the sake of the example, the definition of "customeronboardingworkflow" workflow invoked as a SubFlow +is not shown. + +### Event Based Transitions Example + +#### Description + +In this example we use an Event-based [Switch State](../specification.md#switch-state) to wait for arrival +of the "VisaApproved", or "VisaRejected" Cloud Events. Depending on which type of event happens, +the workflow performs a different transition. If none of the events arrive in the defined 1 hour timeout +period, the workflow transitions to the "HandleNoVisaDecision" state. + +#### Workflow Diagram + +

+Event Based Switch Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Applicant Request Decision Example + +#### Description + +This example shows off the [Switch State](../specification.md#switch-state) and the subflow action. The workflow is started with application information data as input: + +```json + { + "applicant": { + "fname": "John", + "lname": "Stockton", + "age": 22, + "email": "js@something.com" + } + } +``` + +We use the switch state with two conditions to determine if the application should be made based on the applicants age. +If the applicants age is over 18 we start the application (subflow action). Otherwise the workflow notifies the + applicant of the rejection. + +#### Workflow Diagram + +

+Switch State Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Provision Orders Example + +#### Description + +In this example we show off the states error handling capability. The workflow data input that's passed in contains +missing order information that causes the function in the "ProvisionOrder" state to throw a runtime exception. With the "onErrors" definition we +can transition the workflow to different error handling states. Each type of error +in this example is handled by simple delay states. If no errors are encountered the workflow can transition to the "ApplyOrder" state. + +Workflow data is assumed to me: + +```json + { + "order": { + "id": "", + "item": "laptop", + "quantity": "10" + } + } +``` + +The data output of the workflow contains the information of the exception caught during workflow execution. + +#### Workflow Diagram + +

+Handle Errors Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Monitor Job Example + +#### Description + +In this example we submit a job via an operation state action (serverless function call). It is assumed that it takes some time for +the submitted job to complete and that it's completion can be checked via another separate serverless function call. + +To check for completion we first wait 5 seconds and then get the results of the "CheckJob" serverless function. +Depending on the results of this we either return the results or transition back to waiting and checking the job completion. +This is done until the job submission returns "SUCCEEDED" or "FAILED" and the job submission results are reported before workflow +finishes execution. + +In the case job submission raises a runtime error, we transition to an Operation state which invokes + a sub-flow responsible for handling the job submission issue. + +#### Workflow Diagram + +

+Job Monitoring Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Send CloudEvent On Workflow Completion Example + +#### Description + +This example shows how we can produce a CloudEvent on completion of a workflow. Let's say we have the following +workflow data containing orders that need to be provisioned by our workflow: + +```json +{ + "orders": [{ + "id": "123", + "item": "laptop", + "quantity": "10" + }, + { + "id": "456", + "item": "desktop", + "quantity": "4" + }] +} +``` + +Our workflow in this example uses a ForEach state to provision the orders in parallel. The "provisionOrder" function +used is assumed to have the following results: + +```json +{ + "id": "123", + "outcome": "SUCCESS" +} +``` + +After orders have been provisioned the ForEach states defines the end property which stops workflow execution. +It defines its end definition to be of type "event" in which case a CloudEvent will be produced which can be consumed +by other orchestration workflows or other interested consumers. + +Note that we define the event to be produced in the workflows "events" property. + +The data attached to the event contains the information on provisioned orders by this workflow. So the produced +CloudEvent upon completion of the workflow could look like: + +```json +{ + "specversion" : "1.0", + "type" : "provisionCompleteType", + "datacontenttype" : "application/json", + ... + "data": { + "provisionedOrders": [ + { + "id": "123", + "outcome": "SUCCESS" + }, + { + "id": "456", + "outcome": "FAILURE" + } + ] + } +} +``` + +#### Workflow Diagram + +

+Send CloudEvent on Workflow Completion Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Monitor Patient Vital Signs Example + +#### Description + +In this example a hospital patient is monitored by a Vial Sign Monitoring system. This device can produce three different Cloud Events, namely +"High Body Temperature", "High Blood Pressure", and "High Respiration Rate". +Our workflow which needs to take proper actions depending on the event the Vital Sign Monitor produces needs to start +if any of these events occur. For each of these events a new instance of the workflow is started. + +Since the hospital may include many patients that are being monitored it is assumed that all events include a patientId context attribute in the event + message. We can use the value of this context attribute to associate the incoming events with the same patient as well as + use the patient id to pass as parameter to the functions called by event activities. Here is an example of such event: + +```json +{ + "specversion" : "1.0", + "type" : "org.monitor.highBodyTemp", + "source" : "monitoringSource", + "subject" : "BodyTemperatureReading", + "id" : "A234-1234-1234", + "time" : "2020-01-05T17:31:00Z", + "patientId" : "PID-12345", + "data" : { + "value": "98.6F" + } +} +``` + +As you can see the "patientId" context attribute of the event includes our correlation key which is the unique +patient id. If we set it to be the correlation key in our events definition, all events that are considered must +have the matching patient id. + +#### Workflow Diagram + +

+Monitor Patient Vital Signs Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Finalize College Application Example + +#### Description + +In this example our workflow is instantiated when all requirements of a college application are completed. +These requirements include a student submitting an application, the college receiving the students SAT scores, as well +as a student recommendation letter from a former teacher. + +We assume three Cloud Events "ApplicationSubmitted", "SATScoresReceived" and "RecommendationLetterReceived". +Each include the applicant id in their "applicantId" context attribute, so we can use it to associate these events with an individual applicant. + +Our workflow is instantiated and performs the actions to finalize the college application for a student only +when all three of these events happened (in no particular order). + +#### Workflow Diagram + +

+Finalize College Application Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Perform Customer Credit Check Example + +#### Description + +In this example our serverless workflow needs to integrate with an external microservice to perform +a credit check. We assume that this external microservice notifies a human actor which has to make +the approval decision based on customer information. Once this decision is made the service emits a CloudEvent which +includes the decision information as part of its payload. +The workflow waits for this callback event and then triggers workflow transitions based on the +credit check decision results. + +The workflow data input is assumed to be: + +```json +{ + "customer": { + "id": "customer123", + "name": "John Doe", + "SSN": 123456, + "yearlyIncome": 50000, + "address": "123 MyLane, MyCity, MyCountry", + "employer": "MyCompany" + } +} +``` + +The callback event that our workflow will wait on is assumed to have the following formats. +For approved credit check, for example: + +```json +{ + "specversion" : "1.0", + "type" : "creditCheckCompleteType", + "datacontenttype" : "application/json", + ... + "data": { + "creditCheck": [ + { + "id": "customer123", + "score": 700, + "decision": "Approved", + "reason": "Good credit score" + } + ] + } +} +``` + +And for denied credit check, for example: + +```json +{ + "specversion" : "1.0", + "type" : "creditCheckCompleteType", + "datacontenttype" : "application/json", + ... + "data": { + "creditCheck": [ + { + "id": "customer123", + "score": 580, + "decision": "Denied", + "reason": "Low credit score. Recent late payments" + } + ] + } +} +``` + +#### Workflow Diagram + +

+Perform Customer Credit Check Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Handle Car Auction Bids Example + +#### Description + +In this example our serverless workflow needs to handle bits for an online car auction. The car auction has a specific start +and end time. Bids are only allowed to be made during this time period. All bids before or after this time should not be considered. +We assume that the car auction starts at 9am UTC on March 20th 2020 and ends at 3pm UTC on March 20th 2020. + +Bidding is done via an online application and bids are received as events are assumed to have the following format: + +```json +{ + "specversion" : "1.0", + "type" : "carBidType", + "datacontenttype" : "application/json", + ... + "data": { + "bid": [ + { + "carid": "car123", + "amount": 3000, + "bidder": { + "id": "xyz", + "firstName": "John", + "lastName": "Wayne" + } + } + ] + } +} +``` + +#### Workflow Diagram + +

+Handle Car Auction Bid Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Check Inbox Periodically + +#### Description + +In this example we show the use of scheduled cron-based start event property. The example workflow checks the users inbox every 15 minutes +and send them a text message when there are important emails. + +The results of the inbox service called is expected to be for example: + +```json +{ + "messages": [ + { + "title": "Update your health benefits", + "from": "HR", + "priority": "high" + }, + { + "title": "New job candidate resume", + "from": "Recruiting", + "priority": "medium" + }, + ... + ] +} +``` + +#### Workflow Diagram + +

+Check Inbox Periodically Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Event Based Service Invocation + +#### Description + +In this example we want to make a Veterinary appointment for our dog Mia. The vet service can be invoked only +via an event, and its completion results with the appointment day and time is returned via an event as well. + +This shows a common scenario especially inside container environments where some services may not be exposed via +a resource URI, but only accessible by submitting an event to the underlying container events manager. + +For this example we assume that that payload of the Vet service response event includes an "appointment" +object which contains our appointment info. + +This info is then filtered to become the workflow data output. It could also be used to for example send us an +appointment email, a text message reminder, etc. + +For this example we assume that the workflow instance is started given the following workflow data input: + +```json + { + "patientInfo": { + "name": "Mia", + "breed": "German Shepherd", + "age": 5, + "reason": "Bee sting", + "patientId": "Mia1" + } + } +``` + +#### Workflow Diagram + +

+Vet Appointment Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Reusing Function And Event Definitions + +#### Description + +This example shows how [function](../specification.md#Function-Definition) and [event](../specification.md#Event-Definition) definitions +can be declared independently and referenced by workflow definitions. +This is useful when you would like to reuse event and function definitions across multiple workflows. In those scenarios it allows you to make +changed/updates to these definitions in a single place without having to modify multiple workflows. + +For the example we have two files, namely our "functiondefs.json" and "eventdefs.yml" (to show that they can be expressed in either JSON or YAML). +These hold our function and event definitions which then can be referenced by multiple workflows. + +* functiondefs.json + +```json +{ + "functions": [ + { + "name": "checkFundsAvailability", + "operation": "file://myapis/billingapis.json#checkFunds" + }, + { + "name": "sendSuccessEmail", + "operation": "file://myapis/emailapis.json#paymentSuccess" + }, + { + "name": "sendInsufficientFundsEmail", + "operation": "file://myapis/emailapis.json#paymentInsufficientFunds" + } + ] +} +``` + +* eventdefs.yml + +```yaml +events: +- name: PaymentReceivedEvent + type: payment.receive + source: paymentEventSource + correlation: + - contextAttributeName: accountId +- name: ConfirmationCompletedEvent + type: payment.confirmation + kind: produced + +``` + +In our workflow definition then we can reference these files rather than defining function and events in-line. + +#### Workflow Diagram + +

+Reusing Function and Event Definitions Example +

+ +#### Workflow Definitions + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### New Patient Onboarding + +#### Description + +In this example we want to use a workflow to onboard a new patient (at a hospital for example). +To onboard a patient our workflow is invoked via a "NewPatientEvent" event. This events payload contains the +patient information, for example: + +```json +{ + "name": "John", + "condition": "chest pains" +} +``` + +When this event is received we want to create a new workflow instance and invoke three services +sequentially. The first service we want to invoke is responsible to store patient information, +second is to assign a doctor to a patient given the patient condition, and third to assign a +new appoitment with the patient and the assigned doctor. + +In addition, in this example we need to handle a possible situation where one or all of the needed +services are not available (the server returns a http 503 (Service Unavailable) error). If our workflow +catches this error, we want to try to recover from this by issuing retries for the particular +service invocation that caused the error up to 10 times with three seconds in-between retries. +If the retries are not successful, we want to just gracefully end workflow execution. + +#### Workflow Diagram + +

+Patient Onboarding Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +#### Workflow Demo + +This example is used in our Serverless Workflow Hands-on series videos [#1](https://www.youtube.com/watch?v=0gmpuGLP-_o) +and [#2](https://www.youtube.com/watch?v=6A6OYp5nygg). + +### Purchase order deadline + +#### Description + +In this example our workflow processes purchase orders. An order event triggers instance of our workflow. +To complete the created order, our workflow must first wait for an order confirmation event (correlated to the +order id), and then wait for the shipment sent event (also correlated to initial order id). +We do not want to place an exact timeout limit for waiting for the confirmation and shipment events, +as this might take a different amount of time depending on the size of the order. However we do have the requirement +that a total amount of time for the order to be confirmed, once its created, is 30 days. +If the created order is not completed within 30 days it needs to be automatically closed. + +This example shows the use of the workflow [execTimeout definition](../specification.md#ExecTimeout-Definition). + +#### Workflow Diagram + +

+Purchase Order Deadline Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Accumulate room readings + +#### Description + +In this example we have two IoT sensors for each room in our house. One reads temperature values +and the other humidity values of each room. We get these measurements for each of our rooms +as CloudEvents. We can correlate events send by our sensors by the room it is in. + +For the example we want to accumulate the temperature and humidity values for each and send hourly reports +to the home owner for each room. + +**Note:** In this example each rooms measurements will be accumulated by a single workflow instance per room. +Once we receive events for 1 hour (per room) each of the room-based workflow instances will create the report. Events +consumed after the report is created will trigger a new instance of our workflow (again, per room), accumulate +the data for an hour, send report, and so on. + +#### Workflow Diagram + +

+Accumulate Room Readings Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Car Vitals Checks + +#### Description + +In this example we need to check car vital signs while our car is driving. +The workflow should start when we receive the "CarTurnedOnEvent" event and stop when the "CarTurnedOffEvent" event is consumed. +While the car is driving our workflow should repeatedly check the vitals every 1 second. + +For this example we use the workflow [SubFlow](../specification.md#SubFlow-Action) actions to perform the vital checks. + +#### Workflow Diagram + +

+Check Car Vitals Example +

+ +#### Workflow Definition + +We fist define our top-level workflow for this example: + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +And then our reusable sub-workflow which performs the checking of our car vitals: + + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + + + +```yaml +id: vitalscheck +name: Car Vitals Check +version: '1.0.0' +specVersion: '0.8' +start: CheckVitals +states: + - name: CheckVitals + type: operation + actions: + - functionRef: Check Tire Pressure + - functionRef: Check Oil Pressure + - functionRef: Check Coolant Level + - functionRef: Check Battery + end: + produceEvents: + - eventRef: DisplayChecksOnDashboard + data: "${ .evaluations }" +functions: + - name: checkTirePressure + operation: mycarservices.json#checktirepressure + - name: checkOilPressure + operation: mycarservices.json#checkoilpressure + - name: checkCoolantLevel + operation: mycarservices.json#checkcoolantlevel + - name: checkBattery + operation: mycarservices.json#checkbattery +``` + +
+ +### Book Lending + +#### Description + +In this example we want to create a book lending workflow. The workflow starts when a lender +submits a book lending request (via event "Book Lending Request Event"). +The workflow describes our business logic around lending a book, from checking its current availability, +to waiting on the lender's response if the book is currently not available, to checking out the book and notifying +the lender. + +This example expects the "Book Lending Request Event" event to have a payload of for example: + +```json +{ + "book": { + "title": " ... ", + "id": " ... " + }, + "lender": { + "name": "John Doe", + "address": " ... ", + "phone": " ... " + } +} +``` + +where the "book" property defines the book to be lent out, and the "lender" property provides info +about the person wanting to lend the book. + +For the sake of the example we assume the functions and event definitions are defined in separate JSON files. + +#### Workflow Diagram + +

+Book Lending Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Filling a glass of water + +#### Description + +In this example we showcase the power of [expression functions](../specification.md#Using-Functions-For-Expression-Evaluation). +Our workflow definition is assumed to have the following data input: + +```json +{ + "counts": { + "current": 0, + "max": 10 + } +} +``` + +Our workflow simulates filling up a glass of water one "count" at a time until "max" count is reached which +represents our glass is full. +Each time we increment the current count, the workflow checks if we need to keep refilling the glass. +If the current count reaches the max count, the workflow execution ends. +To increment the current count, the workflow invokes the "IncrementCurrent" expression function. +Its results are then merged back into the state data according to the "toStateData" property of the event data filter. + +#### Workflow Diagram + +

+Fill Glass of Water Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Online Food Ordering + +#### Description + +In this example we want to create an online food ordering workflow. The below image outlines the workflow +structure and the available services: + +

+Online Food Ordering Structure +

+ +Our workflow starts with the "Place Order" [Subflow](../specification.md#SubFlow-Action), which is responsible +to send the received order to the requested restaurant and the estimated order ETA. +We then wait for the ETA time when our workflow should go into the "Deliver Order" SubFlow, responsible +for dispatching a Courier and sending her/him off to pick up the order. Once the order is picked up, the Courier needs to deliver the order to the customer. +After the order has been delivered to the customer, our workflow needs to charge the customer. + +Our workflow needs to communicate with three services during its execution, namely the Order, Delivery, and +the Payment services. + +For the sake of the example, we assume that our workflow can communicate to the Order and Delivery services via REST and the Payment service via gRPC. +Let's start by defining an example CloudEvent which triggers an instance of our workflow. +This event can be sent by a web UI, for example, or be pushed onto a Kafka/MQTT topic to start our order workflow. + +```json +{ + "specversion": "1.0", + "type": "org.orders", + "source": "/orders/", + "subject": "Food Order", + "id": "A234-1234-1234", + "time": "2021-03-05T17:31:00Z", + "orderid": "ORDER-12345", + "data": { + "id": "ORDER-12345", + "customerId": "CUSTOMER-12345", + "status": [], + "order": { + "restaurantId": "RESTAURANT-54321", + "items": [ + { + "itemId": "ITEM-8765", + "amount": 1, + "addons": "" + } + ] + }, + "delivery":{ + "address": "1234 MyStreet, MyCountry", + "type": "contactless", + "requestedTime": "ASAP", + "location": "Front door", + "instructions": "" + } + } +} +``` + +Note the `orderid` CloudEvent context attribute, which contains the unique ID of the order specified in this event. [Event correlation](../specification.md#Correlation-Definition) is done against CE context attributes, and as such, to be able +to correlate multiple order events to the same order id, it needs to be part of the CE context attributes, and +not its data (payload). + +Now let's start defining our workflow. For the sake of this example, let's define our function and event definitions +as separate YAML files (and then reference them inside our workflow definition). This is useful in cases +when you want to reuse them between multiple workflow definitions. + +#### Workflow Event Definition + +``` yaml +events: +- name: Food Order Event + source: "/orders/" + type: org.orders + correlation: + - contextAttributeName: orderid +- name: ETA Deadline Event + source: "/orderseta" + type: org.orders.eta + correlation: + - contextAttributeName: orderid +- name: Order Picked Up Event + source: "/orderspickup" + type: org.orders.delivery + correlation: + - contextAttributeName: orderid +- name: Order Delievered Event + source: "/orderdelivery" + type: org.orders.delivery + correlation: + - contextAttributeName: orderid +``` + +#### Workflow Function Definition + +``` yaml +functions: +- name: Submit Order Function + operation: http://myorderservice.org/orders.json#submit +- name: Get Order ETA Function + operation: http://myorderservice.org/orders.json#orderETA +- name: Dispatch Courrier Function + operation: http://mydeliveryservice.org/deliveries.json#dispatch +- name: Deliver Order Function + operation: http://mydeliveryservice.org/deliveries.json#deliver +- name: Charge For Order Function + operation: http://mypaymentservice.org/payments.proto#PaymentService#ChargeUser +``` + +#### Main Workflow Definition + +With the function and event definitions in place we can now start writing our main workflow definition: + +```yaml +id: foodorderworkflow +name: Food Order Workflow +version: '1.0.0' +specVersion: '0.8' +start: Place Order +functions: file://orderfunctions.yml +events: file://orderevents.yml +states: +- name: Place Order + type: operation + actions: + - subFlowRef: placeorderworkflow + transition: Wait for ETA Deadline +- name: Wait for ETA Deadline + type: event + onEvents: + - eventRefs: + - ETA Deadline Event + eventDataFilter: + data: "${ .results.status }" + toStateData: "${ .status }" + transition: Deliver Order +- name: Deliver Order + type: operation + actions: + - subFlowRef: deliverorderworkflow + transition: Charge For Order +- name: Charge For Order + type: operation + actions: + - functionRef: + refName: Charge For Order Function + arguments: + order: "${ .order.id }" + actionDataFilter: + results: "${ .outcome.status }" + toStateData: "${ .status }" + stateDataFilter: + output: '${ . | {"orderid": .id, "orderstatus": .status} | .orderstatus += ["Order + Completed"] }' + end: true +``` + +With this in place we can start defining our sub-workflows: + +#### Place Order Sub-Workflow + +```yaml +id: placeorderworkflow +name: Place Order Workflow +version: '1.0.0' +specVersion: '0.8' +start: Submit Order +states: +- name: Submit Order + type: event + onEvents: + - eventRefs: + - Food Order Event + actions: + - functionRef: + refName: Submit Order Function + arguments: + order: "${ .order }" + actionDataFilter: + results: "${ .results.status }" + toStateData: "${ .status }" + - functionRef: + refName: Get Order ETA Function + arguments: + customer: "${ .customerId }" + restaurantid: "${ .order.restaurantId }" + delivery: " ${ .delivery }" + actionDataFilter: + results: "${ .results.status }" + toStateData: "${ .status }" + end: true +``` + +#### Deliver Order Sub-Workflow + +```yaml +id: deliverorderworkflow +name: Deliver Order Workflow +version: '1.0.0' +specVersion: '0.8' +start: Dispatch Courier +states: +- name: Dispatch Courier + type: operation + actions: + - functionRef: Dispatch Courrier Function + transition: Wait for Order Pickup +- name: Wait for Order Pickup + type: event + onEvents: + - eventRefs: + - Order Picked Up Event + eventDataFilter: + data: "${ .data.status }" + toStateData: "${ .status }" + actions: + - functionRef: Deliver Order Function + transition: Wait for Delivery Confirmation +- name: Wait for Delivery Confirmation + type: event + onEvents: + - eventRefs: + - Order Delievered Event + eventDataFilter: + data: "${ .data.status }" + toStateData: "${ .status }" + end: true +``` + +#### Workflow Results + +For the example order event, the workflow output for a successful completion would look like for example: + +```json +{ + "orderid": "ORDER-12345", + "orderstatus": [ + "Order Submitted", + "Order ETA Received", + "Order Picked up", + "Order Delievered", + "Order Charged", + "Order Completed" + ] +} +``` + +### Continuing as a new Execution + +#### Description + +Some runtime implementations on which we run our workflows can have different quotas, such as maximum execution durations, maximum consumed events, etc. We can use the Serverless workflow "continueAs" functionality that can be used to stop the current workflow execution and start another one (of the same or a different type). This is very useful in cases where we have to ensure we don't reach the imposed quotas of single workflow execution. + +This example assumes that the runtime we are using has a quota set to a maximum of one thousand consumed events per single workflow execution. +Our sample workflow consumes a single customer event at a time and invokes the `emailCustomer` function. +Note that we do not set a workflow `workflowExecTimeout`, so we intend to have a long-running workflow. However, because of the runtime restriction, in this case, we would run into the event consume limit, and our workflow would have to terminate. We can fix this problem by using [`continueAs`](../specification.md#Continuing-as-a-new-Execution), which will allow us to make sure that we reach the given limit and then continue our workflow execution as a new run. + +We assume that our workflow input has the runtime-imposed quota: + +```json +{ + "quota": { + "maxConsumedEvents": 1000 + } +} +``` + +#### Workflow Diagram + +

+ContinueAs Example +

+ +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
+ +### Process Transactions + +#### Description + +This example shows how we can loop through a data input array (in parallel), and decide which action to perform +depending on the value of each element in the input array. +We use the [action definition](../specification.md#Action-Definition) `condition` property to perform the action that +is best suited for the transaction value. +Note that in this example we set the "large transaction amount" as a [workflow constant](../specification.md#Workflow-Constants). +There are other ways to set +this value, for example passing it as [workflow data input](../specification.md#Workflow-Data-Input), +or if this data is sensitive, to use [workflow secrets](../specification.md#Workflow-Secrets). + +For the example, we assume the following workflow data input: + +```json +{ + "customer": { + "id": "abc123", + "name": "John Doe", + "transactions": [1000, 400, 60, 7000, 12000, 250] + } +} +``` + +We use the [ForeEach workflow state](../specification.md#ForEach-State) to iterate through customer transactions (in parallel), and +decide which activity to perform based on the transaction value. + +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +``` + + + +```yaml +``` + +
diff --git a/examples/roomreadings.json b/examples/accumulate-room-readings.json similarity index 100% rename from examples/roomreadings.json rename to examples/accumulate-room-readings.json diff --git a/examples/applicantrequest.json b/examples/applicant-request-decision.json similarity index 100% rename from examples/applicantrequest.json rename to examples/applicant-request-decision.json diff --git a/examples/sendcustomeremail.json b/examples/async-function-invocation.json similarity index 100% rename from examples/sendcustomeremail.json rename to examples/async-function-invocation.json diff --git a/examples/onboardcustomer.json b/examples/async-subflow-invocation.json similarity index 100% rename from examples/onboardcustomer.json rename to examples/async-subflow-invocation.json diff --git a/examples/booklending.json b/examples/book-lending.json similarity index 100% rename from examples/booklending.json rename to examples/book-lending.json diff --git a/examples/vitalscheck.json b/examples/car-vitals-checks-subflow.json similarity index 100% rename from examples/vitalscheck.json rename to examples/car-vitals-checks-subflow.json diff --git a/examples/checkcarvitals.json b/examples/car-vitals-checks.json similarity index 100% rename from examples/checkcarvitals.json rename to examples/car-vitals-checks.json diff --git a/examples/checkinbox.json b/examples/check-inbox-periodically.json similarity index 100% rename from examples/checkinbox.json rename to examples/check-inbox-periodically.json diff --git a/examples/notifycustomerworkflow.json b/examples/continuing-as-a-new-execution.json similarity index 100% rename from examples/notifycustomerworkflow.json rename to examples/continuing-as-a-new-execution.json diff --git a/examples/eventbasedgreeting.json b/examples/event-based-greeting.json similarity index 100% rename from examples/eventbasedgreeting.json rename to examples/event-based-greeting.json diff --git a/examples/vetappointmentworkflow.json b/examples/event-based-service-invocation.json similarity index 100% rename from examples/vetappointmentworkflow.json rename to examples/event-based-service-invocation.json diff --git a/examples/eventbasedswitchstate.json b/examples/event-based-transitions.json similarity index 100% rename from examples/eventbasedswitchstate.json rename to examples/event-based-transitions.json diff --git a/examples/fillglassofwater.json b/examples/filling-a-glass-of-water.json similarity index 100% rename from examples/fillglassofwater.json rename to examples/filling-a-glass-of-water.json diff --git a/examples/finalizecollegeapplication.json b/examples/finalize-college-application.json similarity index 100% rename from examples/finalizecollegeapplication.json rename to examples/finalize-college-application.json diff --git a/examples/handlecarauctionbid.json b/examples/handle-car-auction-bids.json similarity index 100% rename from examples/handlecarauctionbid.json rename to examples/handle-car-auction-bids.json diff --git a/examples/helloworld.json b/examples/hello-world.json similarity index 100% rename from examples/helloworld.json rename to examples/hello-world.json diff --git a/examples/jobmonitoring.json b/examples/monitor-job.json similarity index 100% rename from examples/jobmonitoring.json rename to examples/monitor-job.json diff --git a/examples/patientvitalsworkflow.json b/examples/monitor-patient-vital-signs.json similarity index 100% rename from examples/patientvitalsworkflow.json rename to examples/monitor-patient-vital-signs.json diff --git a/examples/patientonboard.json b/examples/new-patient-onboarding.json similarity index 100% rename from examples/patientonboard.json rename to examples/new-patient-onboarding.json diff --git a/examples/parallelexec.json b/examples/parallel-execution.json similarity index 100% rename from examples/parallelexec.json rename to examples/parallel-execution.json diff --git a/examples/customercreditcheck.json b/examples/perform-customer-credit-check.json similarity index 100% rename from examples/customercreditcheck.json rename to examples/perform-customer-credit-check.json diff --git a/examples/customerbankingtransactions.json b/examples/process-transactions.json similarity index 100% rename from examples/customerbankingtransactions.json rename to examples/process-transactions.json diff --git a/examples/provisionorders.json b/examples/provision-orders.json similarity index 100% rename from examples/provisionorders.json rename to examples/provision-orders.json diff --git a/examples/order.json b/examples/purchase-order-deadline.json similarity index 100% rename from examples/order.json rename to examples/purchase-order-deadline.json diff --git a/examples/paymentconfirmation.json b/examples/reusing-function-and-event-definitions.json similarity index 100% rename from examples/paymentconfirmation.json rename to examples/reusing-function-and-event-definitions.json diff --git a/examples/sendcloudeventonprovision.json b/examples/send-cloudevent-on-workflow-completion.json similarity index 100% rename from examples/sendcloudeventonprovision.json rename to examples/send-cloudevent-on-workflow-completion.json diff --git a/examples/solvemathproblems.json b/examples/solving-math-problems.json similarity index 100% rename from examples/solvemathproblems.json rename to examples/solving-math-problems.json