Skip to content

Commit 08666ea

Browse files
authored
Merge pull request #13 from antmendoza/fix-inject-state-hydration
fix-inject-state-hydration
2 parents bec19d9 + 1963b6c commit 08666ea

File tree

11 files changed

+148
-60
lines changed

11 files changed

+148
-60
lines changed

README.md

Lines changed: 36 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,8 @@
22

33
Provides the Python API/SPI for the [Serverless Workflow Specification](https://github.com/serverlessworkflow/specification)
44

5-
> This is a WIP implementation
6-
75
With the SDK you can:
8-
* **WIP** Programmatically build workflow definitions
6+
* Programmatically build workflow definitions
97
* Parse workflow JSON and YAML definitions
108
* Validate workflow definitions
119

@@ -17,26 +15,50 @@ With the SDK you can:
1715
- pipenv required `pip install pipenv`
1816

1917
```
20-
pipenv install
18+
pipenv install --dev
2119
2220
pipenv shell
2321
2422
python setup.py pytest
2523
```
2624

27-
## **WIP** Programmatically build workflow definitions
25+
## Programmatically build workflow definitions
2826

2927
```
30-
workflow = Workflow(id_="greeting",
31-
name="Greeting Workflow",
32-
description="Greet Someone",
33-
version='1.0',
34-
specVersion='0.8',
35-
start="Greet",
36-
states=[],
37-
functions=[]
38-
)
28+
workflow = Workflow(
29+
id_="greeting",
30+
name="Greeting Workflow",
31+
description="Greet Someone",
32+
version='1.0',
33+
specVersion='0.8',
34+
start="Greet",
35+
states=[
36+
OperationState(
37+
name="Greet",
38+
type="operation",
39+
actions=[
40+
Action(
41+
functionRef=FunctionRef(
42+
refName="greetingFunction",
43+
arguments={
44+
"name": "${ .person.name }"
45+
}
46+
),
47+
actionDataFilter=ActionDataFilter(
48+
results="${ .greeting }"
49+
)
50+
)
51+
],
52+
end=True
53+
)
54+
],
55+
functions=[
56+
Function(name="greetingFunction",
57+
operation="file://myapis/greetingapis.json#greeting")
58+
]
59+
)
3960
```
61+
You can see a full example in the [test_workflow.py](tests/serverlessworkflow/sdk/test_workflow.py) file
4062

4163
## Parse workflow JSON and YAML definitions
4264

serverlessworkflow/sdk/databased_switch_state.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from serverlessworkflow.sdk.default_condition_def import DefaultConditionDef
77
from serverlessworkflow.sdk.end_data_condition import EndDataCondition
88
from serverlessworkflow.sdk.error import Error
9-
from serverlessworkflow.sdk.hydration import HydratableParameter, UnionTypeOf, ComplexTypeOf, ArrayTypeOf, \
9+
from serverlessworkflow.sdk.hydration import HydratableParameter, ComplexTypeOf, ArrayTypeOf, \
1010
Fields
1111
from serverlessworkflow.sdk.metadata import Metadata
1212
from serverlessworkflow.sdk.state import State
@@ -52,7 +52,8 @@ def f_hydration(p_key, p_value):
5252
return HydratableParameter(value=p_value).hydrateAs(ComplexTypeOf(DataBasedSwitchStateTime0ut))
5353

5454
if p_key == 'dataConditions':
55-
return [DataBasedSwitchState.hydrate_state(v) if type(v) is not (TransitionDataCondition or EndDataCondition) else v for v in p_value]
55+
return [DataBasedSwitchState.hydrate_state(v) if not (
56+
isinstance(v, TransitionDataCondition or EndDataCondition)) else v for v in p_value]
5657

5758
if p_key == 'onErrors':
5859
return HydratableParameter(value=p_value).hydrateAs(ArrayTypeOf(Error))

serverlessworkflow/sdk/event_based_switch_state.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from serverlessworkflow.sdk.end_event_condition import EndEventCondition
77
from serverlessworkflow.sdk.error import Error
88
from serverlessworkflow.sdk.event_based_switch_state_timeout import EventBasedSwitchStateTimeOut
9-
from serverlessworkflow.sdk.hydration import HydratableParameter, ComplexTypeOf, UnionTypeOf, ArrayTypeOf, \
9+
from serverlessworkflow.sdk.hydration import HydratableParameter, ComplexTypeOf, ArrayTypeOf, \
1010
Fields
1111
from serverlessworkflow.sdk.metadata import Metadata
1212
from serverlessworkflow.sdk.state import State
@@ -52,7 +52,8 @@ def f_hydration(p_key, p_value):
5252
return HydratableParameter(value=p_value).hydrateAs(ComplexTypeOf(EventBasedSwitchStateTimeOut))
5353

5454
if p_key == 'eventConditions':
55-
return [EventBasedSwitchState.hydrate_state(v) if type(v) is not (TransitionEventCondition or EndEventCondition) else v for v in p_value]
55+
return [EventBasedSwitchState.hydrate_state(v) if not (
56+
isinstance(v, TransitionEventCondition or EndEventCondition)) else v for v in p_value]
5657

5758
if p_key == 'onErrors':
5859
return HydratableParameter(value=p_value).hydrateAs(ArrayTypeOf(Error))

serverlessworkflow/sdk/function_ref.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ class FunctionRef:
1111

1212
def __init__(self,
1313
refName: str = None,
14-
arguments: dict[str, dict] = None,
14+
arguments: dict[str, any] = None,
1515
selectionSet: str = None,
1616
invoke: str = None,
1717
**kwargs):

serverlessworkflow/sdk/hydration.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
from __future__ import annotations
22

33
import dataclasses
4+
from abc import ABC, abstractmethod
45
from typing import Any
56

67

7-
class HydratableType:
8+
class HydratableType(ABC):
9+
@abstractmethod
810
def hydrate(self, value):
911
pass
1012

serverlessworkflow/sdk/inject_state.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ def __init__(self,
4444
def f_hydration(p_key, p_value):
4545

4646
if p_key == 'end':
47-
return HydratableParameter(value=p_value).hydrateAs(UnionTypeOf(SimpleTypeOf(bool),
48-
ComplexTypeOf(End)))
47+
return HydratableParameter(value=p_value).hydrateAs(UnionTypeOf([SimpleTypeOf(bool),
48+
ComplexTypeOf(End)]))
4949
if p_key == 'data':
5050
return HydratableParameter(value=p_value).hydrateAs(UnionTypeOf([SimpleTypeOf(str),
5151
ComplexTypeOf(dict)]))

serverlessworkflow/sdk/workflow.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ def f_hydration(p_key, p_value):
142142
ArrayTypeOf(AuthDef)]))
143143

