diff --git a/Pipfile b/Pipfile new file mode 100644 index 0000000..952677e --- /dev/null +++ b/Pipfile @@ -0,0 +1,15 @@ +[[source]] +url = "https://pypi.org/simple" +verify_ssl = true +name = "pypi" + +[packages] +jsonschema = "==4.4.0" +pyyaml = "==6.0" + +[dev-packages] +pytest = "==6.2.5" +pytest-runner = "==5.3.1" + +[requires] +python_version = "3.9" diff --git a/Pipfile.lock b/Pipfile.lock new file mode 100644 index 0000000..42bf310 --- /dev/null +++ b/Pipfile.lock @@ -0,0 +1,175 @@ +{ + "_meta": { + "hash": { + "sha256": "656dd55f74aa500a1cd756ff6cd720b05744451df49f718d5edda32261ea6e2f" + }, + "pipfile-spec": 6, + "requires": { + "python_version": "3.9" + }, + "sources": [ + { + "name": "pypi", + "url": "https://pypi.org/simple", + "verify_ssl": true + } + ] + }, + "default": { + "attrs": { + "hashes": [ + "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", + "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==21.4.0" + }, + "jsonschema": { + "hashes": [ + "sha256:636694eb41b3535ed608fe04129f26542b59ed99808b4f688aa32dcf55317a83", + "sha256:77281a1f71684953ee8b3d488371b162419767973789272434bbc3f29d9c8823" + ], + "index": "pypi", + "version": "==4.4.0" + }, + "pyrsistent": { + "hashes": [ + "sha256:0e3e1fcc45199df76053026a51cc59ab2ea3fc7c094c6627e93b7b44cdae2c8c", + "sha256:1b34eedd6812bf4d33814fca1b66005805d3640ce53140ab8bbb1e2651b0d9bc", + "sha256:4ed6784ceac462a7d6fcb7e9b663e93b9a6fb373b7f43594f9ff68875788e01e", + "sha256:5d45866ececf4a5fff8742c25722da6d4c9e180daa7b405dc0a2a2790d668c26", + "sha256:636ce2dc235046ccd3d8c56a7ad54e99d5c1cd0ef07d9ae847306c91d11b5fec", + "sha256:6455fc599df93d1f60e1c5c4fe471499f08d190d57eca040c0ea182301321286", + "sha256:6bc66318fb7ee012071b2792024564973ecc80e9522842eb4e17743604b5e045", + "sha256:7bfe2388663fd18bd8ce7db2c91c7400bf3e1a9e8bd7d63bf7e77d39051b85ec", + "sha256:7ec335fc998faa4febe75cc5268a9eac0478b3f681602c1f27befaf2a1abe1d8", + "sha256:914474c9f1d93080338ace89cb2acee74f4f666fb0424896fcfb8d86058bf17c", + "sha256:b568f35ad53a7b07ed9b1b2bae09eb15cdd671a5ba5d2c66caee40dbf91c68ca", + "sha256:cdfd2c361b8a8e5d9499b9082b501c452ade8bbf42aef97ea04854f4a3f43b22", + "sha256:d1b96547410f76078eaf66d282ddca2e4baae8964364abb4f4dcdde855cd123a", + "sha256:d4d61f8b993a7255ba714df3aca52700f8125289f84f704cf80916517c46eb96", + "sha256:d7a096646eab884bf8bed965bad63ea327e0d0c38989fc83c5ea7b8a87037bfc", + "sha256:df46c854f490f81210870e509818b729db4488e1f30f2a1ce1698b2295a878d1", + "sha256:e24a828f57e0c337c8d8bb9f6b12f09dfdf0273da25fda9e314f0b684b415a07", + "sha256:e4f3149fd5eb9b285d6bfb54d2e5173f6a116fe19172686797c056672689daf6", + "sha256:e92a52c166426efbe0d1ec1332ee9119b6d32fc1f0bbfd55d5c1088070e7fc1b", + "sha256:f87cc2863ef33c709e237d4b5f4502a62a00fab450c9e020892e8e2ede5847f5", + "sha256:fd8da6d0124efa2f67d86fa70c851022f87c98e205f0594e1fae044e7119a5a6" + ], + "markers": "python_version >= '3.7'", + "version": "==0.18.1" + }, + "pyyaml": { + "hashes": [ + "sha256:0283c35a6a9fbf047493e3a0ce8d79ef5030852c51e9d911a27badfde0605293", + "sha256:055d937d65826939cb044fc8c9b08889e8c743fdc6a32b33e2390f66013e449b", + "sha256:07751360502caac1c067a8132d150cf3d61339af5691fe9e87803040dbc5db57", + "sha256:0b4624f379dab24d3725ffde76559cff63d9ec94e1736b556dacdfebe5ab6d4b", + "sha256:0ce82d761c532fe4ec3f87fc45688bdd3a4c1dc5e0b4a19814b9009a29baefd4", + "sha256:1e4747bc279b4f613a09eb64bba2ba602d8a6664c6ce6396a4d0cd413a50ce07", + "sha256:213c60cd50106436cc818accf5baa1aba61c0189ff610f64f4a3e8c6726218ba", + "sha256:231710d57adfd809ef5d34183b8ed1eeae3f76459c18fb4a0b373ad56bedcdd9", + "sha256:277a0ef2981ca40581a47093e9e2d13b3f1fbbeffae064c1d21bfceba2030287", + "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513", + "sha256:40527857252b61eacd1d9af500c3337ba8deb8fc298940291486c465c8b46ec0", + "sha256:473f9edb243cb1935ab5a084eb238d842fb8f404ed2193a915d1784b5a6b5fc0", + "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92", + "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f", + "sha256:68fb519c14306fec9720a2a5b45bc9f0c8d1b9c72adf45c37baedfcd949c35a2", + "sha256:77f396e6ef4c73fdc33a9157446466f1cff553d979bd00ecb64385760c6babdc", + "sha256:819b3830a1543db06c4d4b865e70ded25be52a2e0631ccd2f6a47a2822f2fd7c", + "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86", + "sha256:98c4d36e99714e55cfbaaee6dd5badbc9a1ec339ebfc3b1f52e293aee6bb71a4", + "sha256:9df7ed3b3d2e0ecfe09e14741b857df43adb5a3ddadc919a2d94fbdf78fea53c", + "sha256:9fa600030013c4de8165339db93d182b9431076eb98eb40ee068700c9c813e34", + "sha256:a80a78046a72361de73f8f395f1f1e49f956c6be882eed58505a15f3e430962b", + "sha256:b3d267842bf12586ba6c734f89d1f5b871df0273157918b0ccefa29deb05c21c", + "sha256:b5b9eccad747aabaaffbc6064800670f0c297e52c12754eb1d976c57e4f74dcb", + "sha256:c5687b8d43cf58545ade1fe3e055f70eac7a5a1a0bf42824308d868289a95737", + "sha256:cba8c411ef271aa037d7357a2bc8f9ee8b58b9965831d9e51baf703280dc73d3", + "sha256:d15a181d1ecd0d4270dc32edb46f7cb7733c7c508857278d3d378d14d606db2d", + "sha256:d4db7c7aef085872ef65a8fd7d6d09a14ae91f691dec3e87ee5ee0539d516f53", + "sha256:d4eccecf9adf6fbcc6861a38015c2a64f38b9d94838ac1810a9023a0609e1b78", + "sha256:d67d839ede4ed1b28a4e8909735fc992a923cdb84e618544973d7dfc71540803", + "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a", + "sha256:e61ceaab6f49fb8bdfaa0f92c4b57bcfbea54c09277b1b4f7ac376bfb7a7c174", + "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5" + ], + "index": "pypi", + "version": "==6.0" + } + }, + "develop": { + "attrs": { + "hashes": [ + "sha256:2d27e3784d7a565d36ab851fe94887c5eccd6a463168875832a1be79c82828b4", + "sha256:626ba8234211db98e869df76230a137c4c40a12d72445c45d5f5b716f076e2fd" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==21.4.0" + }, + "iniconfig": { + "hashes": [ + "sha256:011e24c64b7f47f6ebd835bb12a743f2fbe9a26d4cecaa7f53bc4f35ee9da8b3", + "sha256:bc3af051d7d14b2ee5ef9969666def0cd1a000e121eaea580d4a313df4b37f32" + ], + "version": "==1.1.1" + }, + "packaging": { + "hashes": [ + "sha256:dd47c42927d89ab911e606518907cc2d3a1f38bbd026385970643f9c5b8ecfeb", + "sha256:ef103e05f519cdc783ae24ea4e2e0f508a9c99b2d4969652eed6a2e1ea5bd522" + ], + "markers": "python_version >= '3.6'", + "version": "==21.3" + }, + "pluggy": { + "hashes": [ + "sha256:4224373bacce55f955a878bf9cfa763c1e360858e330072059e10bad68531159", + "sha256:74134bbf457f031a36d68416e1509f34bd5ccc019f0bcc952c7b909d06b37bd3" + ], + "markers": "python_version >= '3.6'", + "version": "==1.0.0" + }, + "py": { + "hashes": [ + "sha256:51c75c4126074b472f746a24399ad32f6053d1b34b68d2fa41e558e6f4a98719", + "sha256:607c53218732647dff4acdfcd50cb62615cedf612e72d1724fb1a0cc6405b378" + ], + "markers": "python_version >= '2.7' and python_version not in '3.0, 3.1, 3.2, 3.3, 3.4'", + "version": "==1.11.0" + }, + "pyparsing": { + "hashes": [ + "sha256:04ff808a5b90911829c55c4e26f75fa5ca8a2f5f36aa3a51f68e27033341d3e4", + "sha256:d9bdec0013ef1eb5a84ab39a3b3868911598afa494f5faa038647101504e2b81" + ], + "markers": "python_version >= '3.6'", + "version": "==3.0.6" + }, + "pytest": { + "hashes": [ + "sha256:131b36680866a76e6781d13f101efb86cf674ebb9762eb70d3082b6f29889e89", + "sha256:7310f8d27bc79ced999e760ca304d69f6ba6c6649c0b60fb0e04a4a77cacc134" + ], + "index": "pypi", + "version": "==6.2.5" + }, + "pytest-runner": { + "hashes": [ + "sha256:0fce5b8dc68760f353979d99fdd6b3ad46330b6b1837e2077a89ebcf204aac91", + "sha256:85f93af814438ee322b4ea08fe3f5c2ad53b253577f3bd84b2ad451fee450ac5" + ], + "index": "pypi", + "version": "==5.3.1" + }, + "toml": { + "hashes": [ + "sha256:806143ae5bfb6a3c6e736a764057db0e6a0e05e338b5630894a5f779cabb4f9b", + "sha256:b3bda1d108d5dd99f4a20d24d9c348e91c4db7ab1b749200bded2f839ccbe68f" + ], + "markers": "python_version >= '2.6' and python_version not in '3.0, 3.1, 3.2'", + "version": "==0.10.2" + } + } +} diff --git a/README.md b/README.md index 479d08f..4a66c24 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,102 @@ -# sdk-python -Serverless Workflow Python SDK +# Serverless Workflow Specification - Typescript SDK + +Provides the Python API/SPI for the [Serverless Workflow Specification](https://github.com/serverlessworkflow/specification) + +> This is a WIP implementation + +With the SDK you can: +* **WIP** Programmatically build workflow definitions +* Parse workflow JSON and YAML definitions +* Validate workflow definitions + + +## Install dependencies and run test + +- `pipenv install` +- `python setup.py pytest` + +## **WIP** Programmatically build workflow definitions + +``` + workflow = Workflow(id_="greeting", + name="Greeting Workflow", + description="Greet Someone", + version='1.0', + specVersion='0.8', + start="Greet", + states=[], + functions=[] + ) + +``` + +## Parse workflow JSON and YAML definitions + +### Convert from JSON or YAML source + +``` + swf_content = """id: greeting +name: Greeting Workflow +version: '1.0' +description: Greet Someone +specVersion: '0.8' +start: Greet +states: +- name: Greet + type: operation + actions: + - functionRef: + refName: greetingFunction + arguments: + name: ${ .person.name } + actionDataFilter: + results: ${ .greeting } + end: true +functions: +- name: greetingFunction + operation: file://myapis/greetingapis.json#greeting +""" + workflow = Workflow.from_source(swf_content) +``` + +You can see a full example in the [test_workflow.py](./tests/test_workflow.py) file + + +### Parse workflow to JSON / YAML + +``` + workflow = Workflow(id_="greeting", + name="Greeting Workflow", + description="Greet Someone", + version='1.0', + specVersion='0.8', + start="Greet", + states=[], + functions=[] + ) + + print(workflow.to_json()) + print(workflow.to_yaml()) +``` + +You can see a full example in the [test_workflow.py](./tests/test_workflow.py) file + + +## Validate workflow definitions + +``` + workflow = Workflow(id_="greeting", + name="Greeting Workflow", + description="Greet Someone", + version='1.0', + specVersion='0.8', + start="Greet", + states=[], + functions=[] + ) + WorkflowValidator(Workflow(workflow)).validate() + +``` +The `validate` method will raise an exception if the provided workflow does not complaint specification. + +You can see a full example in the [test_workflow_validator](./tests/test_workflow_validator.py) file \ No newline at end of file diff --git a/serverlessworkflow_sdk/__init__.py b/serverlessworkflow_sdk/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/serverlessworkflow_sdk/action.py b/serverlessworkflow_sdk/action.py new file mode 100644 index 0000000..9c49906 --- /dev/null +++ b/serverlessworkflow_sdk/action.py @@ -0,0 +1,38 @@ +class FunctionRef: + pass + + +class ActionDataFilter: + pass + + +class Action: + functionRef: FunctionRef + actionDataFilter: ActionDataFilter + + def __init__(self, functionRef: FunctionRef = None, + actionDataFilter: ActionDataFilter = None, + **kwargs): + # duplicated + for local in list(locals()): + if local in ["self", "kwargs"]: + continue + value = locals().get(local) + if not value: + continue + if value == "true": + value = True + # duplicated + + + self.__setattr__(local.replace("_", ""), value) + + # duplicated + for k in kwargs.keys(): + value = kwargs[k] + if value == "true": + value = True + + self.__setattr__(k.replace("_", ""), value) + # duplicated + diff --git a/serverlessworkflow_sdk/inject_state.py b/serverlessworkflow_sdk/inject_state.py new file mode 100644 index 0000000..9ce0c53 --- /dev/null +++ b/serverlessworkflow_sdk/inject_state.py @@ -0,0 +1,32 @@ +from serverlessworkflow_sdk.state import State + + +class InjectState(State): + + data: dict + + def __init__(self, data: dict = None, + **kwargs): + # duplicated + for local in list(locals()): + if local in ["self", "kwargs"]: + continue + value = locals().get(local) + if not value: + continue + if value == "true": + value = True + # duplicated + + self.__setattr__(local.replace("_", ""), value) + + # duplicated + for k in kwargs.keys(): + value = kwargs[k] + if value == "true": + value = True + + self.__setattr__(k.replace("_", ""), value) + # duplicated + + diff --git a/serverlessworkflow_sdk/jsonschemas/auth.json b/serverlessworkflow_sdk/jsonschemas/auth.json new file mode 100644 index 0000000..8be26b4 --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/auth.json @@ -0,0 +1,221 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/auth.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "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 + } + ] + }, + "required": [ + "auth" + ], + "definitions": { + "authdef": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique auth definition name", + "minLength": 1 + }, + "scheme": { + "type": "string", + "description": "Defines the auth type", + "enum": [ + "basic", + "bearer", + "oauth2" + ], + "default": "basic" + }, + "properties": { + "oneOf": [ + { + "type": "string", + "description": "Expression referencing a workflow secret that contains all needed auth info" + }, + { + "title": "Basic Auth Info", + "$ref": "#/definitions/basicpropsdef" + }, + { + "title": "Bearer Auth Info State", + "$ref": "#/definitions/bearerpropsdef" + }, + { + "title": "OAuth2 Info", + "$ref": "#/definitions/oauth2propsdef" + } + ] + } + }, + "required": [ + "name", + "properties" + ] + }, + "basicpropsdef": { + "oneOf": [ + { + "type": "string", + "description": "Expression referencing a workflow secret that contains all needed basic auth info" + }, + { + "type": "object", + "description": "Basic auth information", + "properties": { + "username": { + "type": "string", + "description": "String or a workflow expression. Contains the user name", + "minLength": 1 + }, + "password": { + "type": "string", + "description": "String or a workflow expression. Contains the user password", + "minLength": 1 + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "required": [ + "username", + "password" + ], + "additionalProperties": false + } + ] + }, + "bearerpropsdef": { + "oneOf": [ + { + "type": "string", + "description": "Expression referencing a workflow secret that contains all needed bearer auth info" + }, + { + "type": "object", + "description": "Bearer auth information", + "properties": { + "token": { + "type": "string", + "description": "String or a workflow expression. Contains the token", + "minLength": 1 + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "required": [ + "token" + ], + "additionalProperties": false + } + ] + }, + "oauth2propsdef": { + "oneOf": [ + { + "type": "string", + "description": "Expression referencing a workflow secret that contains all needed OAuth2 auth info" + }, + { + "type": "object", + "description": "OAuth2 information", + "properties": { + "authority": { + "type": "string", + "description": "String or a workflow expression. Contains the authority information", + "minLength": 1 + }, + "grantType": { + "type": "string", + "description": "Defines the grant type", + "enum": [ + "password", + "clientCredentials", + "tokenExchange" + ], + "additionalItems": false + }, + "clientId": { + "type": "string", + "description": "String or a workflow expression. Contains the client identifier", + "minLength": 1 + }, + "clientSecret": { + "type": "string", + "description": "Workflow secret or a workflow expression. Contains the client secret", + "minLength": 1 + }, + "scopes": { + "type": "array", + "description": "Array containing strings or workflow expressions. Contains the OAuth2 scopes", + "items": { + "type": "string" + }, + "minItems": 1, + "additionalItems": false + }, + "username": { + "type": "string", + "description": "String or a workflow expression. Contains the user name. Used only if grantType is 'resourceOwner'", + "minLength": 1 + }, + "password": { + "type": "string", + "description": "String or a workflow expression. Contains the user password. Used only if grantType is 'resourceOwner'", + "minLength": 1 + }, + "audiences": { + "type": "array", + "description": "Array containing strings or workflow expressions. Contains the OAuth2 audiences", + "items": { + "type": "string" + }, + "minItems": 1, + "additionalItems": false + }, + "subjectToken": { + "type": "string", + "description": "String or a workflow expression. Contains the subject token", + "minLength": 1 + }, + "requestedSubject": { + "type": "string", + "description": "String or a workflow expression. Contains the requested subject", + "minLength": 1 + }, + "requestedIssuer": { + "type": "string", + "description": "String or a workflow expression. Contains the requested issuer", + "minLength": 1 + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "required": [ + "grantType", + "clientId" + ] + } + ] + } + } +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/jsonschemas/common.json b/serverlessworkflow_sdk/jsonschemas/common.json new file mode 100644 index 0000000..5a3593e --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/common.json @@ -0,0 +1,15 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/common.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Serverless Workflow specification - common schema", + "type": "object", + "definitions": { + "metadata": { + "type": "object", + "description": "Metadata information", + "additionalProperties": { + "type": "string" + } + } + } +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/jsonschemas/errors.json b/serverlessworkflow_sdk/jsonschemas/errors.json new file mode 100644 index 0000000..c855548 --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/errors.json @@ -0,0 +1,53 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/errors.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "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 + } + ] + }, + "required": [ + "errors" + ], + "definitions": { + "errordef": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Domain-specific error name", + "minLength": 1 + }, + "code": { + "type": "string", + "description": "Error code. Can be used in addition to the name to help runtimes resolve to technical errors/exceptions. Should not be defined if error is set to '*'", + "minLength": 1 + }, + "description": { + "type": "string", + "description": "Error description" + } + }, + "additionalProperties": false, + "required": [ + "name" + ] + } + } +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/jsonschemas/events.json b/serverlessworkflow_sdk/jsonschemas/events.json new file mode 100644 index 0000000..61d16e1 --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/events.json @@ -0,0 +1,117 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/events.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "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 + } + ] + }, + "required": [ + "events" + ], + "definitions": { + "eventdef": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique event name", + "minLength": 1 + }, + "source": { + "type": "string", + "description": "CloudEvent source" + }, + "type": { + "type": "string", + "description": "CloudEvent type" + }, + "kind": { + "type": "string", + "enum": [ + "consumed", + "produced" + ], + "description": "Defines the CloudEvent as either 'consumed' or 'produced' by the workflow. Default is 'consumed'", + "default": "consumed" + }, + "correlation": { + "type": "array", + "description": "CloudEvent correlation definitions", + "minItems": 1, + "items": { + "type": "object", + "$ref": "#/definitions/correlationDef" + }, + "additionalItems": false + }, + "dataOnly": { + "type": "boolean", + "default": true, + "description": "If `true`, only the Event payload is accessible to consuming Workflow states. If `false`, both event payload and context attributes should be accessible " + }, + "metadata": { + "$ref": "common.json#/definitions/metadata", + "description": "Metadata information" + } + }, + "additionalProperties": false, + "if": { + "properties": { + "kind": { + "const": "consumed" + } + } + }, + "then": { + "required": [ + "name", + "source", + "type" + ] + }, + "else": { + "required": [ + "name", + "type" + ] + } + }, + "correlationDef": { + "type": "object", + "description": "CloudEvent correlation definition", + "properties": { + "contextAttributeName": { + "type": "string", + "description": "CloudEvent Extension Context Attribute name", + "minLength": 1 + }, + "contextAttributeValue": { + "type": "string", + "description": "CloudEvent Extension Context Attribute value", + "minLength": 1 + } + }, + "additionalProperties": false, + "required": [ + "contextAttributeName" + ] + } + } +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/jsonschemas/functions.json b/serverlessworkflow_sdk/jsonschemas/functions.json new file mode 100644 index 0000000..08f3b34 --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/functions.json @@ -0,0 +1,72 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/functions.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "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 + } + ] + }, + "required": [ + "functions" + ], + "definitions": { + "function": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique function name", + "minLength": 1 + }, + "operation": { + "type": "string", + "description": "If type is `rest`, #. If type is `asyncapi`, #. If type is `rpc`, ##. If type is `graphql`, ##. If type is `odata`, #. If type is `expression`, defines the workflow expression.", + "minLength": 1 + }, + "type": { + "type": "string", + "description": "Defines the function type. Is either `rest`, `asyncapi, `rpc`, `graphql`, `odata`, `expression`, or `custom`. Default is `rest`", + "enum": [ + "rest", + "asyncapi", + "rpc", + "graphql", + "odata", + "expression", + "custom" + ], + "default": "rest" + }, + "authRef": { + "type": "string", + "description": "References an auth definition name to be used to access to resource defined in the operation parameter", + "minLength": 1 + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "required": [ + "name", + "operation" + ] + } + } +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/jsonschemas/retries.json b/serverlessworkflow_sdk/jsonschemas/retries.json new file mode 100644 index 0000000..743b758 --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/retries.json @@ -0,0 +1,85 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/retries.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "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 + } + ] + }, + "required": [ + "retries" + ], + "definitions": { + "retrydef": { + "type": "object", + "properties": { + "name": { + "type": "string", + "description": "Unique retry strategy name", + "minLength": 1 + }, + "delay": { + "type": "string", + "description": "Time delay between retry attempts (ISO 8601 duration format)" + }, + "maxDelay": { + "type": "string", + "description": "Maximum time delay between retry attempts (ISO 8601 duration format)" + }, + "increment": { + "type": "string", + "description": "Static value by which the delay increases during each attempt (ISO 8601 time format)" + }, + "multiplier": { + "type": [ + "number", + "string" + ], + "minimum": 0, + "minLength": 1, + "multipleOf": 0.01, + "description": "Numeric value, if specified the delay between retries is multiplied by this value." + }, + "maxAttempts": { + "type": [ + "number", + "string" + ], + "minimum": 1, + "minLength": 0, + "description": "Maximum number of retry attempts." + }, + "jitter": { + "type": [ + "number", + "string" + ], + "minimum": 0, + "maximum": 1, + "description": "If float type, maximum amount of random time added or subtracted from the delay between each retry relative to total delay (between 0 and 1). If string type, absolute maximum amount of random time added or subtracted from the delay between each retry (ISO 8601 duration format)" + } + }, + "additionalProperties": false, + "required": [ + "name", + "maxAttempts" + ] + } + } +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/jsonschemas/secrets.json b/serverlessworkflow_sdk/jsonschemas/secrets.json new file mode 100644 index 0000000..dc553cc --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/secrets.json @@ -0,0 +1,26 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/secrets.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "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 + } + ] + }, + "required": [ + "secrets" + ] +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/jsonschemas/timeouts.json b/serverlessworkflow_sdk/jsonschemas/timeouts.json new file mode 100644 index 0000000..3bd565a --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/timeouts.json @@ -0,0 +1,120 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/timeouts.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "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" + }, + "stateExecTimeout": { + "$ref": "#/definitions/stateExecTimeout" + }, + "actionExecTimeout": { + "$ref": "#/definitions/actionExecTimeout" + }, + "branchExecTimeout": { + "$ref": "#/definitions/branchExecTimeout" + }, + "eventTimeout": { + "$ref": "#/definitions/eventTimeout" + } + }, + "additionalProperties": false, + "required": [] + } + ] + }, + "required": [ + "timeouts" + ], + "definitions": { + "workflowExecTimeout": { + "oneOf": [ + { + "type": "string", + "description": "Workflow execution timeout duration (ISO 8601 duration format). If not specified should be 'unlimited'", + "minLength": 1 + }, + { + "type": "object", + "properties": { + "duration": { + "type": "string", + "description": "Workflow execution timeout duration (ISO 8601 duration format). If not specified should be 'unlimited'", + "minLength": 1 + }, + "interrupt": { + "type": "boolean", + "description": "If `false`, workflow instance is allowed to finish current execution. If `true`, current workflow execution is abrupted.", + "default": true + }, + "runBefore": { + "type": "string", + "description": "Name of a workflow state to be executed before workflow instance is terminated", + "minLength": 1 + } + }, + "additionalProperties": false, + "required": [ + "duration" + ] + } + ] + }, + "stateExecTimeout": { + "oneOf": [ + { + "type": "string", + "description": "Total state execution timeout (including retries) (ISO 8601 duration format)", + "minLength": 1 + }, + { + "type": "object", + "description": "Workflow default timeouts", + "properties": { + "single": { + "type": "string", + "description": "Single state execution timeout, not including retries (ISO 8601 duration format)", + "minLength": 1 + }, + "total": { + "type": "string", + "description": "Total state execution timeout, including retries (ISO 8601 duration format)", + "minLength": 1 + } + }, + "additionalProperties": false, + "required": [ + "total" + ] + } + ] + }, + "actionExecTimeout": { + "type": "string", + "description": "Single actions definition execution timeout duration (ISO 8601 duration format)", + "minLength": 1 + }, + "branchExecTimeout": { + "type": "string", + "description": "Single branch execution timeout duration (ISO 8601 duration format)", + "minLength": 1 + }, + "eventTimeout": { + "type": "string", + "description": "Timeout duration to wait for consuming defined events (ISO 8601 duration format)", + "minLength": 1 + } + } +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/jsonschemas/workflow.json b/serverlessworkflow_sdk/jsonschemas/workflow.json new file mode 100644 index 0000000..2d11730 --- /dev/null +++ b/serverlessworkflow_sdk/jsonschemas/workflow.json @@ -0,0 +1,2008 @@ +{ + "$id": "https://serverlessworkflow.io/schemas/0.8/workflow.json", + "$schema": "http://json-schema.org/draft-07/schema#", + "description": "Serverless Workflow specification - workflow schema", + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Workflow unique identifier", + "minLength": 1 + }, + "key": { + "type": "string", + "description": "Domain-specific workflow identifier", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "Workflow name", + "minLength": 1 + }, + "description": { + "type": "string", + "description": "Workflow description" + }, + "version": { + "type": "string", + "description": "Workflow version", + "minLength": 1 + }, + "annotations": { + "type": "array", + "description": "List of helpful terms describing the workflows intended purpose, subject areas, or other important qualities", + "minItems": 1, + "items": { + "type": "string" + }, + "additionalItems": false + }, + "dataInputSchema": { + "oneOf": [ + { + "type": "string", + "description": "URI of the JSON Schema used to validate the workflow data input", + "minLength": 1 + }, + { + "type": "object", + "description": "Workflow data input schema definition", + "properties": { + "schema": { + "type": "string", + "description": "URI of the JSON Schema used to validate the workflow data input", + "minLength": 1 + }, + "failOnValidationErrors": { + "type": "boolean", + "default": true, + "description": "Determines if workflow execution should continue if there are validation errors" + } + }, + "additionalProperties": false, + "required": [ + "schema", + "failOnValidationErrors" + ] + } + ] + }, + "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)" + } + ] + }, + "start": { + "$ref": "#/definitions/startdef" + }, + "specVersion": { + "type": "string", + "description": "Serverless Workflow schema version", + "minLength": 1 + }, + "expressionLang": { + "type": "string", + "description": "Identifies the expression language used for workflow expressions. Default is 'jq'", + "default": "jq", + "minLength": 1 + }, + "timeouts": { + "$ref": "timeouts.json#/timeouts" + }, + "errors": { + "$ref": "errors.json#/errors" + }, + "keepActive": { + "type": "boolean", + "default": false, + "description": "If 'true', workflow instances is not terminated when there are no active execution paths. Instance can be terminated via 'terminate end definition' or reaching defined 'workflowExecTimeout'" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + }, + "events": { + "$ref": "events.json#/events" + }, + "functions": { + "$ref": "functions.json#/functions" + }, + "autoRetries": { + "type": "boolean", + "default": false, + "description": "If set to true, actions should automatically be retried on unchecked errors. Default is false" + }, + "retries": { + "$ref": "retries.json#/retries" + }, + "auth": { + "$ref": "auth.json#/auth" + }, + "states": { + "type": "array", + "description": "State definitions", + "items": { + "anyOf": [ + { + "title": "Sleep State", + "$ref": "#/definitions/sleepstate" + }, + { + "title": "Event State", + "$ref": "#/definitions/eventstate" + }, + { + "title": "Operation State", + "$ref": "#/definitions/operationstate" + }, + { + "title": "Parallel State", + "$ref": "#/definitions/parallelstate" + }, + { + "title": "Switch State", + "$ref": "#/definitions/switchstate" + }, + { + "title": "Inject State", + "$ref": "#/definitions/injectstate" + }, + { + "title": "ForEach State", + "$ref": "#/definitions/foreachstate" + }, + { + "title": "Callback State", + "$ref": "#/definitions/callbackstate" + } + ] + }, + "additionalItems": false, + "minItems": 1 + } + }, + "oneOf": [ + { + "required": [ + "id", + "specVersion", + "states" + ] + }, + { + "required": [ + "key", + "specVersion", + "states" + ] + } + ], + "definitions": { + "sleep": { + "type": "object", + "properties": { + "before": { + "type": "string", + "description": "Amount of time (ISO 8601 duration format) to sleep before function/subflow invocation. Does not apply if 'eventRef' is defined." + }, + "after": { + "type": "string", + "description": "Amount of time (ISO 8601 duration format) to sleep after function/subflow invocation. Does not apply if 'eventRef' is defined." + } + }, + "oneOf": [ + { + "required": [ + "before" + ] + }, + { + "required": [ + "after" + ] + }, + { + "required": [ + "before", + "after" + ] + } + ] + }, + "crondef": { + "oneOf": [ + { + "type": "string", + "description": "Cron expression defining when workflow instances should be created (automatically)", + "minLength": 1 + }, + { + "type": "object", + "properties": { + "expression": { + "type": "string", + "description": "Repeating interval (cron expression) describing when the workflow instance should be created", + "minLength": 1 + }, + "validUntil": { + "type": "string", + "description": "Specific date and time (ISO 8601 format) when the cron expression invocation is no longer valid" + } + }, + "additionalProperties": false, + "required": [ + "expression" + ] + } + ] + }, + "continueasdef": { + "oneOf": [ + { + "type": "string", + "description": "Unique id of the workflow to be continue execution as. Entire state data is passed as data input to next execution", + "minLength": 1 + }, + { + "type": "object", + "properties": { + "workflowId": { + "type": "string", + "description": "Unique id of the workflow to continue execution as" + }, + "version": { + "type": "string", + "description": "Version of the workflow to continue execution as", + "minLength": 1 + }, + "data": { + "type": [ + "string", + "object" + ], + "description": "If string type, an expression which selects parts of the states data output to become the workflow data input of continued execution. If object type, a custom object to become the workflow data input of the continued execution" + }, + "workflowExecTimeout": { + "$ref": "timeouts.json#/definitions/workflowExecTimeout", + "description": "Workflow execution timeout to be used by the workflow continuing execution. Overwrites any specific settings set by that workflow" + } + }, + "required": [ + "workflowId" + ] + } + ] + }, + "transition": { + "oneOf": [ + { + "type": "string", + "description": "Name of state to transition to", + "minLength": 1 + }, + { + "type": "object", + "description": "Function Reference", + "properties": { + "nextState": { + "type": "string", + "description": "Name of state to transition to", + "minLength": 1 + }, + "produceEvents": { + "type": "array", + "description": "Array of events to be produced before the transition happens", + "items": { + "type": "object", + "$ref": "#/definitions/produceeventdef" + }, + "additionalItems": false + }, + "compensate": { + "type": "boolean", + "default": false, + "description": "If set to true, triggers workflow compensation when before this transition is taken. Default is false" + } + }, + "additionalProperties": false, + "required": [ + "nextState" + ] + } + ] + }, + "error": { + "type": "object", + "properties": { + "errorRef": { + "type": "string", + "description": "Reference to a unique workflow error definition. Used of errorRefs is not used", + "minLength": 1 + }, + "errorRefs": { + "type": "array", + "description": "References one or more workflow error definitions. Used if errorRef is not used", + "minItems": 1, + "items": { + "type": "string" + }, + "additionalItems": false + }, + "transition": { + "description": "Transition to next state to handle the error.", + "$ref": "#/definitions/transition" + }, + "end": { + "description": "End workflow execution in case of this error.", + "$ref": "#/definitions/end" + } + }, + "additionalProperties": false, + "oneOf": [ + { + "required": [ + "errorRef", + "transition" + ] + }, + { + "required": [ + "errorRef", + "end" + ] + }, + { + "required": [ + "errorRefs", + "transition" + ] + }, + { + "required": [ + "errorRefs", + "end" + ] + } + ] + }, + "onevents": { + "type": "object", + "properties": { + "eventRefs": { + "type": "array", + "description": "References one or more unique event names in the defined workflow events", + "minItems": 1, + "items": { + "type": "string" + }, + "uniqueItems": true, + "additionalItems": false + }, + "actionMode": { + "type": "string", + "enum": [ + "sequential", + "parallel" + ], + "description": "Specifies how actions are to be performed (in sequence or in parallel)", + "default": "sequential" + }, + "actions": { + "type": "array", + "description": "Actions to be performed if expression matches", + "items": { + "type": "object", + "$ref": "#/definitions/action" + }, + "additionalItems": false + }, + "eventDataFilter": { + "description": "Event data filter", + "$ref": "#/definitions/eventdatafilter" + } + }, + "additionalProperties": false, + "required": [ + "eventRefs" + ] + }, + "action": { + "type": "object", + "properties": { + "id": { + "type": "string", + "description": "Unique action identifier" + }, + "name": { + "type": "string", + "description": "Unique action definition name" + }, + "functionRef": { + "description": "References a function to be invoked", + "$ref": "#/definitions/functionref" + }, + "eventRef": { + "description": "References a 'trigger' and 'result' reusable event definitions", + "$ref": "#/definitions/eventref" + }, + "subFlowRef": { + "description": "References a sub-workflow to invoke", + "$ref": "#/definitions/subflowref" + }, + "sleep": { + "description": "Defines time periods workflow execution should sleep before / after function execution", + "$ref": "#/definitions/sleep" + }, + "retryRef": { + "type": "string", + "description": "References a defined workflow retry definition. If not defined the default retry policy is assumed" + }, + "nonRetryableErrors": { + "type": "array", + "description": "List of unique references to defined workflow errors for which the action should not be retried. Used only when `autoRetries` is set to `true`", + "minItems": 1, + "items": { + "type": "string" + }, + "additionalItems": false + }, + "retryableErrors": { + "type": "array", + "description": "List of unique references to defined workflow errors for which the action should be retried. Used only when `autoRetries` is set to `false`", + "minItems": 1, + "items": { + "type": "string" + }, + "additionalItems": false + }, + "actionDataFilter": { + "description": "Action data filter", + "$ref": "#/definitions/actiondatafilter" + }, + "condition": { + "description": "Expression, if defined, must evaluate to true for this action to be performed. If false, action is disregarded", + "type": "string", + "minLength": 1 + } + }, + "additionalProperties": false, + "oneOf": [ + { + "required": [ + "functionRef" + ] + }, + { + "required": [ + "eventRef" + ] + }, + { + "required": [ + "subFlowRef" + ] + } + ] + }, + "functionref": { + "oneOf": [ + { + "type": "string", + "description": "Name of the referenced function", + "minLength": 1 + }, + { + "type": "object", + "description": "Function Reference", + "properties": { + "refName": { + "type": "string", + "description": "Name of the referenced function" + }, + "arguments": { + "type": "object", + "description": "Function arguments/inputs" + }, + "selectionSet": { + "type": "string", + "description": "Only used if function type is 'graphql'. A string containing a valid GraphQL selection set" + }, + "invoke": { + "type": "string", + "enum": [ + "sync", + "async" + ], + "description": "Specifies if the function should be invoked sync or async", + "default": "sync" + } + }, + "additionalProperties": false, + "required": [ + "refName" + ] + } + ] + }, + "eventref": { + "type": "object", + "description": "Event References", + "properties": { + "triggerEventRef": { + "type": "string", + "description": "Reference to the unique name of a 'produced' event definition" + }, + "resultEventRef": { + "type": "string", + "description": "Reference to the unique name of a 'consumed' event definition" + }, + "resultEventTimeout": { + "type": "string", + "description": "Maximum amount of time (ISO 8601 format) to wait for the result event. If not defined it should default to the actionExecutionTimeout" + }, + "data": { + "type": [ + "string", + "object" + ], + "description": "If string type, an expression which selects parts of the states data output to become the data (payload) of the event referenced by 'triggerEventRef'. If object type, a custom object to become the data (payload) of the event referenced by 'triggerEventRef'." + }, + "contextAttributes": { + "type": "object", + "description": "Add additional extension context attributes to the produced event", + "additionalProperties": { + "type": "string" + } + }, + "invoke": { + "type": "string", + "enum": [ + "sync", + "async" + ], + "description": "Specifies if the function should be invoked sync or async. Default is sync.", + "default": "sync" + } + }, + "additionalProperties": false, + "required": [ + "triggerEventRef", + "resultEventRef" + ] + }, + "subflowref": { + "oneOf": [ + { + "type": "string", + "description": "Unique id of the sub-workflow to be invoked", + "minLength": 1 + }, + { + "type": "object", + "description": "Specifies a sub-workflow to be invoked", + "properties": { + "workflowId": { + "type": "string", + "description": "Unique id of the sub-workflow to be invoked" + }, + "version": { + "type": "string", + "description": "Version of the sub-workflow to be invoked", + "minLength": 1 + }, + "onParentComplete": { + "type": "string", + "enum": [ + "continue", + "terminate" + ], + "description": "If invoke is 'async', specifies how subflow execution should behave when parent workflow completes. Default is 'terminate'", + "default": "terminate" + }, + "invoke": { + "type": "string", + "enum": [ + "sync", + "async" + ], + "description": "Specifies if the subflow should be invoked sync or async", + "default": "sync" + } + }, + "required": [ + "workflowId" + ] + } + ] + }, + "branch": { + "type": "object", + "description": "Branch Definition", + "properties": { + "name": { + "type": "string", + "description": "Branch name" + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "actionExecTimeout": { + "$ref": "timeouts.json#/definitions/actionExecTimeout" + }, + "branchExecTimeout": { + "$ref": "timeouts.json#/definitions/branchExecTimeout" + } + }, + "required": [] + }, + "actions": { + "type": "array", + "description": "Actions to be executed in this branch", + "items": { + "type": "object", + "$ref": "#/definitions/action" + }, + "additionalItems": false + } + }, + "additionalProperties": false, + "required": [ + "name", + "actions" + ] + }, + "sleepstate": { + "type": "object", + "description": "Causes the workflow execution to sleep for a specified duration", + "properties": { + "id": { + "type": "string", + "description": "Unique State id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "sleep", + "description": "State type" + }, + "end": { + "$ref": "#/definitions/end", + "description": "State end definition" + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "duration": { + "type": "string", + "description": "Duration (ISO 8601 duration format) to sleep" + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + } + }, + "required": [] + }, + "onErrors": { + "type": "array", + "description": "States error handling definitions", + "items": { + "type": "object", + "$ref": "#/definitions/error" + }, + "additionalItems": false + }, + "transition": { + "description": "Next transition of the workflow after the workflow sleep", + "$ref": "#/definitions/transition" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "usedForCompensation": { + "type": "boolean", + "default": false, + "description": "If true, this state is used to compensate another state. Default is false" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "if": { + "properties": { + "usedForCompensation": { + "const": true + } + }, + "required": [ + "usedForCompensation" + ] + }, + "then": { + "required": [ + "name", + "type", + "duration" + ] + }, + "else": { + "oneOf": [ + { + "required": [ + "name", + "type", + "duration", + "end" + ] + }, + { + "required": [ + "name", + "type", + "duration", + "transition" + ] + } + ] + } + }, + "eventstate": { + "type": "object", + "description": "This state is used to wait for events from event sources, then consumes them and invoke one or more actions to run in sequence or parallel", + "properties": { + "id": { + "type": "string", + "description": "Unique State id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "event", + "description": "State type" + }, + "exclusive": { + "type": "boolean", + "default": true, + "description": "If true consuming one of the defined events causes its associated actions to be performed. If false all of the defined events must be consumed in order for actions to be performed" + }, + "onEvents": { + "type": "array", + "description": "Define the events to be consumed and optional actions to be performed", + "items": { + "type": "object", + "$ref": "#/definitions/onevents" + }, + "additionalItems": false + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + }, + "actionExecTimeout": { + "$ref": "timeouts.json#/definitions/actionExecTimeout" + }, + "eventTimeout": { + "$ref": "timeouts.json#/definitions/eventTimeout" + } + }, + "required": [] + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "onErrors": { + "type": "array", + "description": "States error handling definitions", + "items": { + "type": "object", + "$ref": "#/definitions/error" + }, + "additionalItems": false + }, + "transition": { + "description": "Next transition of the workflow after all the actions have been performed", + "$ref": "#/definitions/transition" + }, + "end": { + "$ref": "#/definitions/end", + "description": "State end definition" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "oneOf": [ + { + "required": [ + "name", + "type", + "onEvents", + "end" + ] + }, + { + "required": [ + "name", + "type", + "onEvents", + "transition" + ] + } + ] + }, + "operationstate": { + "type": "object", + "description": "Defines actions be performed. Does not wait for incoming events", + "properties": { + "id": { + "type": "string", + "description": "Unique State id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "operation", + "description": "State type" + }, + "end": { + "$ref": "#/definitions/end", + "description": "State end definition" + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "actionMode": { + "type": "string", + "enum": [ + "sequential", + "parallel" + ], + "description": "Specifies whether actions are performed in sequence or in parallel", + "default": "sequential" + }, + "actions": { + "type": "array", + "description": "Actions to be performed", + "items": { + "type": "object", + "$ref": "#/definitions/action" + } + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + }, + "actionExecTimeout": { + "$ref": "timeouts.json#/definitions/actionExecTimeout" + } + }, + "required": [] + }, + "onErrors": { + "type": "array", + "description": "States error handling definitions", + "items": { + "type": "object", + "$ref": "#/definitions/error" + }, + "additionalItems": false + }, + "transition": { + "description": "Next transition of the workflow after all the actions have been performed", + "$ref": "#/definitions/transition" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "usedForCompensation": { + "type": "boolean", + "default": false, + "description": "If true, this state is used to compensate another state. Default is false" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "if": { + "properties": { + "usedForCompensation": { + "const": true + } + }, + "required": [ + "usedForCompensation" + ] + }, + "then": { + "required": [ + "name", + "type", + "actions" + ] + }, + "else": { + "oneOf": [ + { + "required": [ + "name", + "type", + "actions", + "end" + ] + }, + { + "required": [ + "name", + "type", + "actions", + "transition" + ] + } + ] + } + }, + "parallelstate": { + "type": "object", + "description": "Consists of a number of states that are executed in parallel", + "properties": { + "id": { + "type": "string", + "description": "Unique State id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "parallel", + "description": "State type" + }, + "end": { + "$ref": "#/definitions/end", + "description": "State end definition" + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + }, + "branchExecTimeout": { + "$ref": "timeouts.json#/definitions/branchExecTimeout" + } + }, + "required": [] + }, + "branches": { + "type": "array", + "description": "Branch Definitions", + "items": { + "type": "object", + "$ref": "#/definitions/branch" + }, + "additionalItems": false + }, + "completionType": { + "type": "string", + "enum": [ + "allOf", + "atLeast" + ], + "description": "Option types on how to complete branch execution.", + "default": "allOf" + }, + "numCompleted": { + "type": [ + "number", + "string" + ], + "minimum": 0, + "minLength": 0, + "description": "Used when completionType is set to 'atLeast' to specify the minimum number of branches that must complete before the state will transition." + }, + "onErrors": { + "type": "array", + "description": "States error handling definitions", + "items": { + "type": "object", + "$ref": "#/definitions/error" + }, + "additionalItems": false + }, + "transition": { + "description": "Next transition of the workflow after all branches have completed execution", + "$ref": "#/definitions/transition" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "usedForCompensation": { + "type": "boolean", + "default": false, + "description": "If true, this state is used to compensate another state. Default is false" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "if": { + "properties": { + "usedForCompensation": { + "const": true + } + }, + "required": [ + "usedForCompensation" + ] + }, + "then": { + "required": [ + "name", + "type", + "branches" + ] + }, + "else": { + "oneOf": [ + { + "required": [ + "name", + "type", + "branches", + "end" + ] + }, + { + "required": [ + "name", + "type", + "branches", + "transition" + ] + } + ] + } + }, + "switchstate": { + "oneOf": [ + { + "$ref": "#/definitions/databasedswitchstate" + }, + { + "$ref": "#/definitions/eventbasedswitchstate" + } + ] + }, + "eventbasedswitchstate": { + "type": "object", + "description": "Permits transitions to other states based on events", + "properties": { + "id": { + "type": "string", + "description": "Unique State id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "switch", + "description": "State type" + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + }, + "eventTimeout": { + "$ref": "timeouts.json#/definitions/eventTimeout" + } + }, + "required": [] + }, + "eventConditions": { + "type": "array", + "description": "Defines conditions evaluated against events", + "items": { + "type": "object", + "$ref": "#/definitions/eventcondition" + }, + "additionalItems": false + }, + "onErrors": { + "type": "array", + "description": "States error handling definitions", + "items": { + "type": "object", + "$ref": "#/definitions/error" + }, + "additionalItems": false + }, + "defaultCondition": { + "description": "Default transition of the workflow if there is no matching data conditions. Can include a transition or end definition", + "$ref": "#/definitions/defaultconditiondef" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "usedForCompensation": { + "type": "boolean", + "default": false, + "description": "If true, this state is used to compensate another state. Default is false" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "required": [ + "name", + "type", + "eventConditions", + "defaultCondition" + ] + }, + "databasedswitchstate": { + "type": "object", + "description": "Permits transitions to other states based on data conditions", + "properties": { + "id": { + "type": "string", + "description": "Unique State id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "switch", + "description": "State type" + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + } + }, + "required": [] + }, + "dataConditions": { + "type": "array", + "description": "Defines conditions evaluated against state data", + "items": { + "type": "object", + "$ref": "#/definitions/datacondition" + }, + "additionalItems": false + }, + "onErrors": { + "type": "array", + "description": "States error handling definitions", + "items": { + "type": "object", + "$ref": "#/definitions/error" + }, + "additionalItems": false + }, + "defaultCondition": { + "description": "Default transition of the workflow if there is no matching data conditions. Can include a transition or end definition", + "$ref": "#/definitions/defaultconditiondef" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "usedForCompensation": { + "type": "boolean", + "default": false, + "description": "If true, this state is used to compensate another state. Default is false" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "required": [ + "name", + "type", + "dataConditions", + "defaultCondition" + ] + }, + "defaultconditiondef": { + "type": "object", + "description": "DefaultCondition definition. Can be either a transition or end definition", + "properties": { + "transition": { + "$ref": "#/definitions/transition" + }, + "end": { + "$ref": "#/definitions/end" + } + }, + "additionalProperties": false, + "oneOf": [ + { + "required": [ + "transition" + ] + }, + { + "required": [ + "end" + ] + } + ] + }, + "eventcondition": { + "oneOf": [ + { + "$ref": "#/definitions/transitioneventcondition" + }, + { + "$ref": "#/definitions/enddeventcondition" + } + ] + }, + "transitioneventcondition": { + "type": "object", + "description": "Switch state data event condition", + "properties": { + "name": { + "type": "string", + "description": "Event condition name" + }, + "eventRef": { + "type": "string", + "description": "References an unique event name in the defined workflow events" + }, + "transition": { + "description": "Next transition of the workflow if there is valid matches", + "$ref": "#/definitions/transition" + }, + "eventDataFilter": { + "description": "Event data filter definition", + "$ref": "#/definitions/eventdatafilter" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "required": [ + "eventRef", + "transition" + ] + }, + "enddeventcondition": { + "type": "object", + "description": "Switch state data event condition", + "properties": { + "name": { + "type": "string", + "description": "Event condition name" + }, + "eventRef": { + "type": "string", + "description": "References an unique event name in the defined workflow events" + }, + "end": { + "$ref": "#/definitions/end", + "description": "Explicit transition to end" + }, + "eventDataFilter": { + "description": "Event data filter definition", + "$ref": "#/definitions/eventdatafilter" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "required": [ + "eventRef", + "end" + ] + }, + "datacondition": { + "oneOf": [ + { + "$ref": "#/definitions/transitiondatacondition" + }, + { + "$ref": "#/definitions/enddatacondition" + } + ] + }, + "transitiondatacondition": { + "type": "object", + "description": "Switch state data based condition", + "properties": { + "name": { + "type": "string", + "description": "Data condition name" + }, + "condition": { + "type": "string", + "description": "Workflow expression evaluated against state data. Must evaluate to true or false" + }, + "transition": { + "description": "Workflow transition if condition is evaluated to true", + "$ref": "#/definitions/transition" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "required": [ + "condition", + "transition" + ] + }, + "enddatacondition": { + "type": "object", + "description": "Switch state data based condition", + "properties": { + "name": { + "type": "string", + "description": "Data condition name" + }, + "condition": { + "type": "string", + "description": "Workflow expression evaluated against state data. Must evaluate to true or false" + }, + "end": { + "$ref": "#/definitions/end", + "description": "Workflow end definition" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "required": [ + "condition", + "end" + ] + }, + "injectstate": { + "type": "object", + "description": "Inject static data into state data. Does not perform any actions", + "properties": { + "id": { + "type": "string", + "description": "Unique state id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "inject", + "description": "State type" + }, + "end": { + "$ref": "#/definitions/end", + "description": "State end definition" + }, + "data": { + "type": "object", + "description": "JSON object which can be set as states data input and can be manipulated via filters" + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + } + }, + "required": [] + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "transition": { + "description": "Next transition of the workflow after injection has completed", + "$ref": "#/definitions/transition" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "usedForCompensation": { + "type": "boolean", + "default": false, + "description": "If true, this state is used to compensate another state. Default is false" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "if": { + "properties": { + "usedForCompensation": { + "const": true + } + }, + "required": [ + "usedForCompensation" + ] + }, + "then": { + "required": [ + "name", + "type", + "data" + ] + }, + "else": { + "oneOf": [ + { + "required": [ + "name", + "type", + "data", + "end" + ] + }, + { + "required": [ + "name", + "type", + "data", + "transition" + ] + } + ] + } + }, + "foreachstate": { + "type": "object", + "description": "Execute a set of defined actions or workflows for each element of a data array", + "properties": { + "id": { + "type": "string", + "description": "Unique State id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "foreach", + "description": "State type" + }, + "end": { + "$ref": "#/definitions/end", + "description": "State end definition" + }, + "inputCollection": { + "type": "string", + "description": "Workflow expression selecting an array element of the states data" + }, + "outputCollection": { + "type": "string", + "description": "Workflow expression specifying an array element of the states data to add the results of each iteration" + }, + "iterationParam": { + "type": "string", + "description": "Name of the iteration parameter that can be referenced in actions/workflow. For each parallel iteration, this param should contain an unique element of the inputCollection array" + }, + "batchSize": { + "type": [ + "number", + "string" + ], + "minimum": 0, + "minLength": 0, + "description": "Specifies how many iterations may run in parallel at the same time. Used if 'mode' property is set to 'parallel' (default)" + }, + "actions": { + "type": "array", + "description": "Actions to be executed for each of the elements of inputCollection", + "items": { + "type": "object", + "$ref": "#/definitions/action" + }, + "additionalItems": false + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + }, + "actionExecTimeout": { + "$ref": "timeouts.json#/definitions/actionExecTimeout" + } + }, + "required": [] + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "onErrors": { + "type": "array", + "description": "States error handling definitions", + "items": { + "type": "object", + "$ref": "#/definitions/error" + }, + "additionalItems": false + }, + "transition": { + "description": "Next transition of the workflow after state has completed", + "$ref": "#/definitions/transition" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "usedForCompensation": { + "type": "boolean", + "default": false, + "description": "If true, this state is used to compensate another state. Default is false" + }, + "mode": { + "type": "string", + "enum": [ + "sequential", + "parallel" + ], + "description": "Specifies how iterations are to be performed (sequentially or in parallel)", + "default": "parallel" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "if": { + "properties": { + "usedForCompensation": { + "const": true + } + }, + "required": [ + "usedForCompensation" + ] + }, + "then": { + "required": [ + "name", + "type", + "inputCollection", + "actions" + ] + }, + "else": { + "oneOf": [ + { + "required": [ + "name", + "type", + "inputCollection", + "actions", + "end" + ] + }, + { + "required": [ + "name", + "type", + "inputCollection", + "actions", + "transition" + ] + } + ] + } + }, + "callbackstate": { + "type": "object", + "description": "This state performs an action, then waits for the callback event that denotes completion of the action", + "properties": { + "id": { + "type": "string", + "description": "Unique state id", + "minLength": 1 + }, + "name": { + "type": "string", + "description": "State name" + }, + "type": { + "type": "string", + "const": "callback", + "description": "State type" + }, + "action": { + "description": "Defines the action to be executed", + "$ref": "#/definitions/action" + }, + "eventRef": { + "type": "string", + "description": "References an unique callback event name in the defined workflow events" + }, + "timeouts": { + "type": "object", + "description": "State specific timeouts", + "properties": { + "stateExecTimeout": { + "$ref": "timeouts.json#/definitions/stateExecTimeout" + }, + "actionExecTimeout": { + "$ref": "timeouts.json#/definitions/actionExecTimeout" + }, + "eventTimeout": { + "$ref": "timeouts.json#/definitions/eventTimeout" + } + }, + "required": [] + }, + "eventDataFilter": { + "description": "Event data filter", + "$ref": "#/definitions/eventdatafilter" + }, + "stateDataFilter": { + "description": "State data filter", + "$ref": "#/definitions/statedatafilter" + }, + "onErrors": { + "type": "array", + "description": "States error handling definitions", + "items": { + "type": "object", + "$ref": "#/definitions/error" + }, + "additionalItems": false + }, + "transition": { + "description": "Next transition of the workflow after all the actions have been performed", + "$ref": "#/definitions/transition" + }, + "end": { + "$ref": "#/definitions/end", + "description": "State end definition" + }, + "compensatedBy": { + "type": "string", + "minLength": 1, + "description": "Unique Name of a workflow state which is responsible for compensation of this state" + }, + "usedForCompensation": { + "type": "boolean", + "default": false, + "description": "If true, this state is used to compensate another state. Default is false" + }, + "metadata": { + "$ref": "common.json#/definitions/metadata" + } + }, + "additionalProperties": false, + "if": { + "properties": { + "usedForCompensation": { + "const": true + } + }, + "required": [ + "usedForCompensation" + ] + }, + "then": { + "required": [ + "name", + "type", + "action", + "eventRef" + ] + }, + "else": { + "oneOf": [ + { + "required": [ + "name", + "type", + "action", + "eventRef", + "end" + ] + }, + { + "required": [ + "name", + "type", + "action", + "eventRef", + "transition" + ] + } + ] + } + }, + "startdef": { + "oneOf": [ + { + "type": "string", + "description": "Name of the starting workflow state", + "minLength": 1 + }, + { + "type": "object", + "description": "Workflow start definition", + "properties": { + "stateName": { + "type": "string", + "description": "Name of the starting workflow state", + "minLength": 1 + }, + "schedule": { + "description": "Define the time/repeating intervals or cron at which workflow instances should be automatically started.", + "$ref": "#/definitions/schedule" + } + }, + "additionalProperties": false, + "required": [ + "stateName", + "schedule" + ] + } + ] + }, + "schedule": { + "oneOf": [ + { + "type": "string", + "description": "Time interval (must be repeating interval) described with ISO 8601 format. Declares when workflow instances will be automatically created. (UTC timezone is assumed)", + "minLength": 1 + }, + { + "type": "object", + "description": "Start state schedule definition", + "properties": { + "interval": { + "type": "string", + "description": "Time interval (must be repeating interval) described with ISO 8601 format. Declares when workflow instances will be automatically created.", + "minLength": 1 + }, + "cron": { + "$ref": "#/definitions/crondef" + }, + "timezone": { + "type": "string", + "description": "Timezone name used to evaluate the interval & cron-expression. (default: UTC)" + } + }, + "additionalProperties": false, + "oneOf": [ + { + "required": [ + "interval" + ] + }, + { + "required": [ + "cron" + ] + } + ] + } + ] + }, + "end": { + "oneOf": [ + { + "type": "boolean", + "description": "State end definition", + "default": true + }, + { + "type": "object", + "description": "State end definition", + "properties": { + "terminate": { + "type": "boolean", + "default": false, + "description": "If true, completes all execution flows in the given workflow instance" + }, + "produceEvents": { + "type": "array", + "description": "Defines events that should be produced", + "items": { + "type": "object", + "$ref": "#/definitions/produceeventdef" + }, + "additionalItems": false + }, + "compensate": { + "type": "boolean", + "default": false, + "description": "If set to true, triggers workflow compensation. Default is false" + }, + "continueAs": { + "$ref": "#/definitions/continueasdef" + } + }, + "additionalProperties": false, + "required": [] + } + ] + }, + "produceeventdef": { + "type": "object", + "description": "Produce an event and set its data", + "properties": { + "eventRef": { + "type": "string", + "description": "References a name of a defined event" + }, + "data": { + "type": [ + "string", + "object" + ], + "description": "If String, expression which selects parts of the states data output to become the data of the produced event. If object a custom object to become the data of produced event." + }, + "contextAttributes": { + "type": "object", + "description": "Add additional event extension context attributes", + "additionalProperties": { + "type": "string" + } + } + }, + "additionalProperties": false, + "required": [ + "eventRef" + ] + }, + "statedatafilter": { + "type": "object", + "properties": { + "input": { + "type": "string", + "description": "Workflow expression to filter the state data input" + }, + "output": { + "type": "string", + "description": "Workflow expression that filters the state data output" + } + }, + "additionalProperties": false, + "required": [] + }, + "eventdatafilter": { + "type": "object", + "properties": { + "useData": { + "type": "boolean", + "description": "If set to false, event payload is not added/merged to state data. In this case 'data' and 'toStateData' should be ignored. Default is true.", + "default": true + }, + "data": { + "type": "string", + "description": "Workflow expression that filters the received event payload (default: '${ . }')" + }, + "toStateData": { + "type": "string", + "description": " Workflow expression that selects a state data element to which the filtered event should be added/merged into. If not specified, denotes, the top-level state data element." + } + }, + "additionalProperties": false, + "required": [] + }, + "actiondatafilter": { + "type": "object", + "properties": { + "fromStateData": { + "type": "string", + "description": "Workflow expression that selects state data that the state action can use" + }, + "useResults": { + "type": "boolean", + "description": "If set to false, action data results are not added/merged to state data. In this case 'results' and 'toStateData' should be ignored. Default is true.", + "default": true + }, + "results": { + "type": "string", + "description": "Workflow expression that filters the actions data results" + }, + "toStateData": { + "type": "string", + "description": "Workflow expression that selects a state data element to which the action results should be added/merged into. If not specified, denote, the top-level state data element" + } + }, + "additionalProperties": false, + "required": [] + } + } +} \ No newline at end of file diff --git a/serverlessworkflow_sdk/operation_state.py b/serverlessworkflow_sdk/operation_state.py new file mode 100644 index 0000000..7892c82 --- /dev/null +++ b/serverlessworkflow_sdk/operation_state.py @@ -0,0 +1,55 @@ +from serverlessworkflow_sdk.action import Action +from serverlessworkflow_sdk.state import State + + +class OperationState(State): + + name: str + type: str + actions: [Action] + end: bool + dataConditions: object #TODO + defaultCondition: object #TODO + + def __init__(self, name: str = None, + type: str = "operation", + actions: [Action] = None, + end: bool = None, + dataConditions: object = None, + defaultCondition: object = None, + **kwargs): + # duplicated + for local in list(locals()): + if local in ["self", "kwargs"]: + continue + value = locals().get(local) + if not value: + continue + if value == "true": + value = True + # duplicated + + if local == 'actions': + value = OperationState.load_actions(value) + + + self.__setattr__(local.replace("_", ""), value) + + # duplicated + for k in kwargs.keys(): + value = kwargs[k] + if value == "true": + value = True + + if k == 'actions': + value = OperationState.load_actions(value) + + self.__setattr__(k.replace("_", ""), value) + # duplicated + + + + + @staticmethod + def load_actions(value): + return [Action(**action) for action in value] diff --git a/serverlessworkflow_sdk/state.py b/serverlessworkflow_sdk/state.py new file mode 100644 index 0000000..c030f71 --- /dev/null +++ b/serverlessworkflow_sdk/state.py @@ -0,0 +1,26 @@ +class State: + type: str + + def __init__(self, data: dict = None, + **kwargs): + # duplicated + for local in list(locals()): + if local in ["self", "kwargs"]: + continue + value = locals().get(local) + if not value: + continue + if value == "true": + value = True + # duplicated + + self.__setattr__(local.replace("_", ""), value) + + # duplicated + for k in kwargs.keys(): + value = kwargs[k] + if value == "true": + value = True + + self.__setattr__(k.replace("_", ""), value) + # duplicated diff --git a/serverlessworkflow_sdk/workflow.py b/serverlessworkflow_sdk/workflow.py new file mode 100644 index 0000000..6e22924 --- /dev/null +++ b/serverlessworkflow_sdk/workflow.py @@ -0,0 +1,106 @@ +import json + +import yaml + +from serverlessworkflow_sdk.inject_state import InjectState +from serverlessworkflow_sdk.operation_state import OperationState +from serverlessworkflow_sdk.state import State + + +def is_inject_state(state: State): + return state['type'] == 'inject' + + +def is_operation_state(state: State): + return state['type'] == 'operation' + + +class Workflow: + id: str + key: str + name: str + version: str + description: str + specVersion: str + start: str + states: [State] + functions: [] + + def __init__(self, + id_: str = None, + key: str = None, + name: str = None, + version: str = None, + description: str = None, + specVersion: str = None, + start: str = None, + states: [State] = None, + functions: [] = None, + **kwargs): + + # duplicated + for local in list(locals()): + if local in ["self", "kwargs"]: + continue + value = locals().get(local) + if not value: + continue + if value == "true": + value = True + # duplicated + + if local == 'states': + value = Workflow.load_states(value) + + self.__setattr__(local.replace("_", ""), value) + + # duplicated + for k in kwargs.keys(): + value = kwargs[k] + if value == "true": + value = True + + self.__setattr__(k.replace("_", ""), value) + # duplicated + + def to_json(self) -> str: + return json.dumps(self, + default=lambda o: o.__dict__, + indent=4) + + def to_yaml(self): + def noop(self_, *args, **kw): + pass + + yaml.emitter.Emitter.process_tag = noop + return yaml.dump(self, + sort_keys=False, + # , default_flow_style=False, + allow_unicode=True, + ) + + @classmethod + def from_source(cls, source: str): + try: + loaded_data = yaml.safe_load(source) + loaded_data["id_"] = loaded_data["id"] + del loaded_data["id"] + return cls(**loaded_data) + except Exception: + raise Exception("Format not supported") + + @staticmethod + def load_states(states: [State]): + result = [] + for state in states: + if is_inject_state(state): + result.append(InjectState(**(states[0]))) + elif is_operation_state(state): + result.append(OperationState(**(states[0]))) + else: + result.append(State(**(states[0]))) + + return result + + def __repr__(self): + return "{!r}".format(self.__dict__) diff --git a/serverlessworkflow_sdk/workflow_validator.py b/serverlessworkflow_sdk/workflow_validator.py new file mode 100644 index 0000000..0a9da88 --- /dev/null +++ b/serverlessworkflow_sdk/workflow_validator.py @@ -0,0 +1,26 @@ +import json +import os + +from jsonschema.validators import validate + +from serverlessworkflow_sdk.workflow import Workflow + + +class WorkflowValidator: + workflow: Workflow + json_schema_content: object = None + + def __init__(self, workflow: Workflow): + self.workflow = workflow + + if not self.json_schema_content: + file_json_schema = os.path.join(os.path.dirname(__file__), 'jsonschemas', 'workflow.json') + with open(file_json_schema, "r") as json_schema: + self.json_schema_content = json.load(json_schema) + + def validate(self): + workflow = json.loads(json.dumps(self.workflow, default=lambda o: o.__dict__)) + validate(workflow, self.json_schema_content) + + def __repr__(self): + return "{!r}".format(self.__dict__) diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..4497ced --- /dev/null +++ b/setup.py @@ -0,0 +1,13 @@ +from setuptools import find_packages, setup +setup( + name='serverlessworkflow_sdk', + packages=find_packages(include=['serverlessworkflow_sdk']), + version='0.1.0', + description='Serverless Workflow Specification - Python SDK', + author='Serverless Workflow Contributors', + license='http://www.apache.org/licenses/LICENSE-2.0.txt', + install_requires=[], + setup_requires=['pytest-runner'], + tests_require=['pytest'], + test_suite='tests', +) \ No newline at end of file diff --git a/tests/__init__.py b/tests/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/tests/examples/applicantrequest.json b/tests/examples/applicantrequest.json new file mode 100644 index 0000000..8a05887 --- /dev/null +++ b/tests/examples/applicantrequest.json @@ -0,0 +1,59 @@ +{ + "id": "applicantrequest", + "name": "Applicant Request Decision Workflow", + "version": "1.0", + "description": "Determine if applicant request is valid", + "specVersion": "0.8", + "start": "CheckApplication", + "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 + } + ], + "functions": [ + { + "name": "sendRejectionEmailFunction", + "operation": "http://myapis.org/applicationapi.json#emailRejection" + } + ] +} \ No newline at end of file diff --git a/tests/examples/booklending.json b/tests/examples/booklending.json new file mode 100644 index 0000000..cc640fc --- /dev/null +++ b/tests/examples/booklending.json @@ -0,0 +1,138 @@ +{ + "id": "booklending", + "name": "Book Lending Workflow", + "version": "1.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": "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" +} diff --git a/tests/examples/carauctionbids.json b/tests/examples/carauctionbids.json new file mode 100644 index 0000000..2588031 --- /dev/null +++ b/tests/examples/carauctionbids.json @@ -0,0 +1,49 @@ +{ + "id": "handleCarAuctionBid", + "name": "Car Auction Bidding Workflow", + "version": "1.0", + "description": "Store a single bid whole the car auction is active", + "specVersion": "0.8", + "start": { + "stateName": "StoreCarAuctionBid", + "schedule": "R/PT2H" + }, + "states": [ + { + "name": "StoreCarAuctionBid", + "type": "event", + "exclusive": true, + "onEvents": [ + { + "eventRefs": [ + "CarBidEvent" + ], + "actions": [ + { + "functionRef": { + "refName": "StoreBidFunction", + "arguments": { + "bid": "${ .bid }" + } + } + } + ] + } + ], + "end": true + } + ], + "functions": [ + { + "name": "StoreBidFunction", + "operation": "http://myapis.org/carauctionapi.json#storeBid" + } + ], + "events": [ + { + "name": "CarBidEvent", + "type": "carBidMadeType", + "source": "carBidEventSource" + } + ] +} \ No newline at end of file diff --git a/tests/examples/checkcarvitals.json b/tests/examples/checkcarvitals.json new file mode 100644 index 0000000..af6ecd3 --- /dev/null +++ b/tests/examples/checkcarvitals.json @@ -0,0 +1,60 @@ +{ + "id": "checkcarvitals", + "name": "Check Car Vitals Workflow", + "version": "1.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" + } + ] +} diff --git a/tests/examples/jobmonitoring.json b/tests/examples/jobmonitoring.json new file mode 100644 index 0000000..815b711 --- /dev/null +++ b/tests/examples/jobmonitoring.json @@ -0,0 +1,127 @@ +{ + "id": "jobmonitoring", + "name": "Job Monitoring", + "version": "1.0", + "description": "Monitor finished execution of a submitted job", + "specVersion": "0.8", + "start": "SubmitJob", + "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 + } + ], + "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" + } + ] +} \ No newline at end of file diff --git a/tests/examples/parallel.json b/tests/examples/parallel.json new file mode 100644 index 0000000..6282297 --- /dev/null +++ b/tests/examples/parallel.json @@ -0,0 +1,34 @@ +{ + "id": "parallelexec", + "name": "Parallel Execution Workflow", + "version": "1.0", + "description": "Executes two branches in parallel", + "specVersion": "0.8", + "start": "ParallelExec", + "states": [ + { + "name": "ParallelExec", + "type": "parallel", + "completionType": "allOf", + "branches": [ + { + "name": "ShortDelayBranch", + "actions": [ + { + "subFlowRef": "shortdelayworkflowid" + } + ] + }, + { + "name": "LongDelayBranch", + "actions": [ + { + "subFlowRef": "longdelayworkflowid" + } + ] + } + ], + "end": true + } + ] +} \ No newline at end of file diff --git a/tests/examples/provisionorder.json b/tests/examples/provisionorder.json new file mode 100644 index 0000000..2ef8d23 --- /dev/null +++ b/tests/examples/provisionorder.json @@ -0,0 +1,100 @@ +{ + "id": "provisionorders", + "name": "Provision Orders", + "version": "1.0", + "description": "Provision Orders and handle errors thrown", + "specVersion": "0.8", + "start": "ProvisionOrder", + "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 + } + ], + "functions": [ + { + "name": "provisionOrderFunction", + "operation": "http://myapis.org/provisioningapi.json#doProvision" + } + ], + "errors": [ + { + "name": "Missing order id" + }, + { + "name": "Missing order item" + }, + { + "name": "Missing order quantity" + } + ] +} \ No newline at end of file diff --git a/tests/examples/sendcloudevent.json b/tests/examples/sendcloudevent.json new file mode 100644 index 0000000..11f5ac8 --- /dev/null +++ b/tests/examples/sendcloudevent.json @@ -0,0 +1,47 @@ +{ + "id": "sendcloudeventonprovision", + "name": "Send CloudEvent on provision completion", + "version": "1.0", + "specVersion": "0.8", + "start": "ProvisionOrdersState", + "states": [ + { + "name": "ProvisionOrdersState", + "type": "foreach", + "inputCollection": "${ .orders }", + "iterationParam": "singleorder", + "outputCollection": "${ .provisionedOrders }", + "actions": [ + { + "functionRef": { + "refName": "provisionOrderFunction", + "arguments": { + "order": "${ .singleorder }" + } + } + } + ], + "end": { + "produceEvents": [ + { + "eventRef": "provisioningCompleteEvent", + "data": "${ .provisionedOrders }" + } + ] + } + } + ], + "functions": [ + { + "name": "provisionOrderFunction", + "operation": "http://myapis.org/provisioning.json#doProvision" + } + ], + "events": [ + { + "kind": "produced", + "name": "provisioningCompleteEvent", + "type": "provisionCompleteType" + } + ] +} \ No newline at end of file diff --git a/tests/examples/solvemathproblems.json b/tests/examples/solvemathproblems.json new file mode 100644 index 0000000..d525922 --- /dev/null +++ b/tests/examples/solvemathproblems.json @@ -0,0 +1,37 @@ +{ + "id": "solvemathproblems", + "name": "Solve Math Problems Workflow", + "version": "1.0", + "description": "Solve math problems", + "specVersion": "0.8", + "start": "Solve", + "states": [ + { + "name": "Solve", + "type": "foreach", + "inputCollection": "${ .expressions }", + "iterationParam": "singleexpression", + "outputCollection": "${ .results }", + "actions": [ + { + "functionRef": { + "refName": "solveMathExpressionFunction", + "arguments": { + "expression": "${ .singleexpression }" + } + } + } + ], + "stateDataFilter": { + "output": "${ .results }" + }, + "end": true + } + ], + "functions": [ + { + "name": "solveMathExpressionFunction", + "operation": "http://myapis.org/mapthapis.json#solveExpression" + } + ] +} \ No newline at end of file diff --git a/tests/test_workflow.json b/tests/test_workflow.json new file mode 100644 index 0000000..f946930 --- /dev/null +++ b/tests/test_workflow.json @@ -0,0 +1,34 @@ +{ + "id": "greeting", + "name": "Greeting Workflow", + "version": "1.0", + "description": "Greet Someone", + "specVersion": "0.8", + "start": "Greet", + "states": [ + { + "name": "Greet", + "type": "operation", + "actions": [ + { + "functionRef": { + "refName": "greetingFunction", + "arguments": { + "name": "${ .person.name }" + } + }, + "actionDataFilter": { + "results": "${ .greeting }" + } + } + ], + "end": true + } + ], + "functions": [ + { + "name": "greetingFunction", + "operation": "file://myapis/greetingapis.json#greeting" + } + ] +} \ No newline at end of file diff --git a/tests/test_workflow.py b/tests/test_workflow.py new file mode 100644 index 0000000..d95c670 --- /dev/null +++ b/tests/test_workflow.py @@ -0,0 +1,124 @@ +import os +import unittest + +from serverlessworkflow_sdk.action import Action +from serverlessworkflow_sdk.workflow import Workflow + + +class TestWorkflow(unittest.TestCase): + + workflow = Workflow(id_="greeting", + name="Greeting Workflow", + description="Greet Someone", + version='1.0', + specVersion='0.8', + start="Greet", + states=[ + { + "name": "Greet", + "type": "operation", + "actions": [ + { + "functionRef": { + "refName": "greetingFunction", + "arguments": { + "name": "${ .person.name }" + } + }, + "actionDataFilter": { + "results": "${ .greeting }" + } + } + ], + "end": True + } + ], + functions=[ + { + "name": "greetingFunction", + "operation": "file://myapis/greetingapis.json#greeting" + } + ] + ) + + def test_workflow_to_json(self): + expected = """{ + "id": "greeting", + "name": "Greeting Workflow", + "version": "1.0", + "description": "Greet Someone", + "specVersion": "0.8", + "start": "Greet", + "states": [ + { + "name": "Greet", + "type": "operation", + "actions": [ + { + "functionRef": { + "refName": "greetingFunction", + "arguments": { + "name": "${ .person.name }" + } + }, + "actionDataFilter": { + "results": "${ .greeting }" + } + } + ], + "end": true + } + ], + "functions": [ + { + "name": "greetingFunction", + "operation": "file://myapis/greetingapis.json#greeting" + } + ] +}""" + + self.assertEqual(expected, self.workflow.to_json()) + + def test_workflow_to_yaml(self): + expected = """id: greeting +name: Greeting Workflow +version: '1.0' +description: Greet Someone +specVersion: '0.8' +start: Greet +states: +- name: Greet + type: operation + actions: + - functionRef: + refName: greetingFunction + arguments: + name: ${ .person.name } + actionDataFilter: + results: ${ .greeting } + end: true +functions: +- name: greetingFunction + operation: file://myapis/greetingapis.json#greeting +""" + self.assertEqual(expected, self.workflow.to_yaml()) + + def test_workflow_from_source_json(self): + wf_file = os.path.join(os.path.dirname(__file__), 'test_workflow.json') + self.assert_test_workflow_file(wf_file) + + def test_workflow_from_source_yaml(self): + wf_file = os.path.join(os.path.dirname(__file__), 'test_workflow.yaml') + self.assert_test_workflow_file(wf_file) + + def assert_test_workflow_file(self, wf_file): + with open(wf_file, "r") as swf_file: + swf_content = swf_file.read() + + workflow = Workflow.from_source(swf_content) + + self.assertEqual("greeting", workflow.id) + self.assertEqual("operation", workflow.states[0].type) + self.assertEqual(True, workflow.states[0].end) + self.assertTrue(isinstance(workflow.states[0].actions[0], Action)) + diff --git a/tests/test_workflow.yaml b/tests/test_workflow.yaml new file mode 100644 index 0000000..b2f1e6e --- /dev/null +++ b/tests/test_workflow.yaml @@ -0,0 +1,20 @@ +id: greeting +name: Greeting Workflow +version: '1.0' +description: Greet Someone +specVersion: '0.8' +start: Greet +states: +- name: Greet + type: operation + actions: + - functionRef: + refName: greetingFunction + arguments: + name: ${ .person.name } + actionDataFilter: + results: ${ .greeting } + end: true +functions: +- name: greetingFunction + operation: file://myapis/greetingapis.json#greeting \ No newline at end of file diff --git a/tests/test_workflow_validator.py b/tests/test_workflow_validator.py new file mode 100644 index 0000000..166e9db --- /dev/null +++ b/tests/test_workflow_validator.py @@ -0,0 +1,34 @@ +import json +import os +import unittest +from os import listdir + +from jsonschema.exceptions import ValidationError + +from serverlessworkflow_sdk.workflow import Workflow +from serverlessworkflow_sdk.workflow_validator import WorkflowValidator + + +class TestWorkflowValidator(unittest.TestCase): + + def test_validate_examples(self): + examples_dir = os.path.join(os.path.dirname(__file__), 'examples') + examples = listdir(examples_dir) + self.assertEqual(len(examples), 9) + + for example in examples: + with open(examples_dir + "/" + example, "r") as swf_file: + swf_file_content = json.load(swf_file) + WorkflowValidator(Workflow(**swf_file_content)).validate() + + def test_invalid_wf(self): + wf_file = os.path.join(os.path.dirname(__file__), 'examples', 'applicantrequest.json') + + with open(wf_file, "r") as swf_file: + swf_content = swf_file.read() + + workflow = Workflow.from_source(swf_content) + workflow.specVersion = None + with self.assertRaises(ValidationError): + WorkflowValidator(Workflow(workflow)).validate() + pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy