diff --git a/examples/README.md b/examples/README.md index 1d815d42..f25e1643 100644 --- a/examples/README.md +++ b/examples/README.md @@ -36,6 +36,7 @@ Provides Serverless Workflow language examples - [Online Food Ordering](#Online-Food-Ordering) - [Continuing as a new Execution](#Continuing-as-a-new-Execution) - [Process Transactions (Foreach State with conditions)](#Process-Transactions) +- [Import external resources](#import-external-resources) ### Hello World Example @@ -4944,3 +4945,141 @@ functions: + +### Import External Resources + +#### Description + +For the example, let's assume we have developed a number of workflows to orchestrate an hypothetical, event-driven management solution around the Swagger Pet Store API. + +At start, we were copy/pasting the many Pet Store function definitions from workflow to workflow as we needed them. Even though that was very tedious, we just coped with it, until that day where the names of a couple of functions changed in the `swagger.json`, breaking most of our workflows. + +We then sat down, and agreed for our sanity to respect the DRY principle, and only declare those functions once, in an hypothetical `https://fake.examples/sw/petstore.yaml` file, that could look like the following: + +```yaml +functions: + - name: add-pet + type: openapi + operation: https://petstore.swagger.io/v2/swagger.json#addPet + - name: get-pet-by-id + type: openapi + operation: https://petstore.swagger.io/v2/swagger.json#getPetById + - name: update-pet + type: openapi + operation: https://petstore.swagger.io/v2/swagger.json#updatePet + - name: delete-pet + type: openapi + operation: https://petstore.swagger.io/v2/swagger.json#deletePet +``` + +Starting from there, we could now reuse those definitions in any workflows by just importing them: + +#### Workflow Definition + + + + + + + + + + +
JSONYAML
+ +```json +{ + "id": "get-pet-availability", + "name": "Get Pet Availability", + "version": "1.0.0", + "specVersion": 0.8, + "resources": [ + { + "name": "petstore", + "uri": "https://test.com/myresource.json" + } + ], + "functions": [ + { + "name": "email-send", + "type": "openapi", + "operation": "smtp.json#send" + } + ], + "states": [ + { + "name": "get-pet", + "type": "operation", + "actions": [ + { + "name": "get-pet-by-id", + "functionRef": { + "refName": "petstore.get-pet-by-id", + "arguments": { + "petId": "${ .input.petId }" + } + }, + "actionDataFilter": { + "toStateData": "${ .pet }" + } + }, + { + "name": "send-pet-status-by-email", + "functionRef": { + "refName": "email-send", + "arguments": { + "from": "info@petstore.io", + "to": "${ .input.client.email }", + "subject": "Pet Inquiry Result", + "isBodyHtml": true, + "body": "The status of the pet you enquired (${ .input.petId }) about is '${ .pet.status }'" + } + } + } + ], + "end": true + } + ] +} +``` + + + +```yaml +id: get-pet-availability +name: Get Pet Availability +version: 1.0.0 +specVersion: 0.8 +resources: + - name: petstore + uri: 'https://test.com/myresource.json' +functions: + - name: email-send + type: openapi + operation: 'smtp.json#send' +states: + - name: get-pet + type: operation + actions: + - name: get-pet-by-id + functionRef: + refName: petstore.get-pet-by-id + arguments: + petId: '${ .input.petId }' + actionDataFilter: + toStateData: '${ .pet }' + - name: send-pet-status-by-email + functionRef: + refName: email-send + arguments: + from: info@petstore.io + to: '${ .input.client.email }' + subject: Pet Inquiry Result + isBodyHtml: true + body: >- + The status of the pet you enquired (${ .input.petId }) about is + '${ .pet.status }' + end: true +``` + +
diff --git a/roadmap/README.md b/roadmap/README.md index f092313d..da26761c 100644 --- a/roadmap/README.md +++ b/roadmap/README.md @@ -37,6 +37,7 @@ _Status description:_ | ✔️| Add the new `WORKFLOW` reserved keyword to workflow expressions | | ✔️| Update `ForEach` state iteration parameter example. This parameter is an expression variable, not a JSON property | | ✔️| Add the new `rest` function type [spec doc](https://github.com/serverlessworkflow/specification/tree/main/specification.md#using-functions-for-restful-service-invocations) | +| ✔️| Add support for importing and referencing externally defined workflow `resources` | | ✏️️| Add inline state defs in branches | | | ✏️️| Add "completedBy" functionality | | | ✏️️| Define workflow context | | diff --git a/schema/auth.json b/schema/auth.json index 5a651988..c2d1876a 100644 --- a/schema/auth.json +++ b/schema/auth.json @@ -4,23 +4,14 @@ "description": "Serverless Workflow specification - auth schema", "type": "object", "auth": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "description": "URI to a resource containing auth definitions (json or yaml)" - }, - { - "type": "array", - "description": "Workflow auth definitions", - "items": { - "type": "object", - "$ref": "#/definitions/authdef" - }, - "additionalItems": false, - "minItems": 1 - } - ] + "type": "array", + "description": "Workflow auth definitions", + "items": { + "type": "object", + "$ref": "#/definitions/authdef" + }, + "additionalItems": false, + "minItems": 1 }, "required": [ "auth" diff --git a/schema/errors.json b/schema/errors.json index 7e01a8ea..88fcbd99 100644 --- a/schema/errors.json +++ b/schema/errors.json @@ -4,23 +4,14 @@ "description": "Serverless Workflow specification - errors schema", "type": "object", "errors": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "description": "URI to a resource containing error definitions (json or yaml)" - }, - { - "type": "array", - "description": "Workflow Error definitions. Defines checked errors that can be explicitly handled during workflow execution", - "items": { - "type": "object", - "$ref": "#/definitions/errordef" - }, - "additionalItems": false, - "minItems": 1 - } - ] + "type": "array", + "description": "Workflow Error definitions. Defines checked errors that can be explicitly handled during workflow execution", + "items": { + "type": "object", + "$ref": "#/definitions/errordef" + }, + "additionalItems": false, + "minItems": 1 }, "required": [ "errors" diff --git a/schema/events.json b/schema/events.json index 9c20f8de..f57a5d5b 100644 --- a/schema/events.json +++ b/schema/events.json @@ -4,23 +4,14 @@ "description": "Serverless Workflow specification - events schema", "type": "object", "events": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "description": "URI to a resource containing event definitions (json or yaml)" - }, - { - "type": "array", - "description": "Workflow CloudEvent definitions. Defines CloudEvents that can be consumed or produced", - "items": { - "type": "object", - "$ref": "#/definitions/eventdef" - }, - "additionalItems": false, - "minItems": 1 - } - ] + "type": "array", + "description": "Workflow CloudEvent definitions. Defines CloudEvents that can be consumed or produced", + "items": { + "type": "object", + "$ref": "#/definitions/eventdef" + }, + "additionalItems": false, + "minItems": 1 }, "required": [ "events" diff --git a/schema/functions.json b/schema/functions.json index 0b3448d9..7aaf60f1 100644 --- a/schema/functions.json +++ b/schema/functions.json @@ -4,23 +4,14 @@ "description": "Serverless Workflow specification - functions schema", "type": "object", "functions": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "description": "URI to a resource containing function definitions (json or yaml)" - }, - { - "type": "array", - "description": "Workflow function definitions", - "items": { - "type": "object", - "$ref": "#/definitions/function" - }, - "additionalItems": false, - "minItems": 1 - } - ] + "type": "array", + "description": "Workflow function definitions", + "items": { + "type": "object", + "$ref": "#/definitions/function" + }, + "additionalItems": false, + "minItems": 1 }, "required": [ "functions" diff --git a/schema/resources.json b/schema/resources.json new file mode 100644 index 00000000..63e5c583 --- /dev/null +++ b/schema/resources.json @@ -0,0 +1,100 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/resources.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Serverless Workflow specification - resources schema", + "type": "object", + "resources":{ + "type": "array", + "description": "Defines the external resources referenced by the workflow", + "items": { + "type": "object", + "$ref": "#/definitions/resourceRef" + }, + "additionalItems": false, + "minItems": 1 + }, + "required":[ + "resources" + ], + "definitions":{ + "resourceRef":{ + "type": "object", + "description": "References an external resource", + "properties": { + "name": { + "type": "string", + "description": "The name of the external resource. It must be lowercased and must only contain alphanumeric characters, with the exception of `-` and `_`. The name is used as prefix to reference any definition contained in the referenced resource.", + "pattern": "^[a-z0-9-_]+$" + }, + "uri": { + "type": "string", + "format": "uri", + "description": "The uri at which to find the referenced external resource definition" + }, + "authRef": { + "type": "string", + "description": "References the (inline) auth definition used to retrieve the referenced external resource" + } + }, + "required": [ + "name", + "uri" + ] + }, + "resourceDef":{ + "type": "object", + "description": "Defines an external resource", + "properties": { + "constants": { + "type": "object" + }, + "secrets": { + "type": "array", + "items": { + "$ref": "secrets.json#/secrets" + } + }, + "retries": { + "type": "array", + "items": { + "$ref": "retries.json#/retries" + } + }, + "auth": { + "type": "array", + "items": { + "$ref": "auth.json#/auth" + } + }, + "timeouts": { + "type": "array", + "items": { + "$ref": "timeouts.json#/timeouts" + } + }, + "events": { + "type": "array", + "items": { + "$ref": "events.json#/events" + } + }, + "functions": { + "type": "array", + "items": { + "$ref": "functions.json#/functions" + } + }, + "errors": { + "type": "array", + "items": { + "$ref": "errors.json#/errors" + } + }, + "metadata":{ + "type": "object", + "additionalProperties": true + } + } + } + } +} \ No newline at end of file diff --git a/schema/retries.json b/schema/retries.json index ab113212..ea7f9120 100644 --- a/schema/retries.json +++ b/schema/retries.json @@ -4,23 +4,14 @@ "description": "Serverless Workflow specification - retries schema", "type": "object", "retries": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "description": "URI to a resource containing retry definitions (json or yaml)" - }, - { - "type": "array", - "description": "Workflow Retry definitions. Define retry strategies that can be referenced in states onError definitions", - "items": { - "type": "object", - "$ref": "#/definitions/retrydef" - }, - "additionalItems": false, - "minItems": 1 - } - ] + "type": "array", + "description": "Workflow Retry definitions. Define retry strategies that can be referenced in states onError definitions", + "items": { + "type": "object", + "$ref": "#/definitions/retrydef" + }, + "additionalItems": false, + "minItems": 1 }, "required": [ "retries" diff --git a/schema/secrets.json b/schema/secrets.json index dc553cc1..990f56d8 100644 --- a/schema/secrets.json +++ b/schema/secrets.json @@ -4,21 +4,12 @@ "description": "Serverless Workflow specification - secrets schema", "type": "object", "secrets": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "description": "URI to a resource containing secrets definitions (json or yaml)" - }, - { - "type": "array", - "description": "Workflow Secrets definitions", - "items": { - "type": "string" - }, - "minItems": 1 - } - ] + "type": "array", + "description": "Workflow Secrets definitions", + "items": { + "type": "string" + }, + "minItems": 1 }, "required": [ "secrets" diff --git a/schema/timeouts.json b/schema/timeouts.json index aa263e8e..38de1db9 100644 --- a/schema/timeouts.json +++ b/schema/timeouts.json @@ -4,36 +4,27 @@ "description": "Serverless Workflow specification - functions schema", "type": "object", "timeouts": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "description": "URI to a resource containing timeouts definitions (json or yaml)" + "type": "object", + "description": "Workflow default timeouts", + "properties": { + "workflowExecTimeout": { + "$ref": "#/definitions/workflowExecTimeout" }, - { - "type": "object", - "description": "Workflow default timeouts", - "properties": { - "workflowExecTimeout": { - "$ref": "#/definitions/workflowExecTimeout" - }, - "stateExecTimeout": { - "$ref": "#/definitions/stateExecTimeout" - }, - "actionExecTimeout": { - "$ref": "#/definitions/actionExecTimeout" - }, - "branchExecTimeout": { - "$ref": "#/definitions/branchExecTimeout" - }, - "eventTimeout": { - "$ref": "#/definitions/eventTimeout" - } - }, - "additionalProperties": false, - "required": [] + "stateExecTimeout": { + "$ref": "#/definitions/stateExecTimeout" + }, + "actionExecTimeout": { + "$ref": "#/definitions/actionExecTimeout" + }, + "branchExecTimeout": { + "$ref": "#/definitions/branchExecTimeout" + }, + "eventTimeout": { + "$ref": "#/definitions/eventTimeout" } - ] + }, + "additionalProperties": false, + "required": [] }, "required": [ "timeouts" diff --git a/schema/workflow.json b/schema/workflow.json index 61e6c47b..6957886b 100644 --- a/schema/workflow.json +++ b/schema/workflow.json @@ -44,21 +44,15 @@ "dataOutputSchema": { "$ref": "#/definitions/validationSchema" }, + "resources":{ + "$ref": "resources.json#/resources" + }, "secrets": { "$ref": "secrets.json#/secrets" }, "constants": { - "oneOf": [ - { - "type": "string", - "format": "uri", - "description": "URI to a resource containing constants data (json or yaml)" - }, - { - "type": "object", - "description": "Workflow constants data (object type)" - } - ] + "type": "object", + "description": "Workflow constants data (object type)" }, "start": { "$ref": "#/definitions/startdef" diff --git a/specification.md b/specification.md index ea4d2ef1..e2866467 100644 --- a/specification.md +++ b/specification.md @@ -1933,6 +1933,7 @@ definition "id" must be a constant value. | annotations | List of helpful terms describing the workflows intended purpose, subject areas, or other important qualities | array | no | | dataInputSchema | Used to validate the workflow data input against a defined JSON Schema| string or object | no | | dataOutputSchema | Used to validate the workflow data output against a defined JSON Schema| string or object | no | +| [resources](#Workflow-Resources) | External resources imported by the workflow | array | no | | [constants](#Workflow-Constants) | Workflow constants | string or object | no | | [secrets](#Workflow-Secrets) | Workflow secrets | string or array | no | | [start](#Start-Definition) | Workflow start definition | string or object | no | @@ -6173,6 +6174,60 @@ There are two places in the [workflow definition](#Workflow-Definition-Structure The `version` property must respect the [semantic versioning](https://semver.org/) guidelines. +### Workflow Resources + +Third parties have the ability to declare a set of resources that can be imported in a workflow and which are used to declare a set of reusable functionalities. It can be seen as a document describing the interactions supported by a given service with Serverless Workflows. + +External resources are defined by the workflow's top-level [resources property](#Workflow-Definition-Structure). + +Because a workflow can reference multiple resources that can potentially define components with conflicting names (ex: resource 'A' and resource 'B' both define a function called 'F'), resources are name spaced, which means that references to the components they define must be prefixed with their name (i.e. 'A.F' references the function 'F' defined in the resource 'A'). + +*Example of an external definition located at the mock address https://test.com/myresource.json:* + +```yaml +functions: + - name: getPetById + type: openapi + operation: https://petstore.swagger.io/v2/swagger.json#getPetById +``` + +*Example of a workflow using the above external definition:* + +```yaml +id: get-pet-availability +name: Get Pet Availability +version: 1.0.0 +specVersion: 0.8 +resources: + - name: petstore + uri: https://test.com/myresource.json +functions: + - name: email-send + type: openapi + operation: smtp.json#send +states: + - name: Get Pet from PetStore + type: operation + actions: + - name: Get Pet + functionRef: + refName: petstore.getPetById #note that the reference is prefixed with 'petstore', which is the name of the external resource that defines the 'getPetById' function + arguments: + petId: ${ .input.petId } + actionDataFilter: + toStateData: ${ .pet } + - name: Send Status By Email + functionRef: + refName: email-send #note that the function name is prefixless because the function has been defined inline, and is therefore considered as part of the 'default' name space + arguments: + from: info@petstore.io + to: ${ .input.client.email } + subject: Pet Inquiry Result + isBodyHtml: true + body: The status of the pet you enquired (${ .input.petId }) about is '${ .pet.status }' + end: true +``` + ### Workflow Constants Workflow constants are used to define static, and immutable, data which is available to [Workflow Expressions](#Workflow-Expressions).