144144
if p_key == 'states':
145-
return [Workflow.hydrate_state(v) if type(v) is not State else v for v in p_value]
145+
return [Workflow.hydrate_state(v) if not isinstance(v,State) else v for v in p_value]
146146

147147
if p_key == 'functions':
148148
return HydratableParameter(value=p_value).hydrateAs(UnionTypeOf([SimpleTypeOf(str),

tests/examples/helloworld.json

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
{
2+
"id": "helloworld",
3+
"version": "1.0",
4+
"specVersion": "0.8",
5+
"name": "Hello World Workflow",
6+
"description": "Inject Hello World",
7+
"start": "Hello State",
8+
"states": [
9+
{
10+
"name": "Hello State",
11+
"type": "inject",
12+
"data": {
13+
"result": "Hello World!"
14+
},
15+
"end": true
16+
}
17+
]
18+
}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import unittest
2+
3+
from serverlessworkflow.sdk.event_based_switch_state import EventBasedSwitchState
4+
from serverlessworkflow.sdk.transition_event_condition import TransitionEventCondition
5+
6+
7+
class TestEventBasedSwitchState(unittest.TestCase):
8+
def test_programmatically_create_object(self):
9+
event_based_switch_state = EventBasedSwitchState(eventConditions=[TransitionEventCondition(
10+
name="Hold Book",
11+
eventRef="Hold Book Event",
12+
transition="Request Hold"
13+
14+
)])
15+
16+
self.assertTrue(isinstance(event_based_switch_state.eventConditions[0], TransitionEventCondition))

tests/serverlessworkflow/sdk/test_workflow.py

Lines changed: 64 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,49 @@
1+
import json
12
import os
23
import unittest
4+
from os import listdir
35

46
from serverlessworkflow.sdk.action import Action
7+
from serverlessworkflow.sdk.action_data_filter import ActionDataFilter
58
from serverlessworkflow.sdk.function import Function
69
from serverlessworkflow.sdk.function_ref import FunctionRef
10+
from serverlessworkflow.sdk.operation_state import OperationState
711
from serverlessworkflow.sdk.workflow import Workflow
812

913

1014
class TestWorkflow(unittest.TestCase):
11-
12-
workflow = Workflow(id_="greeting",
13-
name="Greeting Workflow",
14-
description="Greet Someone",
15-
version='1.0',
16-
specVersion='0.8',
17-
start="Greet",
18-
states=[
19-
{
20-
"name": "Greet",
21-
"type": "operation",
22-
"actions": [
23-
{
24-
"functionRef": {
25-
"refName": "greetingFunction",
26-
"arguments": {
27-
"name": "${ .person.name }"
28-
}
29-
},
30-
"actionDataFilter": {
31-
"results": "${ .greeting }"
32-
}
33-
}
34-
],
35-
"end": True
36-
}
37-
],
38-
functions=[
39-
{
40-
"name": "greetingFunction",
41-
"operation": "file://myapis/greetingapis.json#greeting"
15+
workflow = Workflow(
16+
id_="greeting",
17+
name="Greeting Workflow",
18+
description="Greet Someone",
19+
version='1.0',
20+
specVersion='0.8',
21+
start="Greet",
22+
states=[
23+
OperationState(
24+
name="Greet",
25+
type="operation",
26+
actions=[
27+
Action(
28+
functionRef=FunctionRef(
29+
refName="greetingFunction",
30+
arguments={
31+
"name": "${ .person.name }"
4232
}
43-
]
33+
),
34+
actionDataFilter=ActionDataFilter(
35+
results="${ .greeting }"
4436
)
37+
)
38+
],
39+
end=True
40+
)
41+
],
42+
functions=[
43+
Function(name="greetingFunction",
44+
operation="file://myapis/greetingapis.json#greeting")
45+
]
46+
)
4547

4648
def test_workflow_to_json(self):
4749
expected = """{
@@ -105,9 +107,37 @@ def test_workflow_to_yaml(self):
105107
"""
106108
self.assertEqual(expected, self.workflow.to_yaml())
107109

110+
def test_programmatically_create_workflow(self):
111+
112+
self.assertEqual("greeting", self.workflow.id)
113+
self.assertEqual("operation", self.workflow.states[0].type)
114+
self.assertTrue(isinstance(self.workflow.states[0], OperationState))
115+
self.assertEqual(True, self.workflow.states[0].end)
116+
self.assertTrue(isinstance(self.workflow.states[0].actions[0], Action))
117+
self.assertTrue(isinstance(self.workflow.states[0].actions[0].functionRef, FunctionRef))
118+
self.assertTrue(isinstance(self.workflow.functions[0], Function))
119+
108120
def test_workflow_from_source_json(self):
109-
wf_file = os.path.join(os.path.dirname(__file__), 'test_workflow.json')
110-
self.assert_test_workflow_file(wf_file)
121+
examples_dir = os.path.join(os.path.dirname(__file__), '../../examples')
122+
examples = listdir(examples_dir)
123+
self.assertEqual(len(examples), 10)
124+
125+
for example in examples:
126+
with self.subTest(f"test_{example}"):
127+
with open(examples_dir + "/" + example, "r") as swf_file:
128+
workflow = Workflow.from_source(swf_file)
129+
self.assertTrue(isinstance(workflow, Workflow))
130+
131+
def test_instance_workflow_class(self):
132+
examples_dir = os.path.join(os.path.dirname(__file__), '../../examples')
133+
examples = listdir(examples_dir)
134+
self.assertEqual(len(examples), 10)
135+
136+
for example in examples:
137+
with self.subTest(f"test_{example}"):
138+
with open(examples_dir + "/" + example, "r") as swf_file:
139+
workflow = Workflow(**json.load(swf_file))
140+
self.assertTrue(isinstance(workflow, Workflow))
111141

112142
def test_workflow_from_source_yaml(self):
113143
wf_file = os.path.join(os.path.dirname(__file__), 'test_workflow.yaml')
@@ -125,4 +155,3 @@ def assert_test_workflow_file(self, wf_file):
125155
self.assertTrue(isinstance(workflow.states[0].actions[0], Action))
126156
self.assertTrue(isinstance(workflow.states[0].actions[0].functionRef, FunctionRef))
127157
self.assertTrue(isinstance(workflow.functions[0], Function))
128-

0 commit comments

Comments
 (0)
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