diff --git a/ctk/features/call.feature b/ctk/features/call.feature index 6ebcfbe2..0db32456 100644 --- a/ctk/features/call.feature +++ b/ctk/features/call.feature @@ -15,14 +15,13 @@ Feature: Call Task namespace: default name: http-call-with-content-output do: - getFirstAvailablePet: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/findByStatus?status={status} - output: - from: .[0] + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/findByStatus?status={status} + output: + from: .[0] """ And given the workflow input is: """yaml @@ -43,13 +42,12 @@ Feature: Call Task namespace: default name: http-call-with-response-output do: - getPetById: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/{petId} - output: response + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/{petId} + output: response """ And given the workflow input is: """yaml @@ -70,16 +68,15 @@ Feature: Call Task namespace: default name: http-call-with-basic-auth do: - getSecuredEndpoint: - call: http - with: - method: get - endpoint: - uri: https://httpbin.org/basic-auth/{username}/{password} - authentication: - basic: - username: ${ .username } - password: ${ .password } + call: http + with: + method: get + endpoint: + uri: https://httpbin.org/basic-auth/{username}/{password} + authentication: + basic: + username: ${ .username } + password: ${ .password } """ And given the workflow input is: """yaml @@ -99,16 +96,15 @@ Feature: Call Task namespace: default name: openapi-call-with-content-output do: - getPetsByStatus: - call: openapi - with: - document: - uri: https://petstore.swagger.io/v2/swagger.json - operation: findPetsByStatus - parameters: - status: ${ .status } - output: - from: . | length + call: openapi + with: + document: + uri: https://petstore.swagger.io/v2/swagger.json + operation: findPetsByStatus + parameters: + status: ${ .status } + output: + from: . | length """ And given the workflow input is: """yaml @@ -127,15 +123,14 @@ Feature: Call Task namespace: default name: openapi-call-with-response-output do: - getPetById: - call: openapi - with: - document: - uri: https://petstore.swagger.io/v2/swagger.json - operation: getPetById - parameters: - petId: ${ .petId } - output: response + call: openapi + with: + document: + uri: https://petstore.swagger.io/v2/swagger.json + operation: getPetById + parameters: + petId: ${ .petId } + output: response """ And given the workflow input is: """yaml diff --git a/ctk/features/composite.feature b/ctk/features/composite.feature index aad61fc0..0cc3b799 100644 --- a/ctk/features/composite.feature +++ b/ctk/features/composite.feature @@ -12,16 +12,15 @@ Feature: Composite Task namespace: default name: composite-sequential do: - setRGB: - execute: - sequentially: - setRed: + execute: + sequentially: + - setRed: set: colors: ${ .colors + ["red"] } - setGreen: + - setGreen: set: colors: ${ .colors + ["green"] } - setBlue: + - setBlue: set: colors: ${ .colors + ["blue"] } """ @@ -40,19 +39,18 @@ Feature: Composite Task namespace: default name: composite-sequential do: - setRGB: - execute: - concurrently: - setRed: + execute: + compete: true + concurrently: + - setRed: set: colors: ${ .colors + ["red"] } - setGreen: + - setGreen: set: colors: ${ .colors + ["green"] } - setBlue: + - setBlue: set: colors: ${ .colors + ["blue"] } - compete: true """ When the workflow is executed Then the workflow should complete diff --git a/ctk/features/data-flow.feature b/ctk/features/data-flow.feature index 7b06f226..6c92e081 100644 --- a/ctk/features/data-flow.feature +++ b/ctk/features/data-flow.feature @@ -12,11 +12,10 @@ Feature: Data Flow namespace: default name: output-filtering do: - setUsername: - input: - from: .user.claims.subject #filters the input of the task, using only the user's subject - set: - playerId: ${ . } + input: + from: .user.claims.subject #filters the input of the task, using only the user's subject + set: + playerId: ${ . } """ And given the workflow input is: """yaml @@ -39,14 +38,13 @@ Feature: Data Flow namespace: default name: output-filtering do: - getPetById: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/{petId} #simple interpolation, only possible with top level variables - output: - from: .id #filters the output of the http call, using only the id of the returned object + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/{petId} #simple interpolation, only possible with top level variables + output: + from: .id #filters the output of the http call, using only the id of the returned object """ And given the workflow input is: """yaml @@ -67,22 +65,24 @@ Feature: Data Flow namespace: default name: non-object-output do: - getPetById1: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/{petId} #simple interpolation, only possible with top level variables - output: - from: .id - getPetById2: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/2 - output: - from: '{ ids: [ $input, .id ] }' + execute: + sequentially: + - getPetById1: + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/{petId} #simple interpolation, only possible with top level variables + output: + from: .id + - getPetById2: + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/2 + output: + from: '{ ids: [ $input, .id ] }' """ When the workflow is executed Then the workflow should complete with output: diff --git a/ctk/features/emit.feature b/ctk/features/emit.feature index 8954a4c2..4531be46 100644 --- a/ctk/features/emit.feature +++ b/ctk/features/emit.feature @@ -12,14 +12,13 @@ Feature: Emit Task namespace: default name: emit do: - emitUserGreeted: - emit: - event: - with: - source: https://fake-source.com - type: com.fake-source.user.greeted.v1 - data: - greetings: ${ "Hello \(.user.firstName) \(.user.lastName)!" } + emit: + event: + with: + source: https://fake-source.com + type: com.fake-source.user.greeted.v1 + data: + greetings: ${ "Hello \(.user.firstName) \(.user.lastName)!" } """ And given the workflow input is: """yaml diff --git a/ctk/features/flow.feature b/ctk/features/flow.feature index 705a34b3..cad4a786 100644 --- a/ctk/features/flow.feature +++ b/ctk/features/flow.feature @@ -11,15 +11,17 @@ Feature: Flow Directive namespace: default name: implicit-sequence do: - setRed: - set: - colors: '${ .colors + [ "red" ] }' - setGreen: - set: - colors: '${ .colors + [ "green" ] }' - setBlue: - set: - colors: '${ .colors + [ "blue" ] }' + execute: + sequentially: + - setRed: + set: + colors: '${ .colors + [ "red" ] }' + - setGreen: + set: + colors: '${ .colors + [ "green" ] }' + - setBlue: + set: + colors: '${ .colors + [ "blue" ] }' """ When the workflow is executed Then the workflow should complete with output: @@ -38,18 +40,20 @@ Feature: Flow Directive namespace: default name: explicit-sequence do: - setRed: - set: - colors: '${ .colors + [ "red" ] }' - then: setGreen - setBlue: - set: - colors: '${ .colors + [ "blue" ] }' - then: end - setGreen: - set: - colors: '${ .colors + [ "green" ] }' - then: setBlue + execute: + sequentially: + - setRed: + set: + colors: '${ .colors + [ "red" ] }' + then: setGreen + - setBlue: + set: + colors: '${ .colors + [ "blue" ] }' + then: end + - setGreen: + set: + colors: '${ .colors + [ "green" ] }' + then: setBlue """ When the workflow is executed Then the workflow should complete with output: diff --git a/ctk/features/for.feature b/ctk/features/for.feature index 43c17e04..0423622f 100644 --- a/ctk/features/for.feature +++ b/ctk/features/for.feature @@ -14,13 +14,12 @@ Feature: For Task namespace: default name: for do: - forEachColor: - for: - each: color - in: '.colors' - do: - set: - processed: '${ { colors: (.processed.colors + [ $color ]), indexes: (.processed.indexes + [ $index ])} }' + for: + each: color + in: '.colors' + do: + set: + processed: '${ { colors: (.processed.colors + [ $color ]), indexes: (.processed.indexes + [ $index ])} }' """ And given the workflow input is: """yaml diff --git a/ctk/features/raise.feature b/ctk/features/raise.feature index 58567530..e7df2459 100644 --- a/ctk/features/raise.feature +++ b/ctk/features/raise.feature @@ -11,12 +11,11 @@ Feature: Raise Task namespace: default name: raise-custom-error do: - raiseComplianceError: - raise: - error: - status: 400 - type: https://serverlessworkflow.io/errors/types/compliance - title: Compliance Error + raise: + error: + status: 400 + type: https://serverlessworkflow.io/errors/types/compliance + title: Compliance Error """ When the workflow is executed Then the workflow should fault with error: diff --git a/ctk/features/set.feature b/ctk/features/set.feature index 6d748ed9..f9cbb64a 100644 --- a/ctk/features/set.feature +++ b/ctk/features/set.feature @@ -12,11 +12,10 @@ Feature: Set Task namespace: default name: set do: - initialize: - set: - shape: circle - size: ${ .configuration.size } - fill: ${ .configuration.fill } + set: + shape: circle + size: ${ .configuration.size } + fill: ${ .configuration.fill } """ And given the workflow input is: """yaml diff --git a/ctk/features/switch.feature b/ctk/features/switch.feature index 01471cc0..f101bef9 100644 --- a/ctk/features/switch.feature +++ b/ctk/features/switch.feature @@ -11,29 +11,31 @@ Feature: Switch Task namespace: default name: switch-match do: - switchColor: - switch: - red: - when: '.color == "red"' - then: setRed - green: - when: '.color == "green"' - then: setGreen - blue: - when: '.color == "blue"' - then: setBlue - setRed: - set: - colors: '${ .colors + [ "red" ] }' - then: end - setGreen: - set: - colors: '${ .colors + [ "green" ] }' - then: end - setBlue: - set: - colors: '${ .colors + [ "blue" ] }' - then: end + execute: + sequentially; + - switchColor: + switch: + - red: + when: '.color == "red"' + then: setRed + - green: + when: '.color == "green"' + then: setGreen + - blue: + when: '.color == "blue"' + then: setBlue + - setRed: + set: + colors: '${ .colors + [ "red" ] }' + then: end + - setGreen: + set: + colors: '${ .colors + [ "green" ] }' + then: end + - setBlue: + set: + colors: '${ .colors + [ "blue" ] }' + then: end """ And given the workflow input is: """yaml @@ -55,27 +57,29 @@ Feature: Switch Task namespace: default name: switch-default-implicit do: - switchColor: - switch: - red: - when: '.color == "red"' - then: setRed - green: - when: '.color == "green"' - then: setGreen - blue: - when: '.color == "blue"' - then: setBlue - then: end - setRed: - set: - colors: '${ .colors + [ "red" ] }' - setGreen: - set: - colors: '${ .colors + [ "green" ] }' - setBlue: - set: - colors: '${ .colors + [ "blue" ] }' + execute: + sequentially: + - switchColor: + switch: + - red: + when: '.color == "red"' + then: setRed + - green: + when: '.color == "green"' + then: setGreen + - blue: + when: '.color == "blue"' + then: setBlue + then: end + - setRed: + set: + colors: '${ .colors + [ "red" ] }' + - setGreen: + set: + colors: '${ .colors + [ "green" ] }' + - setBlue: + set: + colors: '${ .colors + [ "blue" ] }' """ And given the workflow input is: """yaml @@ -97,31 +101,33 @@ Feature: Switch Task namespace: default name: switch-default-implicit do: - switchColor: - switch: - red: - when: '.color == "red"' - then: setRed - green: - when: '.color == "green"' - then: setGreen - blue: - when: '.color == "blue"' - then: setBlue - anyOtherColor: - then: setCustomColor - setRed: - set: - colors: '${ .colors + [ "red" ] }' - setGreen: - set: - colors: '${ .colors + [ "green" ] }' - setBlue: - set: - colors: '${ .colors + [ "blue" ] }' - setCustomColor: - set: - colors: '${ .colors + [ $input.color ] }' + execute: + sequentially: + - switchColor: + switch: + - red: + when: '.color == "red"' + then: setRed + - green: + when: '.color == "green"' + then: setGreen + - blue: + when: '.color == "blue"' + then: setBlue + - anyOtherColor: + then: setCustomColor + - setRed: + set: + colors: '${ .colors + [ "red" ] }' + - setGreen: + set: + colors: '${ .colors + [ "green" ] }' + - setBlue: + set: + colors: '${ .colors + [ "blue" ] }' + - setCustomColor: + set: + colors: '${ .colors + [ $input.color ] }' """ And given the workflow input is: """yaml diff --git a/ctk/features/try.feature b/ctk/features/try.feature index cd5fb272..893f9ff6 100644 --- a/ctk/features/try.feature +++ b/ctk/features/try.feature @@ -15,22 +15,21 @@ Feature: Try Task namespace: default name: try-catch-404 do: - tryGetPet: - try: - call: http + try: + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/getPetByName/{petName} + catch: + errors: with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/getPetByName/{petName} - catch: - errors: - with: - type: https://serverlessworkflow.io/dsl/errors/types/communication - status: 404 - as: err - do: - set: - error: ${ $err } + type: https://serverlessworkflow.io/dsl/errors/types/communication + status: 404 + as: err + do: + set: + error: ${ $err } """ And given the workflow input is: """yaml @@ -56,22 +55,21 @@ Feature: Try Task namespace: default name: try-catch-503 do: - tryGetPet: - try: - call: http + try: + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/getPetByName/{petName} + catch: + errors: with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/getPetByName/{petName} - catch: - errors: - with: - type: https://serverlessworkflow.io/dsl/errors/types/communication - status: 503 - as: err - do: - set: - error: ${ $err } + type: https://serverlessworkflow.io/dsl/errors/types/communication + status: 503 + as: err + do: + set: + error: ${ $err } """ And given the workflow input is: """yaml diff --git a/dsl-reference.md b/dsl-reference.md index 64d3beb7..5631d019 100644 --- a/dsl-reference.md +++ b/dsl-reference.md @@ -66,7 +66,7 @@ A [workflow](#workflow) serves as a blueprint outlining the series of [tasks](#t | document | [`document`](#document) | `yes` | Documents the defined workflow. | | input | [`input`](#input) | `no` | Configures the workflow's input. | | use | [`use`](#use) | `no` | Defines the workflow's reusable components, if any. | -| do | [`map[string, task]`](#task) | `yes` | The [task(s)](#task) that must be performed by the [workflow](#workflow). | +| do | [`task`](#task) | `yes` | The [task](#task) that must be performed by the [workflow](#workflow). | | timeout | [`timeout`](#timeout) | `no` | The configuration, if any, of the workflow's timeout. | | output | [`output`](#output) | `no` | Configures the workflow's output. | | schedule | [`schedule`](#schedule) | `no` | Configures the workflow's schedule, if any. | @@ -94,7 +94,7 @@ Defines the workflow's reusable components. |:--|:---:|:---:|:---| | authentications | [`map[string, authentication]`](#authentication) | `no` | A name/value mapping of the workflow's reusable authentication policies. | | errors | [`map[string, error]`](#error) | `no` | A name/value mapping of the workflow's reusable errors. | -| extensions | [`map[string, extension]`](#extension) | `no` | A name/value mapping of the workflow's reusable extensions. | +| extensions | [`map[string, extension][]`](#extension) | `no` | A list of the workflow's reusable extensions. | | functions | [`map[string, task]`](#task) | `no` | A name/value mapping of the workflow's reusable tasks. | | retries | [`map[string, retryPolicy]`](#retry) | `no` | A name/value mapping of the workflow's reusable retry policies. | | secrets | `string[]` | `no` | A list containing the workflow's secrets. | @@ -143,22 +143,22 @@ use: petStoreOAuth2: oauth2: my-oauth2-secret extensions: - externalLogging: - extend: all - before: - call: http - with: - method: post - endpoint: https://fake.log.collector.com - body: - message: "${ \"Executing task '\($task.reference)'...\" }" - after: - call: http - with: - method: post - endpoint: https://fake.log.collector.com - body: - message: "${ \"Executed task '\($task.reference)'...\" }" + - externalLogging: + extend: all + before: + call: http + with: + method: post + endpoint: https://fake.log.collector.com + body: + message: "${ \"Executing task '\($task.reference)'...\" }" + after: + call: http + with: + method: post + endpoint: https://fake.log.collector.com + body: + message: "${ \"Executed task '\($task.reference)'...\" }" functions: getAvailablePets: call: openapi @@ -169,37 +169,39 @@ use: parameters: status: available secrets: - - my-oauth2-secret + - my-oauth2-secret do: - getAvailablePets: - call: getAvailablePets - output: - from: "$input + { availablePets: [.[] | select(.category.name == "dog" and (.tags[] | .breed == $input.order.breed))] }" - submitMatchesByMail: - call: http - with: - method: post - endpoint: - uri: https://fake.smtp.service.com/email/send - authentication: petStoreOAuth2 - body: - from: noreply@fake.petstore.com - to: ${ .order.client.email } - subject: Candidates for Adoption - body: > - Hello ${ .order.client.preferredDisplayName }! + execute: + sequentially: + - getAvailablePets: + call: getAvailablePets + output: + from: "$input + { availablePets: [.[] | select(.category.name == "dog" and (.tags[] | .breed == $input.order.breed))] }" + - submitMatchesByMail: + call: http + with: + method: post + endpoint: + uri: https://fake.smtp.service.com/email/send + authentication: petStoreOAuth2 + body: + from: noreply@fake.petstore.com + to: ${ .order.client.email } + subject: Candidates for Adoption + body: > + Hello ${ .order.client.preferredDisplayName }! - Following your interest to adopt a dog, here is a list of candidates that you might be interested in: + Following your interest to adopt a dog, here is a list of candidates that you might be interested in: - ${ .pets | map("-\(.name)") | join("\n") } + ${ .pets | map("-\(.name)") | join("\n") } - Please do not hesistate to contact us at info@fake.petstore.com if your have questions. + Please do not hesistate to contact us at info@fake.petstore.com if your have questions. - Hope to hear from you soon! + Hope to hear from you soon! - ---------------------------------------------------------------------------------------------- - DO NOT REPLY - ---------------------------------------------------------------------------------------------- + ---------------------------------------------------------------------------------------------- + DO NOT REPLY + ---------------------------------------------------------------------------------------------- ``` ### Task @@ -255,11 +257,10 @@ document: name: sample-workflow version: '0.1.0' do: - getPetById: - call: http - with: - method: get - endpoint: https://petstore.swagger.io/v2/pet/{petId} + call: http + with: + method: get + endpoint: https://petstore.swagger.io/v2/pet/{petId} ``` Serverless Workflow defines several default functions that **MUST** be supported by all implementations and runtimes: @@ -294,16 +295,15 @@ document: name: sample-workflow version: '0.1.0' do: - greetUser: - call: asyncapi - with: - document: https://fake.com/docs/asyncapi.json - operation: findPetsByStatus - server: staging - message: getPetByStatusQuery - binding: http - payload: - petId: ${ .pet.id } + call: asyncapi + with: + document: https://fake.com/docs/asyncapi.json + operation: findPetsByStatus + server: staging + message: getPetByStatusQuery + binding: http + payload: + petId: ${ .pet.id } ``` ##### gRPC Call @@ -331,17 +331,16 @@ document: name: sample-workflow version: '0.1.0' do: - greetUser: - call: grpc - with: - proto: file://app/greet.proto - service: - name: GreeterApi.Greeter - host: localhost - port: 5011 - method: SayHello - arguments: - name: ${ .user.preferredDisplayName } + call: grpc + with: + proto: file://app/greet.proto + service: + name: GreeterApi.Greeter + host: localhost + port: 5011 + method: SayHello + arguments: + name: ${ .user.preferredDisplayName } ``` ##### HTTP Call @@ -367,11 +366,10 @@ document: name: sample-workflow version: '0.1.0' do: - getPetById: - call: http - with: - method: get - endpoint: https://petstore.swagger.io/v2/pet/{petId} + call: http + with: + method: get + endpoint: https://petstore.swagger.io/v2/pet/{petId} ``` ##### OpenAPI Call @@ -397,13 +395,12 @@ document: name: sample-workflow version: '0.1.0' do: - getPets: - call: openapi - with: - document: https://petstore.swagger.io/v2/swagger.json - operation: findPetsByStatus - parameters: - status: available + call: openapi + with: + document: https://petstore.swagger.io/v2/swagger.json + operation: findPetsByStatus + parameters: + status: available ``` #### Composite @@ -414,8 +411,8 @@ do: | Name | Type | Required | Description| |:--|:---:|:---:|:---| -| execute.sequentially | [`map(string, task)`](#task) | `no` | The tasks to perform sequentially.
*Required if `execute.concurrently` has not been set, otherwise ignored.*
*If set, must contains **at least** two [`tasks`](#task).* | -| execute.concurrently | [`map(string, task)`](#task) | `no` | The tasks to perform concurrently.
*Required if `execute.sequentially` has not been set, otherwise ignored.*
*If set, must contains **at least** two [`tasks`](#task).* | +| execute.sequentially | [`map[string, task][]`](#task) | `no` | The tasks to perform sequentially.
*Required if `execute.concurrently` has not been set, otherwise ignored.*
*If set, must contains **at least** two [`tasks`](#task).* | +| execute.concurrently | [`map[string, task][]`](#task) | `no` | The tasks to perform concurrently.
*Required if `execute.sequentially` has not been set, otherwise ignored.*
*If set, must contains **at least** two [`tasks`](#task).* | | execute.compete | `boolean` | `no` | Indicates whether or not the concurrent [`tasks`](#task) are racing against each other, with a single possible winner, which sets the composite task's output.
*Ignored if `execute.sequentially` has been set. Defaults to `false`.*
*Must **not** be set if the [`tasks`](#task) are executed sequentially.* | ##### Examples @@ -428,10 +425,9 @@ document: name: sample-workflow version: '0.1.0' do: - bookTrip: - execute: - sequentially: - bookHotel: + execute: + sequentially: + - bookHotel: call: http with: method: post @@ -442,7 +438,7 @@ do: name: Four Seasons city: Antwerp country: Belgium - bookFlight: + - bookFlight: call: http with: method: post @@ -474,10 +470,9 @@ document: name: sample-workflow version: '0.1.0' do: - raiseAlarm: - execute: - concurrently: - callNurse: + execute: + concurrently: + - callNurse: call: http with: method: put @@ -485,7 +480,7 @@ do: body: patientId: ${ .patient.fullName } room: ${ .room.number } - callDoctor: + - callDoctor: call: http with: method: put @@ -514,19 +509,18 @@ document: name: sample-workflow version: '0.1.0' do: - placeOrder: - emit: - event: - with: - source: https://petstore.com - type: com.petstore.order.placed.v1 - data: - client: - firstName: Cruella - lastName: de Vil - items: - - breed: dalmatian - quantity: 101 + emit: + event: + with: + source: https://petstore.com + type: com.petstore.order.placed.v1 + data: + client: + firstName: Cruella + lastName: de Vil + items: + - breed: dalmatian + quantity: 101 ``` #### For @@ -552,22 +546,19 @@ document: name: sample-workflow version: '0.1.0' do: - checkup: - for: - each: pet - in: .pets - at: index - while: .vet != null - do: - checkForFleas: - if: $pet.lastCheckup == null - listen: - to: - one: - with: - type: com.fake.petclinic.pets.checkup.completed.v2 - output: - to: '.pets + [{ "id": $pet.id }]' + for: + each: pet + in: .pets + at: index + while: .vet != null + do: + listen: + to: + one: + with: + type: com.fake.petclinic.pets.checkup.completed.v2 + output: + to: '.pets + [{ "id": $pet.id }]' ``` #### Listen @@ -589,18 +580,17 @@ document: name: sample-workflow version: '0.1.0' do: - callDoctor: - listen: - to: - any: - - with: - type: com.fake-hospital.vitals.measurements.temperature - data: - temperature: ${ .temperature > 38 } - - with: - type: com.fake-hospital.vitals.measurements.bpm - data: - temperature: ${ .bpm < 60 or .bpm > 100 } + listen: + to: + any: + - with: + type: com.fake-hospital.vitals.measurements.temperature + data: + temperature: ${ .temperature > 38 } + - with: + type: com.fake-hospital.vitals.measurements.bpm + data: + temperature: ${ .bpm < 60 or .bpm > 100 } ``` #### Raise @@ -622,28 +612,30 @@ document: name: sample-workflow version: '0.1.0' do: - processTicket: - switch: - highPriority: - when: .ticket.priority == "high" - then: escalateToManager - mediumPriority: - when: .ticket.priority == "medium" - then: assignToSpecialist - lowPriority: - when: .ticket.priority == "low" - then: resolveTicket - default: - then: raiseUndefinedPriorityError - raiseUndefinedPriorityError: - raise: - error: - type: https://fake.com/errors/tickets/undefined-priority - status: 400 - title: Undefined Priority - escalateToManager: {...} - assignToSpecialist: {...} - resolveTicket: {...} + execute: + sequentially: + - processTicket: + switch: + - highPriority: + when: .ticket.priority == "high" + then: escalateToManager + - mediumPriority: + when: .ticket.priority == "medium" + then: assignToSpecialist + - lowPriority: + when: .ticket.priority == "low" + then: resolveTicket + - default: + then: raiseUndefinedPriorityError + - raiseUndefinedPriorityError: + raise: + error: + type: https://fake.com/errors/tickets/undefined-priority + status: 400 + title: Undefined Priority + - escalateToManager: {} + - assignToSpecialist: {} + - resolveTicket: {} ``` #### Run @@ -668,29 +660,30 @@ document: name: sample-workflow version: '0.1.0' do: - - runContainer: - run: - container: - image: fake-image - - runScript: - run: - script: - language: js - code: > - Some cool multiline script - - runShell: - run: - shell: - command: 'echo "Hello, ${ .user.name }"' - - runWorkflow: - run: - workflow: - reference: another-one:0.1.0 - input: {} + execute: + sequentially: + - runContainer: + run: + container: + image: fake-image + + - runScript: + run: + script: + language: js + code: > + Some cool multiline script + + - runShell: + run: + shell: + command: 'echo "Hello, ${ .user.name }"' + + - runWorkflow: + run: + workflow: + reference: another-one:0.1.0 + input: {} ``` ##### Container Process @@ -716,10 +709,9 @@ document: name: sample-workflow version: '0.1.0' do: - runContainer: - run: - container: - image: fake-image + run: + container: + image: fake-image ``` ##### Script Process @@ -744,12 +736,11 @@ document: name: sample-workflow version: '0.1.0' do: - runScript: - run: - script: - language: js - code: > - Some cool multiline script + run: + script: + language: js + code: > + Some cool multiline script ``` ##### Shell Process @@ -773,10 +764,9 @@ document: name: sample-workflow version: '0.1.0' do: - runShell: - run: - shell: - command: 'echo "Hello, ${ .user.name }"' + run: + shell: + command: 'echo "Hello, ${ .user.name }"' ``` ##### Workflow Process @@ -800,12 +790,11 @@ document: name: sample-workflow version: '0.1.0' do: - runShell: - run: - workflow: - reference: another-one:0.1.0 - input: - foo: bar + run: + workflow: + reference: another-one:0.1.0 + input: + foo: bar ``` #### Set @@ -827,11 +816,10 @@ document: name: set version: '0.1.0' do: - initialize: - set: - shape: circle - size: ${ .configuration.size } - fill: ${ .configuration.fill } + set: + shape: circle + size: ${ .configuration.size } + fill: ${ .configuration.fill } ``` #### Switch @@ -842,7 +830,7 @@ Enables conditional branching within workflows, allowing them to dynamically sel | Name | Type | Required | Description | |:--|:---:|:---:|:---| -| switch | [`map[string, case]`](#switch-case) | `yes` | A name/value map of the cases to switch on | +| switch | [`case[]`](#switch-case) | `yes` | A name/value map of the cases to switch on | ##### Examples @@ -853,34 +841,36 @@ document: name: sample-workflow version: '0.1.0' do: - processOrder: - switch: - case1: - when: .orderType == "electronic" - then: processElectronicOrder - case2: - when: .orderType == "physical" - then: processPhysicalOrder - default: - then: handleUnknownOrderType - processElectronicOrder: - execute: - sequentially: - validatePayment: {...} - fulfillOrder: {...} - then: exit - processPhysicalOrder: - execute: - sequentially: - checkInventory: {...} - packItems: {...} - scheduleShipping: {...} - then: exit - handleUnknownOrderType: - execute: - sequentially: - logWarning: {...} - notifyAdmin: {...} + execute: + sequentially: + - processOrder: + switch: + - case1: + when: .orderType == "electronic" + then: processElectronicOrder + - case2: + when: .orderType == "physical" + then: processPhysicalOrder + - default: + then: handleUnknownOrderType + - processElectronicOrder: + execute: + sequentially: + - validatePayment: {} + - fulfillOrder: {} + then: exit + - processPhysicalOrder: + execute: + sequentially: + - checkInventory: {} + - packItems: {} + - scheduleShipping: {} + then: exit + - handleUnknownOrderType: + execute: + sequentially: + - logWarning: {} + - notifyAdmin: {} ``` ##### Switch Case @@ -900,7 +890,7 @@ Serves as a mechanism within workflows to handle errors gracefully, potentially | Name | Type | Required | Description| |:--|:---:|:---:|:---| -| try | [`task`](#task) | `yes` | The task to perform. | +| try | [`task`](#task) | `yes` | The task(s) to perform. | | catch | [`catch`](#catch) | `yes` | Configures the errors to catch and how to handle them. | ##### Examples @@ -912,26 +902,25 @@ document: name: sample-workflow version: '0.1.0' do: - processOrder: - try: - call: http + try: + call: http + with: + method: get + endpoint: https:// + catch: + errors: with: - method: get - endpoint: https:// - catch: - errors: - with: - type: https://serverlessworkflow.io.io/dsl/errors/types/communication - status: 503 - as: error - retry: - delay: - seconds: 3 - backoff: - exponential: {} - limit: - attempt: - count: 5 + type: https://serverlessworkflow.io.io/dsl/errors/types/communication + status: 503 + as: error + retry: + delay: + seconds: 3 + backoff: + exponential: {} + limit: + attempt: + count: 5 ``` ##### Catch @@ -968,9 +957,8 @@ document: name: sample-workflow version: '0.1.0' do: - delay10Seconds: - wait: - seconds: 10 + wait: + seconds: 10 ``` ### Flow Directive @@ -979,9 +967,10 @@ Flow Directives are commands within a workflow that dictate its progression. | Directive | Description | | --------- | ----------- | -| `continue` | Instructs the workflow to proceed with the next task in line. This action may conclude the execution of a particular workflow or branch if there are not task defined after the continue one. | -| `exit` | Halts the current branch's execution, potentially terminating the entire workflow if the current task resides within the main branch. | -| `end` | Provides a graceful conclusion to the workflow execution, signaling its completion explicitly. | +| `"continue"` | Instructs the workflow to proceed with the next task in line. This action may conclude the execution of a particular workflow or branch if there are not task defined after the continue one. | +| `"exit"` | Halts the current branch's execution, potentially terminating the entire workflow if the current task resides within the main branch. | +| `"end"` | Provides a graceful conclusion to the workflow execution, signaling its completion explicitly. | +| `string` | Continues the workflow at the task with the specified name | ### External Resource @@ -1035,13 +1024,12 @@ use: sampleBasicFromSecret: basic: usernamePasswordSecret do: - getMessages: - call: http - with: - method: get - endpoint: - uri: https://secured.fake.com/sample - authentication: sampleBasicFromSecret + call: http + with: + method: get + endpoint: + uri: https://secured.fake.com/sample + authentication: sampleBasicFromSecret ``` #### Basic Authentication @@ -1070,13 +1058,12 @@ use: username: admin password: 123 do: - getMessages: - call: http - with: - method: get - endpoint: - uri: https://secured.fake.com/sample - authentication: sampleBasic + call: http + with: + method: get + endpoint: + uri: https://secured.fake.com/sample + authentication: sampleBasic ``` #### Bearer Authentication @@ -1098,15 +1085,14 @@ document: name: sample-workflow version: '0.1.0' do: - getMessages: - call: http - with: - method: get - endpoint: - uri: https://secured.fake.com/sample - authentication: - bearer: - token: ${ .user.token } + call: http + with: + method: get + endpoint: + uri: https://secured.fake.com/sample + authentication: + bearer: + token: ${ .user.token } ``` #### Certificate Authentication @@ -1143,21 +1129,20 @@ document: name: sample-workflow version: '0.1.0' do: - getMessages: - call: http - with: - method: get - endpoint: - uri: https://secured.fake.com/sample - authentication: - oauth2: - authority: http://keycloak/realms/fake-authority/.well-known/openid-configuration - grant: client-credentials - client: - id: workflow-runtime - secret: ********** - scopes: [ api ] - audiences: [ runtime ] + call: http + with: + method: get + endpoint: + uri: https://secured.fake.com/sample + authentication: + oauth2: + authority: http://keycloak/realms/fake-authority/.well-known/openid-configuration + grant: client-credentials + client: + id: workflow-runtime + secret: ********** + scopes: [ api ] + audiences: [ runtime ] ``` ##### OAUTH2 Token @@ -1197,28 +1182,27 @@ document: version: '0.1.0' use: extensions: - logging: - extend: all - before: - call: http - with: - method: post - endpoint: https://fake.log.collector.com - body: - message: "${ \"Executing task '\($task.reference)'...\" }" - after: - call: http - with: - method: post - endpoint: https://fake.log.collector.com - body: - message: "${ \"Executed task '\($task.reference)'...\" }" + - logging: + extend: all + before: + call: http + with: + method: post + endpoint: https://fake.log.collector.com + body: + message: "${ \"Executing task '\($task.reference)'...\" }" + after: + call: http + with: + method: post + endpoint: https://fake.log.collector.com + body: + message: "${ \"Executed task '\($task.reference)'...\" }" do: - get: - call: http - with: - method: get - endpoint: https://fake.com/sample + call: http + with: + method: get + endpoint: https://fake.com/sample ``` *Intercept HTTP calls to 'https://mocked.service.com' and mock its response:* @@ -1230,24 +1214,23 @@ document: version: '0.1.0' use: extensions: - mockService: - extend: http - when: ($task.with.uri != null and ($task.with.uri | startswith("https://mocked.service.com"))) or ($task.with.endpoint.uri != null and ($task.with.endpoint.uri | startswith("https://mocked.service.com"))) - before: - set: - statusCode: 200 - headers: - Content-Type: application/json - content: - foo: - bar: baz - then: exit #using this, we indicate to the workflow we want to exit the extended task, thus just returning what we injected + - mockService: + extend: http + when: ($task.with.uri != null and ($task.with.uri | startswith("https://mocked.service.com"))) or ($task.with.endpoint.uri != null and ($task.with.endpoint.uri | startswith("https://mocked.service.com"))) + before: + set: + statusCode: 200 + headers: + Content-Type: application/json + content: + foo: + bar: baz + then: exit #using this, we indicate to the workflow we want to exit the extended task, thus just returning what we injected do: - get: - call: http - with: - method: get - endpoint: https://fake.com/sample + call: http + with: + method: get + endpoint: https://fake.com/sample ``` ### Error @@ -1488,9 +1471,8 @@ document: name: sample version: '0.1.0' do: - waitFor60Seconds: - wait: - seconds: 60 + wait: + seconds: 60 timeout: after: seconds: 30 diff --git a/dsl.md b/dsl.md index 900c5fb0..b481d9c5 100644 --- a/dsl.md +++ b/dsl.md @@ -342,10 +342,9 @@ document: version: '0.1.0' do: - validateEmail: - call: https://github.com/myorg/functions/validateEmailAddress@v1 - with: - emailAddress: ${ .userEmail } + call: https://github.com/myorg/functions/validateEmailAddress@v1 + with: + emailAddress: ${ .userEmail } ``` ##### Publishing a Custom Function @@ -426,11 +425,10 @@ use: body: message: "${ \"Executed task '\($task.reference)'...\" }" do: - get: - call: http - with: - method: get - uri: https://fake.com/sample + call: http + with: + method: get + uri: https://fake.com/sample ``` ### External Resources diff --git a/examples/accumulate-room-readings.yaml b/examples/accumulate-room-readings.yaml index d7d91eec..5b322b07 100644 --- a/examples/accumulate-room-readings.yaml +++ b/examples/accumulate-room-readings.yaml @@ -4,43 +4,45 @@ document: name: accumulate-room-readings version: 1.0.0-alpha1 do: - consumeReading: - listen: - to: - all: - - with: - source: https://my.home.com/sensor - type: my.home.sensors.temperature - correlate: - roomId: - from: .roomid - output: - from: .data.reading - - with: - source: https://my.home.com/sensor - type: my.home.sensors.humidity - correlate: - roomId: - from: .roomid - output: - from: .data.reading - as: readings - logReading: - for: - each: reading - in: .readings - do: - call: openapi - with: - document: - uri: http://myorg.io/ordersservices.json - operationId: logreading - generateReport: - call: openapi - with: - document: - uri: http://myorg.io/ordersservices.json - operationId: produceReport + execute: + sequentially: + - consumeReading: + listen: + to: + all: + - with: + source: https://my.home.com/sensor + type: my.home.sensors.temperature + correlate: + roomId: + from: .roomid + output: + from: .data.reading + - with: + source: https://my.home.com/sensor + type: my.home.sensors.humidity + correlate: + roomId: + from: .roomid + output: + from: .data.reading + as: readings + - logReading: + for: + each: reading + in: .readings + do: + call: openapi + with: + document: + uri: http://myorg.io/ordersservices.json + operationId: logreading + - generateReport: + call: openapi + with: + document: + uri: http://myorg.io/ordersservices.json + operationId: produceReport timeout: after: hours: 1 \ No newline at end of file diff --git a/examples/asyncapi.yaml b/examples/asyncapi.yaml index 9d442d08..ca6ff26d 100644 --- a/examples/asyncapi.yaml +++ b/examples/asyncapi.yaml @@ -4,17 +4,16 @@ document: name: bearer-auth version: 1.0.0-alpha1 do: - greetUser: - call: asyncapi - with: - document: - uri: https://fake.com/docs/asyncapi.json - operationRef: findPetsByStatus - server: staging - message: getPetByStatusQuery - binding: http - payload: - petId: ${ .pet.id } - authentication: - bearer: - token: ${ .token } + call: asyncapi + with: + document: + uri: https://fake.com/docs/asyncapi.json + operationRef: findPetsByStatus + server: staging + message: getPetByStatusQuery + binding: http + payload: + petId: ${ .pet.id } + authentication: + bearer: + token: ${ .token } diff --git a/examples/bearer-auth-uri-format.yaml b/examples/bearer-auth-uri-format.yaml index 622aae2d..2a0700ec 100644 --- a/examples/bearer-auth-uri-format.yaml +++ b/examples/bearer-auth-uri-format.yaml @@ -4,12 +4,11 @@ document: name: bearer-auth-uri-format version: 1.0.0-alpha1 do: - getPetById: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/1 - authentication: - bearer: - token: ${ .token } + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/1 + authentication: + bearer: + token: ${ .token } diff --git a/examples/bearer-auth.yaml b/examples/bearer-auth.yaml index f4b2227c..46e72bf1 100644 --- a/examples/bearer-auth.yaml +++ b/examples/bearer-auth.yaml @@ -4,12 +4,11 @@ document: name: bearer-auth version: 1.0.0-alpha1 do: - getPetById: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/{petId} - authentication: - bearer: - token: ${ .token } + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/{petId} + authentication: + bearer: + token: ${ .token } diff --git a/examples/call-http-shorthand-endpoint.yaml b/examples/call-http-shorthand-endpoint.yaml index f682ed30..52b606b2 100644 --- a/examples/call-http-shorthand-endpoint.yaml +++ b/examples/call-http-shorthand-endpoint.yaml @@ -4,8 +4,7 @@ document: name: call-http-shorthand-endpoint version: 1.0.0-alpha1 do: - getPetById: - call: http - with: - method: get - endpoint: https://petstore.swagger.io/v2/pet/{petId} + call: http + with: + method: get + endpoint: https://petstore.swagger.io/v2/pet/{petId} diff --git a/examples/do-single.yaml b/examples/do-single.yaml new file mode 100644 index 00000000..52b606b2 --- /dev/null +++ b/examples/do-single.yaml @@ -0,0 +1,10 @@ +document: + dsl: 1.0.0-alpha1 + namespace: examples + name: call-http-shorthand-endpoint + version: 1.0.0-alpha1 +do: + call: http + with: + method: get + endpoint: https://petstore.swagger.io/v2/pet/{petId} diff --git a/examples/mock-service-extension.yaml b/examples/mock-service-extension.yaml new file mode 100644 index 00000000..4a6c7724 --- /dev/null +++ b/examples/mock-service-extension.yaml @@ -0,0 +1,25 @@ +document: + dsl: '1.0.0-alpha1' + namespace: test + name: sample-workflow + version: '0.1.0' +use: + extensions: + - mockService: + extend: call + when: ($task.with.endpoint != null and ($task.with.endpoint | startswith("https://mocked.service.com"))) or ($task.with.endpoint.uri != null and ($task.with.endpoint.uri | startswith("https://mocked.service.com"))) + before: + set: + statusCode: 200 + headers: + Content-Type: application/json + content: + foo: + bar: baz + then: exit #using this, we indicate to the workflow we want to exit the extended task, thus just returning what we injected +do: + call: http + with: + method: get + endpoint: + uri: https://fake.com/sample \ No newline at end of file diff --git a/examples/switch-then-string.yaml b/examples/switch-then-string.yaml index 7db53745..99c69ea1 100644 --- a/examples/switch-then-string.yaml +++ b/examples/switch-then-string.yaml @@ -4,45 +4,47 @@ document: name: sample-workflow version: '0.1.0' do: - processOrder: - switch: - case1: - when: .orderType == "electronic" - then: processElectronicOrder - case2: - when: .orderType == "physical" - then: processPhysicalOrder - default: - then: handleUnknownOrderType - processElectronicOrder: - execute: - sequentially: - validatePayment: - set: - validate: true - fulfillOrder: - set: - status: fulfilled - then: exit - processPhysicalOrder: - execute: - sequentially: - checkInventory: - set: - inventory: clear - packItems: - set: - items: 1 - scheduleShipping: - set: - address: Elmer St - then: exit - handleUnknownOrderType: - execute: - sequentially: - logWarning: - set: - log: warn - notifyAdmin: - set: - message: something's wrong \ No newline at end of file + execute: + sequentially: + - processOrder: + switch: + - case1: + when: .orderType == "electronic" + then: processElectronicOrder + - case2: + when: .orderType == "physical" + then: processPhysicalOrder + - default: + then: handleUnknownOrderType + - processElectronicOrder: + execute: + sequentially: + - validatePayment: + set: + validate: true + - fulfillOrder: + set: + status: fulfilled + then: exit + - processPhysicalOrder: + execute: + sequentially: + - checkInventory: + set: + inventory: clear + - packItems: + set: + items: 1 + - scheduleShipping: + set: + address: Elmer St + then: exit + - handleUnknownOrderType: + execute: + sequentially: + - logWarning: + set: + log: warn + - notifyAdmin: + set: + message: something's wrong \ No newline at end of file diff --git a/examples/use-authentication.yaml b/examples/use-authentication.yaml index c8cd34fe..613764df 100644 --- a/examples/use-authentication.yaml +++ b/examples/use-authentication.yaml @@ -9,10 +9,9 @@ use: bearer: token: ${ .token } do: - getPetById: - call: http - with: - method: get - endpoint: - uri: https://petstore.swagger.io/v2/pet/{petId} - authentication: petStoreAuth + call: http + with: + method: get + endpoint: + uri: https://petstore.swagger.io/v2/pet/{petId} + authentication: petStoreAuth diff --git a/schema/workflow.yaml b/schema/workflow.yaml index 75c2093e..d83a7b45 100644 --- a/schema/workflow.yaml +++ b/schema/workflow.yaml @@ -51,9 +51,13 @@ properties: $ref: '#/$defs/error' description: The workflow's reusable errors. extensions: - type: object - additionalProperties: - $ref: '#/$defs/extension' + type: array + items: + type: object + minProperties: 1 + maxProperties: 1 + additionalProperties: + $ref: '#/$defs/extension' description: The workflow's extensions. functions: type: object @@ -72,11 +76,8 @@ properties: description: The workflow's secrets. description: Defines the workflow's reusable components. do: - type: object - minProperties: 1 - additionalProperties: - $ref: '#/$defs/task' - description: Defines the tasks the workflow must perform + description: Defines the task the workflow must perform + $ref: '#/$defs/task' timeout: $ref: '#/$defs/timeout' description: The workflow's timeout configuration, if any. @@ -273,33 +274,42 @@ $defs: description: A name/value mapping of the parameters, if any, to call the function with. required: [ call ] compositeTask: - type: object + type: object + required: [ execute ] + description: Serves as a pivotal orchestrator within workflow systems, enabling the seamless integration and execution of multiple subtasks to accomplish complex operations properties: execute: type: object - oneOf: - - properties: - concurrently: - type: object - minProperties: 2 - additionalProperties: - $ref: '#/$defs/task' - description: A name/definition mapping of the tasks to perform concurrently. - compete: - type: boolean - description: Indicates whether or not the concurrent tasks are racing against each other, with a single possible winner, which sets the composite task's output. - required: [ concurrently ] - - properties: - sequentially: - type: object - minProperties: 2 - additionalProperties: - $ref: '#/$defs/task' - description: A name/definition mapping of the tasks to perform sequentially. - required: [ sequentially ] description: Configures the task execution strategy to use - required: [ execute ] - description: Serves as a pivotal orchestrator within workflow systems, enabling the seamless integration and execution of multiple subtasks to accomplish complex operations + oneOf: + - required: [ concurrently ] + properties: + concurrently: + description: A list of the tasks to perform concurrently. + type: array + minItems: 2 + items: + type: object + minProperties: 1 + maxProperties: 1 + additionalProperties: + $ref: '#/$defs/task' + compete: + description: Indicates whether or not the concurrent tasks are racing against each other, with a single possible winner, which sets the composite task's output. + type: boolean + default: false + - required: [ sequentially ] + properties: + sequentially: + description: A list of the tasks to perform sequentially. + type: array + minItems: 2 + items: + type: object + minProperties: 1 + maxProperties: 1 + additionalProperties: + $ref: '#/$defs/task' emitTask: type: object properties: @@ -499,25 +509,32 @@ $defs: type: object properties: switch: - type: object - minProperties: 1 - additionalProperties: + type: array + minItems: 1 + items: type: object - properties: - when: - type: string - description: A runtime expression used to determine whether or not the case matches. - then: - $ref: '#/$defs/flowDirective' - description: The flow directive to execute when the case matches. + minProperties: 1 + maxProperties: 1 + additionalProperties: + type: object + properties: + name: + type: string + description: The case's name. + when: + type: string + description: A runtime expression used to determine whether or not the case matches. + then: + $ref: '#/$defs/flowDirective' + description: The flow directive to execute when the case matches. required: [ switch ] description: Enables conditional branching within workflows, allowing them to dynamically select different paths based on specified conditions or criteria tryTask: type: object properties: try: - $ref: '#/$defs/task' description: The task to perform. + $ref: '#/$defs/task' catch: type: object properties: @@ -536,8 +553,8 @@ $defs: $ref: '#/$defs/retryPolicy' description: The retry policy to use, if any, when catching errors. do: - $ref: '#/$defs/task' description: The definition of the task to run when catching an error. + $ref: '#/$defs/task' required: [ try, catch ] description: Serves as a mechanism within workflows to handle errors gracefully, potentially retrying failed tasks before proceeding with alternate ones. waitTask: @@ -760,11 +777,11 @@ $defs: type: string description: A runtime expression, if any, used to determine whether or not the extension should apply in the specified context. before: - $ref: '#/$defs/task' description: The task to execute before the extended task, if any. - after: $ref: '#/$defs/task' + after: description: The task to execute after the extended task, if any. + $ref: '#/$defs/task' required: [ extend ] description: The definition of a an extension. externalResource: