diff --git a/README.md b/README.md index 7eb6ae6..e110090 100644 --- a/README.md +++ b/README.md @@ -2,10 +2,8 @@ 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 +* Programmatically build workflow definitions * Parse workflow JSON and YAML definitions * Validate workflow definitions @@ -17,26 +15,50 @@ With the SDK you can: - pipenv required `pip install pipenv` ``` -pipenv install +pipenv install --dev pipenv shell python setup.py pytest ``` -## **WIP** Programmatically build workflow definitions +## Programmatically build workflow definitions ``` -workflow = Workflow(id_="greeting", - name="Greeting Workflow", - description="Greet Someone", - version='1.0', - specVersion='0.8', - start="Greet", - states=[], - functions=[] -) + workflow = Workflow( + id_="greeting", + name="Greeting Workflow", + description="Greet Someone", + version='1.0', + specVersion='0.8', + start="Greet", + states=[ + OperationState( + name="Greet", + type="operation", + actions=[ + Action( + functionRef=FunctionRef( + refName="greetingFunction", + arguments={ + "name": "${ .person.name }" + } + ), + actionDataFilter=ActionDataFilter( + results="${ .greeting }" + ) + ) + ], + end=True + ) + ], + functions=[ + Function(name="greetingFunction", + operation="file://myapis/greetingapis.json#greeting") + ] + ) ``` +You can see a full example in the [test_workflow.py](tests/serverlessworkflow/sdk/test_workflow.py) file ## Parse workflow JSON and YAML definitions diff --git a/serverlessworkflow/sdk/databased_switch_state.py b/serverlessworkflow/sdk/databased_switch_state.py index 6030807..fe5eb4e 100644 --- a/serverlessworkflow/sdk/databased_switch_state.py +++ b/serverlessworkflow/sdk/databased_switch_state.py @@ -6,7 +6,7 @@ from serverlessworkflow.sdk.default_condition_def import DefaultConditionDef from serverlessworkflow.sdk.end_data_condition import EndDataCondition from serverlessworkflow.sdk.error import Error -from serverlessworkflow.sdk.hydration import HydratableParameter, UnionTypeOf, ComplexTypeOf, ArrayTypeOf, \ +from serverlessworkflow.sdk.hydration import HydratableParameter, ComplexTypeOf, ArrayTypeOf, \ Fields from serverlessworkflow.sdk.metadata import Metadata from serverlessworkflow.sdk.state import State @@ -52,7 +52,8 @@ def f_hydration(p_key, p_value): return HydratableParameter(value=p_value).hydrateAs(ComplexTypeOf(DataBasedSwitchStateTime0ut)) if p_key == 'dataConditions': - return [DataBasedSwitchState.hydrate_state(v) if type(v) is not (TransitionDataCondition or EndDataCondition) else v for v in p_value] + return [DataBasedSwitchState.hydrate_state(v) if not ( + isinstance(v, TransitionDataCondition or EndDataCondition)) else v for v in p_value] if p_key == 'onErrors': return HydratableParameter(value=p_value).hydrateAs(ArrayTypeOf(Error)) diff --git a/serverlessworkflow/sdk/event_based_switch_state.py b/serverlessworkflow/sdk/event_based_switch_state.py index 0dd3960..b54fb7a 100644 --- a/serverlessworkflow/sdk/event_based_switch_state.py +++ b/serverlessworkflow/sdk/event_based_switch_state.py @@ -6,7 +6,7 @@ from serverlessworkflow.sdk.end_event_condition import EndEventCondition from serverlessworkflow.sdk.error import Error from serverlessworkflow.sdk.event_based_switch_state_timeout import EventBasedSwitchStateTimeOut -from serverlessworkflow.sdk.hydration import HydratableParameter, ComplexTypeOf, UnionTypeOf, ArrayTypeOf, \ +from serverlessworkflow.sdk.hydration import HydratableParameter, ComplexTypeOf, ArrayTypeOf, \ Fields from serverlessworkflow.sdk.metadata import Metadata from serverlessworkflow.sdk.state import State @@ -52,7 +52,8 @@ def f_hydration(p_key, p_value): return HydratableParameter(value=p_value).hydrateAs(ComplexTypeOf(EventBasedSwitchStateTimeOut)) if p_key == 'eventConditions': - return [EventBasedSwitchState.hydrate_state(v) if type(v) is not (TransitionEventCondition or EndEventCondition) else v for v in p_value] + return [EventBasedSwitchState.hydrate_state(v) if not ( + isinstance(v, TransitionEventCondition or EndEventCondition)) else v for v in p_value] if p_key == 'onErrors': return HydratableParameter(value=p_value).hydrateAs(ArrayTypeOf(Error)) diff --git a/serverlessworkflow/sdk/function_ref.py b/serverlessworkflow/sdk/function_ref.py index 4295272..0666015 100644 --- a/serverlessworkflow/sdk/function_ref.py +++ b/serverlessworkflow/sdk/function_ref.py @@ -11,7 +11,7 @@ class FunctionRef: def __init__(self, refName: str = None, - arguments: dict[str, dict] = None, + arguments: dict[str, any] = None, selectionSet: str = None, invoke: str = None, **kwargs): diff --git a/serverlessworkflow/sdk/hydration.py b/serverlessworkflow/sdk/hydration.py index 8bb27cb..32bed72 100644 --- a/serverlessworkflow/sdk/hydration.py +++ b/serverlessworkflow/sdk/hydration.py @@ -1,10 +1,12 @@ from __future__ import annotations import dataclasses +from abc import ABC, abstractmethod from typing import Any -class HydratableType: +class HydratableType(ABC): + @abstractmethod def hydrate(self, value): pass diff --git a/serverlessworkflow/sdk/inject_state.py b/serverlessworkflow/sdk/inject_state.py index 52c937b..858e55d 100644 --- a/serverlessworkflow/sdk/inject_state.py +++ b/serverlessworkflow/sdk/inject_state.py @@ -44,8 +44,8 @@ def __init__(self, def f_hydration(p_key, p_value): if p_key == 'end': - return HydratableParameter(value=p_value).hydrateAs(UnionTypeOf(SimpleTypeOf(bool), - ComplexTypeOf(End))) + return HydratableParameter(value=p_value).hydrateAs(UnionTypeOf([SimpleTypeOf(bool), + ComplexTypeOf(End)])) if p_key == 'data': return HydratableParameter(value=p_value).hydrateAs(UnionTypeOf([SimpleTypeOf(str), ComplexTypeOf(dict)])) diff --git a/serverlessworkflow/sdk/workflow.py b/serverlessworkflow/sdk/workflow.py index e074469..e4c1da6 100644 --- a/serverlessworkflow/sdk/workflow.py +++ b/serverlessworkflow/sdk/workflow.py @@ -142,7 +142,7 @@ def f_hydration(p_key, p_value): ArrayTypeOf(AuthDef)])) if p_key == 'states': - return [Workflow.hydrate_state(v) if type(v) is not State else v for v in p_value] + return [Workflow.hydrate_state(v) if not isinstance(v,State) else v for v in p_value] if p_key == 'functions': return HydratableParameter(value=p_value).hydrateAs(UnionTypeOf([SimpleTypeOf(str), diff --git a/tests/examples/helloworld.json b/tests/examples/helloworld.json new file mode 100644 index 0000000..619af07 --- /dev/null +++ b/tests/examples/helloworld.json @@ -0,0 +1,18 @@ +{ + "id": "helloworld", + "version": "1.0", + "specVersion": "0.8", + "name": "Hello World Workflow", + "description": "Inject Hello World", + "start": "Hello State", + "states": [ + { + "name": "Hello State", + "type": "inject", + "data": { + "result": "Hello World!" + }, + "end": true + } + ] +} \ No newline at end of file diff --git a/tests/serverlessworkflow/sdk/test_event_based_switch_state.py b/tests/serverlessworkflow/sdk/test_event_based_switch_state.py new file mode 100644 index 0000000..0bf41ae --- /dev/null +++ b/tests/serverlessworkflow/sdk/test_event_based_switch_state.py @@ -0,0 +1,16 @@ +import unittest + +from serverlessworkflow.sdk.event_based_switch_state import EventBasedSwitchState +from serverlessworkflow.sdk.transition_event_condition import TransitionEventCondition + + +class TestEventBasedSwitchState(unittest.TestCase): + def test_programmatically_create_object(self): + event_based_switch_state = EventBasedSwitchState(eventConditions=[TransitionEventCondition( + name="Hold Book", + eventRef="Hold Book Event", + transition="Request Hold" + + )]) + + self.assertTrue(isinstance(event_based_switch_state.eventConditions[0], TransitionEventCondition)) diff --git a/tests/serverlessworkflow/sdk/test_workflow.py b/tests/serverlessworkflow/sdk/test_workflow.py index 9cd5d61..0e956fb 100644 --- a/tests/serverlessworkflow/sdk/test_workflow.py +++ b/tests/serverlessworkflow/sdk/test_workflow.py @@ -1,47 +1,49 @@ +import json import os import unittest +from os import listdir from serverlessworkflow.sdk.action import Action +from serverlessworkflow.sdk.action_data_filter import ActionDataFilter from serverlessworkflow.sdk.function import Function from serverlessworkflow.sdk.function_ref import FunctionRef +from serverlessworkflow.sdk.operation_state import OperationState 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" + workflow = Workflow( + id_="greeting", + name="Greeting Workflow", + description="Greet Someone", + version='1.0', + specVersion='0.8', + start="Greet", + states=[ + OperationState( + name="Greet", + type="operation", + actions=[ + Action( + functionRef=FunctionRef( + refName="greetingFunction", + arguments={ + "name": "${ .person.name }" } - ] + ), + actionDataFilter=ActionDataFilter( + results="${ .greeting }" ) + ) + ], + end=True + ) + ], + functions=[ + Function(name="greetingFunction", + operation="file://myapis/greetingapis.json#greeting") + ] + ) def test_workflow_to_json(self): expected = """{ @@ -105,9 +107,37 @@ def test_workflow_to_yaml(self): """ self.assertEqual(expected, self.workflow.to_yaml()) + def test_programmatically_create_workflow(self): + + self.assertEqual("greeting", self.workflow.id) + self.assertEqual("operation", self.workflow.states[0].type) + self.assertTrue(isinstance(self.workflow.states[0], OperationState)) + self.assertEqual(True, self.workflow.states[0].end) + self.assertTrue(isinstance(self.workflow.states[0].actions[0], Action)) + self.assertTrue(isinstance(self.workflow.states[0].actions[0].functionRef, FunctionRef)) + self.assertTrue(isinstance(self.workflow.functions[0], Function)) + 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) + examples_dir = os.path.join(os.path.dirname(__file__), '../../examples') + examples = listdir(examples_dir) + self.assertEqual(len(examples), 10) + + for example in examples: + with self.subTest(f"test_{example}"): + with open(examples_dir + "/" + example, "r") as swf_file: + workflow = Workflow.from_source(swf_file) + self.assertTrue(isinstance(workflow, Workflow)) + + def test_instance_workflow_class(self): + examples_dir = os.path.join(os.path.dirname(__file__), '../../examples') + examples = listdir(examples_dir) + self.assertEqual(len(examples), 10) + + for example in examples: + with self.subTest(f"test_{example}"): + with open(examples_dir + "/" + example, "r") as swf_file: + workflow = Workflow(**json.load(swf_file)) + self.assertTrue(isinstance(workflow, Workflow)) def test_workflow_from_source_yaml(self): wf_file = os.path.join(os.path.dirname(__file__), 'test_workflow.yaml') @@ -125,4 +155,3 @@ def assert_test_workflow_file(self, wf_file): self.assertTrue(isinstance(workflow.states[0].actions[0], Action)) self.assertTrue(isinstance(workflow.states[0].actions[0].functionRef, FunctionRef)) self.assertTrue(isinstance(workflow.functions[0], Function)) - diff --git a/tests/serverlessworkflow/sdk/test_workflow_validator.py b/tests/serverlessworkflow/sdk/test_workflow_validator.py index 8965af6..30f54b7 100644 --- a/tests/serverlessworkflow/sdk/test_workflow_validator.py +++ b/tests/serverlessworkflow/sdk/test_workflow_validator.py @@ -14,11 +14,10 @@ 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) + self.assertEqual(len(examples), 10) for example in examples: with self.subTest(f"test_{example}"): - with open(examples_dir + "/" + example, "r") as swf_file: swf_file_content = json.load(swf_file) workflow = Workflow(**swf_file_content) 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