azure-functions-durable 1.2.8__py3-none-any.whl → 1.2.10__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- azure/durable_functions/__init__.py +81 -81
- azure/durable_functions/constants.py +9 -9
- azure/durable_functions/decorators/__init__.py +3 -3
- azure/durable_functions/decorators/durable_app.py +249 -249
- azure/durable_functions/decorators/metadata.py +109 -109
- azure/durable_functions/entity.py +125 -125
- azure/durable_functions/models/DurableEntityContext.py +201 -201
- azure/durable_functions/models/DurableHttpRequest.py +58 -58
- azure/durable_functions/models/DurableOrchestrationBindings.py +66 -66
- azure/durable_functions/models/DurableOrchestrationClient.py +781 -711
- azure/durable_functions/models/DurableOrchestrationContext.py +722 -707
- azure/durable_functions/models/DurableOrchestrationStatus.py +156 -156
- azure/durable_functions/models/EntityStateResponse.py +23 -23
- azure/durable_functions/models/FunctionContext.py +7 -7
- azure/durable_functions/models/OrchestrationRuntimeStatus.py +32 -29
- azure/durable_functions/models/OrchestratorState.py +117 -116
- azure/durable_functions/models/PurgeHistoryResult.py +33 -33
- azure/durable_functions/models/ReplaySchema.py +8 -8
- azure/durable_functions/models/RetryOptions.py +69 -69
- azure/durable_functions/models/RpcManagementOptions.py +86 -86
- azure/durable_functions/models/Task.py +426 -426
- azure/durable_functions/models/TaskOrchestrationExecutor.py +346 -333
- azure/durable_functions/models/TokenSource.py +56 -56
- azure/durable_functions/models/__init__.py +24 -24
- azure/durable_functions/models/actions/Action.py +23 -23
- azure/durable_functions/models/actions/ActionType.py +18 -18
- azure/durable_functions/models/actions/CallActivityAction.py +41 -41
- azure/durable_functions/models/actions/CallActivityWithRetryAction.py +45 -45
- azure/durable_functions/models/actions/CallEntityAction.py +46 -46
- azure/durable_functions/models/actions/CallHttpAction.py +35 -35
- azure/durable_functions/models/actions/CallSubOrchestratorAction.py +40 -40
- azure/durable_functions/models/actions/CallSubOrchestratorWithRetryAction.py +44 -44
- azure/durable_functions/models/actions/CompoundAction.py +35 -35
- azure/durable_functions/models/actions/ContinueAsNewAction.py +36 -36
- azure/durable_functions/models/actions/CreateTimerAction.py +48 -48
- azure/durable_functions/models/actions/NoOpAction.py +35 -35
- azure/durable_functions/models/actions/SignalEntityAction.py +47 -47
- azure/durable_functions/models/actions/WaitForExternalEventAction.py +63 -63
- azure/durable_functions/models/actions/WhenAllAction.py +14 -14
- azure/durable_functions/models/actions/WhenAnyAction.py +14 -14
- azure/durable_functions/models/actions/__init__.py +24 -24
- azure/durable_functions/models/entities/EntityState.py +74 -74
- azure/durable_functions/models/entities/OperationResult.py +76 -76
- azure/durable_functions/models/entities/RequestMessage.py +53 -53
- azure/durable_functions/models/entities/ResponseMessage.py +48 -48
- azure/durable_functions/models/entities/Signal.py +62 -62
- azure/durable_functions/models/entities/__init__.py +17 -17
- azure/durable_functions/models/history/HistoryEvent.py +92 -92
- azure/durable_functions/models/history/HistoryEventType.py +27 -25
- azure/durable_functions/models/history/__init__.py +8 -8
- azure/durable_functions/models/utils/__init__.py +7 -7
- azure/durable_functions/models/utils/entity_utils.py +103 -91
- azure/durable_functions/models/utils/http_utils.py +69 -69
- azure/durable_functions/models/utils/json_utils.py +56 -56
- azure/durable_functions/orchestrator.py +71 -71
- {azure_functions_durable-1.2.8.dist-info → azure_functions_durable-1.2.10.dist-info}/LICENSE +21 -21
- {azure_functions_durable-1.2.8.dist-info → azure_functions_durable-1.2.10.dist-info}/METADATA +58 -58
- azure_functions_durable-1.2.10.dist-info/RECORD +100 -0
- {azure_functions_durable-1.2.8.dist-info → azure_functions_durable-1.2.10.dist-info}/WHEEL +1 -1
- tests/models/test_DecoratorMetadata.py +135 -135
- tests/models/test_Decorators.py +107 -107
- tests/models/test_DurableOrchestrationBindings.py +68 -56
- tests/models/test_DurableOrchestrationClient.py +730 -612
- tests/models/test_DurableOrchestrationContext.py +102 -102
- tests/models/test_DurableOrchestrationStatus.py +59 -59
- tests/models/test_OrchestrationState.py +28 -28
- tests/models/test_RpcManagementOptions.py +79 -79
- tests/models/test_TokenSource.py +10 -10
- tests/orchestrator/models/OrchestrationInstance.py +18 -18
- tests/orchestrator/orchestrator_test_utils.py +130 -130
- tests/orchestrator/schemas/OrchetrationStateSchema.py +66 -66
- tests/orchestrator/test_call_http.py +235 -176
- tests/orchestrator/test_continue_as_new.py +67 -67
- tests/orchestrator/test_create_timer.py +126 -126
- tests/orchestrator/test_entity.py +395 -395
- tests/orchestrator/test_external_event.py +53 -53
- tests/orchestrator/test_fan_out_fan_in.py +175 -175
- tests/orchestrator/test_is_replaying_flag.py +101 -101
- tests/orchestrator/test_retries.py +308 -308
- tests/orchestrator/test_sequential_orchestrator.py +841 -801
- tests/orchestrator/test_sequential_orchestrator_custom_status.py +119 -119
- tests/orchestrator/test_sequential_orchestrator_with_retry.py +465 -465
- tests/orchestrator/test_serialization.py +30 -30
- tests/orchestrator/test_sub_orchestrator.py +95 -95
- tests/orchestrator/test_sub_orchestrator_with_retry.py +129 -129
- tests/orchestrator/test_task_any.py +60 -60
- tests/tasks/tasks_test_utils.py +17 -17
- tests/tasks/test_new_uuid.py +34 -34
- tests/test_utils/ContextBuilder.py +174 -174
- tests/test_utils/EntityContextBuilder.py +56 -56
- tests/test_utils/constants.py +1 -1
- tests/test_utils/json_utils.py +30 -30
- tests/test_utils/testClasses.py +56 -56
- tests/utils/__init__.py +1 -0
- tests/utils/test_entity_utils.py +24 -0
- azure_functions_durable-1.2.8.data/data/_manifest/bsi.json +0 -1
- azure_functions_durable-1.2.8.data/data/_manifest/manifest.cat +0 -0
- azure_functions_durable-1.2.8.data/data/_manifest/manifest.spdx.json +0 -12845
- azure_functions_durable-1.2.8.data/data/_manifest/manifest.spdx.json.sha256 +0 -1
- azure_functions_durable-1.2.8.dist-info/RECORD +0 -102
- {azure_functions_durable-1.2.8.dist-info → azure_functions_durable-1.2.10.dist-info}/top_level.txt +0 -0
tests/tasks/tasks_test_utils.py
CHANGED
|
@@ -1,17 +1,17 @@
|
|
|
1
|
-
def assert_tasks_equal(task1, task2):
|
|
2
|
-
assert task1.is_completed == task2.is_completed
|
|
3
|
-
assert task1.is_faulted == task2.is_faulted
|
|
4
|
-
assert task1.result == task2.result
|
|
5
|
-
assert task1.timestamp == task2.timestamp
|
|
6
|
-
assert task1.id == task2.id
|
|
7
|
-
assert task1.action == task2.action
|
|
8
|
-
assert str(task1.exception) == str(task2.exception)
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
def assert_taskset_equal(taskset1, taskset2):
|
|
12
|
-
assert taskset1.is_completed == taskset2.is_completed
|
|
13
|
-
assert taskset1.is_faulted == taskset2.is_faulted
|
|
14
|
-
assert taskset1.result == taskset2.result
|
|
15
|
-
assert taskset1.actions == taskset2.actions
|
|
16
|
-
assert taskset1.timestamp == taskset2.timestamp
|
|
17
|
-
assert str(taskset1.exception) == str(taskset2.exception)
|
|
1
|
+
def assert_tasks_equal(task1, task2):
|
|
2
|
+
assert task1.is_completed == task2.is_completed
|
|
3
|
+
assert task1.is_faulted == task2.is_faulted
|
|
4
|
+
assert task1.result == task2.result
|
|
5
|
+
assert task1.timestamp == task2.timestamp
|
|
6
|
+
assert task1.id == task2.id
|
|
7
|
+
assert task1.action == task2.action
|
|
8
|
+
assert str(task1.exception) == str(task2.exception)
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def assert_taskset_equal(taskset1, taskset2):
|
|
12
|
+
assert taskset1.is_completed == taskset2.is_completed
|
|
13
|
+
assert taskset1.is_faulted == taskset2.is_faulted
|
|
14
|
+
assert taskset1.result == taskset2.result
|
|
15
|
+
assert taskset1.actions == taskset2.actions
|
|
16
|
+
assert taskset1.timestamp == taskset2.timestamp
|
|
17
|
+
assert str(taskset1.exception) == str(taskset2.exception)
|
tests/tasks/test_new_uuid.py
CHANGED
|
@@ -1,35 +1,35 @@
|
|
|
1
|
-
from uuid import uuid1
|
|
2
|
-
from typing import List, Any, Dict
|
|
3
|
-
from azure.durable_functions.models.DurableOrchestrationContext import DurableOrchestrationContext
|
|
4
|
-
from azure.durable_functions.constants import DATETIME_STRING_FORMAT
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
def history_list() -> List[Dict[Any, Any]]:
|
|
8
|
-
history = [{'EventType': 12, 'EventId': -1, 'IsPlayed': False,
|
|
9
|
-
'Timestamp': '2019-12-08T23:18:41.3240927Z'}, {
|
|
10
|
-
'OrchestrationInstance': {'InstanceId': '48d0f95957504c2fa579e810a390b938',
|
|
11
|
-
'ExecutionId': 'fd183ee02e4b4fd18c95b773cfb5452b'},
|
|
12
|
-
'EventType': 0, 'ParentInstance': None, 'Name': 'DurableOrchestratorTrigger',
|
|
13
|
-
'Version': '', 'Input': 'null', 'Tags': None, 'EventId': -1, 'IsPlayed': False,
|
|
14
|
-
'Timestamp': '2019-12-08T23:18:39.756132Z'}]
|
|
15
|
-
return history
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
def test_new_uuid():
|
|
19
|
-
instance_id = str(uuid1())
|
|
20
|
-
history = history_list()
|
|
21
|
-
context1 = DurableOrchestrationContext(history, instance_id, False, None)
|
|
22
|
-
|
|
23
|
-
result1a = context1.new_uuid()
|
|
24
|
-
result1b = context1.new_uuid()
|
|
25
|
-
|
|
26
|
-
context2 = DurableOrchestrationContext(history, instance_id, False, None)
|
|
27
|
-
|
|
28
|
-
result2a = context2.new_uuid()
|
|
29
|
-
result2b = context2.new_uuid()
|
|
30
|
-
|
|
31
|
-
assert result1a == result2a
|
|
32
|
-
assert result1b == result2b
|
|
33
|
-
|
|
34
|
-
assert result1a != result1b
|
|
1
|
+
from uuid import uuid1
|
|
2
|
+
from typing import List, Any, Dict
|
|
3
|
+
from azure.durable_functions.models.DurableOrchestrationContext import DurableOrchestrationContext
|
|
4
|
+
from azure.durable_functions.constants import DATETIME_STRING_FORMAT
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
def history_list() -> List[Dict[Any, Any]]:
|
|
8
|
+
history = [{'EventType': 12, 'EventId': -1, 'IsPlayed': False,
|
|
9
|
+
'Timestamp': '2019-12-08T23:18:41.3240927Z'}, {
|
|
10
|
+
'OrchestrationInstance': {'InstanceId': '48d0f95957504c2fa579e810a390b938',
|
|
11
|
+
'ExecutionId': 'fd183ee02e4b4fd18c95b773cfb5452b'},
|
|
12
|
+
'EventType': 0, 'ParentInstance': None, 'Name': 'DurableOrchestratorTrigger',
|
|
13
|
+
'Version': '', 'Input': 'null', 'Tags': None, 'EventId': -1, 'IsPlayed': False,
|
|
14
|
+
'Timestamp': '2019-12-08T23:18:39.756132Z'}]
|
|
15
|
+
return history
|
|
16
|
+
|
|
17
|
+
|
|
18
|
+
def test_new_uuid():
|
|
19
|
+
instance_id = str(uuid1())
|
|
20
|
+
history = history_list()
|
|
21
|
+
context1 = DurableOrchestrationContext(history, instance_id, False, None)
|
|
22
|
+
|
|
23
|
+
result1a = context1.new_uuid()
|
|
24
|
+
result1b = context1.new_uuid()
|
|
25
|
+
|
|
26
|
+
context2 = DurableOrchestrationContext(history, instance_id, False, None)
|
|
27
|
+
|
|
28
|
+
result2a = context2.new_uuid()
|
|
29
|
+
result2b = context2.new_uuid()
|
|
30
|
+
|
|
31
|
+
assert result1a == result2a
|
|
32
|
+
assert result1b == result2b
|
|
33
|
+
|
|
34
|
+
assert result1a != result1b
|
|
35
35
|
assert result2a != result2b
|
|
@@ -1,174 +1,174 @@
|
|
|
1
|
-
from azure.durable_functions.models.ReplaySchema import ReplaySchema
|
|
2
|
-
import uuid
|
|
3
|
-
import json
|
|
4
|
-
from datetime import datetime, timedelta
|
|
5
|
-
from typing import List, Dict, Any, Optional
|
|
6
|
-
|
|
7
|
-
from .json_utils import add_attrib, convert_history_event_to_json_dict
|
|
8
|
-
from azure.durable_functions.constants import DATETIME_STRING_FORMAT
|
|
9
|
-
from tests.orchestrator.models.OrchestrationInstance \
|
|
10
|
-
import OrchestrationInstance
|
|
11
|
-
from azure.durable_functions.models.history.HistoryEvent import HistoryEvent
|
|
12
|
-
from azure.durable_functions.models.history.HistoryEventType \
|
|
13
|
-
import HistoryEventType
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
class ContextBuilder:
|
|
17
|
-
def __init__(self, name: str="", increase_time: bool = True, starting_time: Optional[datetime] = None, is_replaying=False, replay_schema: ReplaySchema = ReplaySchema.V1):
|
|
18
|
-
self.increase_time = increase_time
|
|
19
|
-
self.instance_id = uuid.uuid4()
|
|
20
|
-
self.is_replaying: bool = False
|
|
21
|
-
self.input_ = None
|
|
22
|
-
self.parent_instance_id = None
|
|
23
|
-
self.history_events: List[HistoryEvent] = []
|
|
24
|
-
|
|
25
|
-
if starting_time is None:
|
|
26
|
-
starting_time = datetime.now()
|
|
27
|
-
self.current_datetime: datetime = starting_time
|
|
28
|
-
self.upperSchemaVersion = replay_schema.value
|
|
29
|
-
|
|
30
|
-
self.add_orchestrator_started_event()
|
|
31
|
-
self.add_execution_started_event(name, is_played=is_replaying)
|
|
32
|
-
|
|
33
|
-
def get_base_event(
|
|
34
|
-
self, event_type: HistoryEventType, id_: int = -1,
|
|
35
|
-
is_played: bool = False, timestamp=None) -> HistoryEvent:
|
|
36
|
-
if self.increase_time:
|
|
37
|
-
self.current_datetime = self.current_datetime + timedelta(seconds=1)
|
|
38
|
-
if not timestamp:
|
|
39
|
-
timestamp = self.current_datetime
|
|
40
|
-
event = HistoryEvent(EventType=event_type, EventId=id_,
|
|
41
|
-
IsPlayed=is_played,
|
|
42
|
-
Timestamp=timestamp.strftime(DATETIME_STRING_FORMAT))
|
|
43
|
-
|
|
44
|
-
return event
|
|
45
|
-
|
|
46
|
-
def add_orchestrator_started_event(self):
|
|
47
|
-
event = self.get_base_event(HistoryEventType.ORCHESTRATOR_STARTED)
|
|
48
|
-
self.history_events.append(event)
|
|
49
|
-
|
|
50
|
-
def add_orchestrator_completed_event(self):
|
|
51
|
-
event = self.get_base_event(HistoryEventType.ORCHESTRATOR_COMPLETED)
|
|
52
|
-
self.history_events.append(event)
|
|
53
|
-
|
|
54
|
-
def add_sub_orchestrator_started_event(self, name: str, id_, input_=None):
|
|
55
|
-
event = self.get_base_event(HistoryEventType.SUB_ORCHESTRATION_INSTANCE_CREATED,
|
|
56
|
-
id_=id_)
|
|
57
|
-
event.Name = name
|
|
58
|
-
event.Input = input_
|
|
59
|
-
self.history_events.append(event)
|
|
60
|
-
|
|
61
|
-
def add_sub_orchestrator_completed_event(self, result, id_):
|
|
62
|
-
event = self.get_base_event(HistoryEventType.SUB_ORCHESTRATION_INSTANCE_COMPLETED)
|
|
63
|
-
event.Result = result
|
|
64
|
-
event.TaskScheduledId = id_
|
|
65
|
-
self.history_events.append(event)
|
|
66
|
-
|
|
67
|
-
def add_sub_orchestrator_failed_event(self, id_, reason, details):
|
|
68
|
-
event = self.get_base_event(HistoryEventType.SUB_ORCHESTRATION_INSTANCE_FAILED)
|
|
69
|
-
event.Reason = reason
|
|
70
|
-
event.Details = details
|
|
71
|
-
event.TaskScheduledId = id_
|
|
72
|
-
self.history_events.append(event)
|
|
73
|
-
|
|
74
|
-
def add_event_sent_event(self, instance_id, event_id):
|
|
75
|
-
event = self.get_base_event(HistoryEventType.EVENT_SENT)
|
|
76
|
-
event.InstanceId = instance_id
|
|
77
|
-
event._event_id = event_id
|
|
78
|
-
event.Name = "op"
|
|
79
|
-
event.Input = json.dumps({ "id": "0000" }) # usually provided by the extension
|
|
80
|
-
self.history_events.append(event)
|
|
81
|
-
|
|
82
|
-
def add_task_scheduled_event(
|
|
83
|
-
self, name: str, id_: int, version: str = '', input_=None):
|
|
84
|
-
event = self.get_base_event(HistoryEventType.TASK_SCHEDULED, id_=id_)
|
|
85
|
-
event.Name = name
|
|
86
|
-
event.Version = version
|
|
87
|
-
event.Input_ = input_
|
|
88
|
-
self.history_events.append(event)
|
|
89
|
-
|
|
90
|
-
def add_task_completed_event(self, id_: int, result, is_played=False):
|
|
91
|
-
event = self.get_base_event(HistoryEventType.TASK_COMPLETED, is_played=is_played)
|
|
92
|
-
event.Result = result
|
|
93
|
-
event.TaskScheduledId = id_
|
|
94
|
-
self.history_events.append(event)
|
|
95
|
-
|
|
96
|
-
def add_task_failed_event(self, id_: int, reason: str, details: str):
|
|
97
|
-
event = self.get_base_event(HistoryEventType.TASK_FAILED)
|
|
98
|
-
event.Reason = reason
|
|
99
|
-
event.Details = details
|
|
100
|
-
event.TaskScheduledId = id_
|
|
101
|
-
self.history_events.append(event)
|
|
102
|
-
|
|
103
|
-
def add_timer_created_event(self, id_: int, timestamp: str = None):
|
|
104
|
-
fire_at = self.current_datetime.strftime(DATETIME_STRING_FORMAT)
|
|
105
|
-
if timestamp is not None:
|
|
106
|
-
fire_at = timestamp
|
|
107
|
-
event = self.get_base_event(HistoryEventType.TIMER_CREATED, id_=id_)
|
|
108
|
-
event.FireAt = fire_at
|
|
109
|
-
self.history_events.append(event)
|
|
110
|
-
return fire_at
|
|
111
|
-
|
|
112
|
-
def add_timer_fired_event(self, id_: int, fire_at: str, is_played: bool = True):
|
|
113
|
-
event = self.get_base_event(HistoryEventType.TIMER_FIRED, is_played=is_played)
|
|
114
|
-
event.TimerId = id_
|
|
115
|
-
event.FireAt = fire_at
|
|
116
|
-
self.history_events.append(event)
|
|
117
|
-
|
|
118
|
-
def add_execution_started_event(
|
|
119
|
-
self, name: str, version: str = '', input_=None, is_played=True):
|
|
120
|
-
event = self.get_base_event(HistoryEventType.EXECUTION_STARTED, is_played=is_played)
|
|
121
|
-
event.orchestration_instance = OrchestrationInstance()
|
|
122
|
-
self.instance_id = event.orchestration_instance.instance_id
|
|
123
|
-
event.Name = name
|
|
124
|
-
event.Version = version
|
|
125
|
-
event.Input = input_
|
|
126
|
-
self.history_events.append(event)
|
|
127
|
-
|
|
128
|
-
def add_event_raised_event(self, name:str, id_: int, input_=None, timestamp=None, is_entity=False, is_error = False, literal_input=False):
|
|
129
|
-
event = self.get_base_event(HistoryEventType.EVENT_RAISED, id_=id_, timestamp=timestamp)
|
|
130
|
-
event.Name = name
|
|
131
|
-
if is_entity:
|
|
132
|
-
if is_error:
|
|
133
|
-
event.Input = json.dumps({ "result": json.dumps(input_), "exceptionType": "True" })
|
|
134
|
-
else:
|
|
135
|
-
if literal_input:
|
|
136
|
-
event.Input = json.dumps({ "result": input_ })
|
|
137
|
-
else:
|
|
138
|
-
event.Input = json.dumps({ "result": json.dumps(input_) })
|
|
139
|
-
else:
|
|
140
|
-
event.Input = input_
|
|
141
|
-
# event.timestamp = timestamp
|
|
142
|
-
self.history_events.append(event)
|
|
143
|
-
|
|
144
|
-
def to_json(self, **kwargs) -> Dict[str, Any]:
|
|
145
|
-
json_dict = {}
|
|
146
|
-
|
|
147
|
-
add_attrib(json_dict, self, 'instance_id', 'instanceId')
|
|
148
|
-
add_attrib(json_dict, self, 'parent_instance_id', 'parentInstanceId')
|
|
149
|
-
add_attrib(json_dict, self, 'is_replaying', 'isReplaying')
|
|
150
|
-
add_attrib(json_dict, self, 'input_', "input")
|
|
151
|
-
add_attrib(json_dict, self, 'upperSchemaVersion', "upperSchemaVersion")
|
|
152
|
-
|
|
153
|
-
history_list_as_dict = self.get_history_list_as_dict()
|
|
154
|
-
json_dict['history'] = history_list_as_dict
|
|
155
|
-
|
|
156
|
-
if kwargs is not None:
|
|
157
|
-
for key, value in kwargs.items():
|
|
158
|
-
json_dict[key] = value
|
|
159
|
-
|
|
160
|
-
return json_dict
|
|
161
|
-
|
|
162
|
-
def get_history_list_as_dict(self) -> List[Dict[str, Any]]:
|
|
163
|
-
history_list = []
|
|
164
|
-
|
|
165
|
-
for history_event in self.history_events:
|
|
166
|
-
event_as_dict = convert_history_event_to_json_dict(history_event)
|
|
167
|
-
history_list.append(event_as_dict)
|
|
168
|
-
|
|
169
|
-
return history_list
|
|
170
|
-
|
|
171
|
-
def to_json_string(self, **kwargs) -> str:
|
|
172
|
-
json_dict = self.to_json(**kwargs)
|
|
173
|
-
|
|
174
|
-
return json.dumps(json_dict)
|
|
1
|
+
from azure.durable_functions.models.ReplaySchema import ReplaySchema
|
|
2
|
+
import uuid
|
|
3
|
+
import json
|
|
4
|
+
from datetime import datetime, timedelta
|
|
5
|
+
from typing import List, Dict, Any, Optional
|
|
6
|
+
|
|
7
|
+
from .json_utils import add_attrib, convert_history_event_to_json_dict
|
|
8
|
+
from azure.durable_functions.constants import DATETIME_STRING_FORMAT
|
|
9
|
+
from tests.orchestrator.models.OrchestrationInstance \
|
|
10
|
+
import OrchestrationInstance
|
|
11
|
+
from azure.durable_functions.models.history.HistoryEvent import HistoryEvent
|
|
12
|
+
from azure.durable_functions.models.history.HistoryEventType \
|
|
13
|
+
import HistoryEventType
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
class ContextBuilder:
|
|
17
|
+
def __init__(self, name: str="", increase_time: bool = True, starting_time: Optional[datetime] = None, is_replaying=False, replay_schema: ReplaySchema = ReplaySchema.V1):
|
|
18
|
+
self.increase_time = increase_time
|
|
19
|
+
self.instance_id = uuid.uuid4()
|
|
20
|
+
self.is_replaying: bool = False
|
|
21
|
+
self.input_ = None
|
|
22
|
+
self.parent_instance_id = None
|
|
23
|
+
self.history_events: List[HistoryEvent] = []
|
|
24
|
+
|
|
25
|
+
if starting_time is None:
|
|
26
|
+
starting_time = datetime.now()
|
|
27
|
+
self.current_datetime: datetime = starting_time
|
|
28
|
+
self.upperSchemaVersion = replay_schema.value
|
|
29
|
+
|
|
30
|
+
self.add_orchestrator_started_event()
|
|
31
|
+
self.add_execution_started_event(name, is_played=is_replaying)
|
|
32
|
+
|
|
33
|
+
def get_base_event(
|
|
34
|
+
self, event_type: HistoryEventType, id_: int = -1,
|
|
35
|
+
is_played: bool = False, timestamp=None) -> HistoryEvent:
|
|
36
|
+
if self.increase_time:
|
|
37
|
+
self.current_datetime = self.current_datetime + timedelta(seconds=1)
|
|
38
|
+
if not timestamp:
|
|
39
|
+
timestamp = self.current_datetime
|
|
40
|
+
event = HistoryEvent(EventType=event_type, EventId=id_,
|
|
41
|
+
IsPlayed=is_played,
|
|
42
|
+
Timestamp=timestamp.strftime(DATETIME_STRING_FORMAT))
|
|
43
|
+
|
|
44
|
+
return event
|
|
45
|
+
|
|
46
|
+
def add_orchestrator_started_event(self):
|
|
47
|
+
event = self.get_base_event(HistoryEventType.ORCHESTRATOR_STARTED)
|
|
48
|
+
self.history_events.append(event)
|
|
49
|
+
|
|
50
|
+
def add_orchestrator_completed_event(self):
|
|
51
|
+
event = self.get_base_event(HistoryEventType.ORCHESTRATOR_COMPLETED)
|
|
52
|
+
self.history_events.append(event)
|
|
53
|
+
|
|
54
|
+
def add_sub_orchestrator_started_event(self, name: str, id_, input_=None):
|
|
55
|
+
event = self.get_base_event(HistoryEventType.SUB_ORCHESTRATION_INSTANCE_CREATED,
|
|
56
|
+
id_=id_)
|
|
57
|
+
event.Name = name
|
|
58
|
+
event.Input = input_
|
|
59
|
+
self.history_events.append(event)
|
|
60
|
+
|
|
61
|
+
def add_sub_orchestrator_completed_event(self, result, id_):
|
|
62
|
+
event = self.get_base_event(HistoryEventType.SUB_ORCHESTRATION_INSTANCE_COMPLETED)
|
|
63
|
+
event.Result = result
|
|
64
|
+
event.TaskScheduledId = id_
|
|
65
|
+
self.history_events.append(event)
|
|
66
|
+
|
|
67
|
+
def add_sub_orchestrator_failed_event(self, id_, reason, details):
|
|
68
|
+
event = self.get_base_event(HistoryEventType.SUB_ORCHESTRATION_INSTANCE_FAILED)
|
|
69
|
+
event.Reason = reason
|
|
70
|
+
event.Details = details
|
|
71
|
+
event.TaskScheduledId = id_
|
|
72
|
+
self.history_events.append(event)
|
|
73
|
+
|
|
74
|
+
def add_event_sent_event(self, instance_id, event_id):
|
|
75
|
+
event = self.get_base_event(HistoryEventType.EVENT_SENT)
|
|
76
|
+
event.InstanceId = instance_id
|
|
77
|
+
event._event_id = event_id
|
|
78
|
+
event.Name = "op"
|
|
79
|
+
event.Input = json.dumps({ "id": "0000" }) # usually provided by the extension
|
|
80
|
+
self.history_events.append(event)
|
|
81
|
+
|
|
82
|
+
def add_task_scheduled_event(
|
|
83
|
+
self, name: str, id_: int, version: str = '', input_=None):
|
|
84
|
+
event = self.get_base_event(HistoryEventType.TASK_SCHEDULED, id_=id_)
|
|
85
|
+
event.Name = name
|
|
86
|
+
event.Version = version
|
|
87
|
+
event.Input_ = input_
|
|
88
|
+
self.history_events.append(event)
|
|
89
|
+
|
|
90
|
+
def add_task_completed_event(self, id_: int, result, is_played=False):
|
|
91
|
+
event = self.get_base_event(HistoryEventType.TASK_COMPLETED, is_played=is_played)
|
|
92
|
+
event.Result = result
|
|
93
|
+
event.TaskScheduledId = id_
|
|
94
|
+
self.history_events.append(event)
|
|
95
|
+
|
|
96
|
+
def add_task_failed_event(self, id_: int, reason: str, details: str):
|
|
97
|
+
event = self.get_base_event(HistoryEventType.TASK_FAILED)
|
|
98
|
+
event.Reason = reason
|
|
99
|
+
event.Details = details
|
|
100
|
+
event.TaskScheduledId = id_
|
|
101
|
+
self.history_events.append(event)
|
|
102
|
+
|
|
103
|
+
def add_timer_created_event(self, id_: int, timestamp: str = None):
|
|
104
|
+
fire_at = self.current_datetime.strftime(DATETIME_STRING_FORMAT)
|
|
105
|
+
if timestamp is not None:
|
|
106
|
+
fire_at = timestamp
|
|
107
|
+
event = self.get_base_event(HistoryEventType.TIMER_CREATED, id_=id_)
|
|
108
|
+
event.FireAt = fire_at
|
|
109
|
+
self.history_events.append(event)
|
|
110
|
+
return fire_at
|
|
111
|
+
|
|
112
|
+
def add_timer_fired_event(self, id_: int, fire_at: str, is_played: bool = True):
|
|
113
|
+
event = self.get_base_event(HistoryEventType.TIMER_FIRED, is_played=is_played)
|
|
114
|
+
event.TimerId = id_
|
|
115
|
+
event.FireAt = fire_at
|
|
116
|
+
self.history_events.append(event)
|
|
117
|
+
|
|
118
|
+
def add_execution_started_event(
|
|
119
|
+
self, name: str, version: str = '', input_=None, is_played=True):
|
|
120
|
+
event = self.get_base_event(HistoryEventType.EXECUTION_STARTED, is_played=is_played)
|
|
121
|
+
event.orchestration_instance = OrchestrationInstance()
|
|
122
|
+
self.instance_id = event.orchestration_instance.instance_id
|
|
123
|
+
event.Name = name
|
|
124
|
+
event.Version = version
|
|
125
|
+
event.Input = input_
|
|
126
|
+
self.history_events.append(event)
|
|
127
|
+
|
|
128
|
+
def add_event_raised_event(self, name:str, id_: int, input_=None, timestamp=None, is_entity=False, is_error = False, literal_input=False):
|
|
129
|
+
event = self.get_base_event(HistoryEventType.EVENT_RAISED, id_=id_, timestamp=timestamp)
|
|
130
|
+
event.Name = name
|
|
131
|
+
if is_entity:
|
|
132
|
+
if is_error:
|
|
133
|
+
event.Input = json.dumps({ "result": json.dumps(input_), "exceptionType": "True" })
|
|
134
|
+
else:
|
|
135
|
+
if literal_input:
|
|
136
|
+
event.Input = json.dumps({ "result": input_ })
|
|
137
|
+
else:
|
|
138
|
+
event.Input = json.dumps({ "result": json.dumps(input_) })
|
|
139
|
+
else:
|
|
140
|
+
event.Input = input_
|
|
141
|
+
# event.timestamp = timestamp
|
|
142
|
+
self.history_events.append(event)
|
|
143
|
+
|
|
144
|
+
def to_json(self, **kwargs) -> Dict[str, Any]:
|
|
145
|
+
json_dict = {}
|
|
146
|
+
|
|
147
|
+
add_attrib(json_dict, self, 'instance_id', 'instanceId')
|
|
148
|
+
add_attrib(json_dict, self, 'parent_instance_id', 'parentInstanceId')
|
|
149
|
+
add_attrib(json_dict, self, 'is_replaying', 'isReplaying')
|
|
150
|
+
add_attrib(json_dict, self, 'input_', "input")
|
|
151
|
+
add_attrib(json_dict, self, 'upperSchemaVersion', "upperSchemaVersion")
|
|
152
|
+
|
|
153
|
+
history_list_as_dict = self.get_history_list_as_dict()
|
|
154
|
+
json_dict['history'] = history_list_as_dict
|
|
155
|
+
|
|
156
|
+
if kwargs is not None:
|
|
157
|
+
for key, value in kwargs.items():
|
|
158
|
+
json_dict[key] = value
|
|
159
|
+
|
|
160
|
+
return json_dict
|
|
161
|
+
|
|
162
|
+
def get_history_list_as_dict(self) -> List[Dict[str, Any]]:
|
|
163
|
+
history_list = []
|
|
164
|
+
|
|
165
|
+
for history_event in self.history_events:
|
|
166
|
+
event_as_dict = convert_history_event_to_json_dict(history_event)
|
|
167
|
+
history_list.append(event_as_dict)
|
|
168
|
+
|
|
169
|
+
return history_list
|
|
170
|
+
|
|
171
|
+
def to_json_string(self, **kwargs) -> str:
|
|
172
|
+
json_dict = self.to_json(**kwargs)
|
|
173
|
+
|
|
174
|
+
return json.dumps(json_dict)
|
|
@@ -1,57 +1,57 @@
|
|
|
1
|
-
import json
|
|
2
|
-
from typing import Any, List, Dict, Any
|
|
3
|
-
|
|
4
|
-
class EntityContextBuilder():
|
|
5
|
-
"""Mock class for an EntityContext object, includes a batch field for convenience
|
|
6
|
-
"""
|
|
7
|
-
def __init__(self,
|
|
8
|
-
name: str = "",
|
|
9
|
-
key: str = "",
|
|
10
|
-
exists: bool = True,
|
|
11
|
-
state: Any = None,
|
|
12
|
-
batch: List[Dict[str, Any]] = []):
|
|
13
|
-
"""Construct an EntityContextBuilder
|
|
14
|
-
|
|
15
|
-
Parameters
|
|
16
|
-
----------
|
|
17
|
-
name: str:
|
|
18
|
-
The name of the entity. Defaults to the empty string.
|
|
19
|
-
key: str
|
|
20
|
-
The key of the entity. Defaults to the empty string.
|
|
21
|
-
exists: bool
|
|
22
|
-
Boolean representing if the entity exists, defaults to True.
|
|
23
|
-
state: Any
|
|
24
|
-
The state of the entity, defaults ot None.
|
|
25
|
-
batch: List[Dict[str, Any]]
|
|
26
|
-
The upcoming batch of operations for the entity to perform.
|
|
27
|
-
Note that the batch is not technically a part of the entity context
|
|
28
|
-
and so it is here only for convenience. Defaults to the empty list.
|
|
29
|
-
"""
|
|
30
|
-
self.name = name
|
|
31
|
-
self.key = key
|
|
32
|
-
self.exists = exists
|
|
33
|
-
self.state = state
|
|
34
|
-
self.batch = batch
|
|
35
|
-
|
|
36
|
-
def to_json_string(self) -> str:
|
|
37
|
-
"""Generate a string-representation of the Entity input payload.
|
|
38
|
-
|
|
39
|
-
The payload matches the current durable-extension entity-communication
|
|
40
|
-
schema.
|
|
41
|
-
|
|
42
|
-
Returns
|
|
43
|
-
-------
|
|
44
|
-
str:
|
|
45
|
-
A JSON-formatted string for an EntityContext to load via `from_json`
|
|
46
|
-
"""
|
|
47
|
-
context_json = {
|
|
48
|
-
"self": {
|
|
49
|
-
"name": self.name,
|
|
50
|
-
"key": self.key
|
|
51
|
-
},
|
|
52
|
-
"state": self.state,
|
|
53
|
-
"exists": self.exists,
|
|
54
|
-
"batch": self.batch
|
|
55
|
-
}
|
|
56
|
-
json_string = json.dumps(context_json)
|
|
1
|
+
import json
|
|
2
|
+
from typing import Any, List, Dict, Any
|
|
3
|
+
|
|
4
|
+
class EntityContextBuilder():
|
|
5
|
+
"""Mock class for an EntityContext object, includes a batch field for convenience
|
|
6
|
+
"""
|
|
7
|
+
def __init__(self,
|
|
8
|
+
name: str = "",
|
|
9
|
+
key: str = "",
|
|
10
|
+
exists: bool = True,
|
|
11
|
+
state: Any = None,
|
|
12
|
+
batch: List[Dict[str, Any]] = []):
|
|
13
|
+
"""Construct an EntityContextBuilder
|
|
14
|
+
|
|
15
|
+
Parameters
|
|
16
|
+
----------
|
|
17
|
+
name: str:
|
|
18
|
+
The name of the entity. Defaults to the empty string.
|
|
19
|
+
key: str
|
|
20
|
+
The key of the entity. Defaults to the empty string.
|
|
21
|
+
exists: bool
|
|
22
|
+
Boolean representing if the entity exists, defaults to True.
|
|
23
|
+
state: Any
|
|
24
|
+
The state of the entity, defaults ot None.
|
|
25
|
+
batch: List[Dict[str, Any]]
|
|
26
|
+
The upcoming batch of operations for the entity to perform.
|
|
27
|
+
Note that the batch is not technically a part of the entity context
|
|
28
|
+
and so it is here only for convenience. Defaults to the empty list.
|
|
29
|
+
"""
|
|
30
|
+
self.name = name
|
|
31
|
+
self.key = key
|
|
32
|
+
self.exists = exists
|
|
33
|
+
self.state = state
|
|
34
|
+
self.batch = batch
|
|
35
|
+
|
|
36
|
+
def to_json_string(self) -> str:
|
|
37
|
+
"""Generate a string-representation of the Entity input payload.
|
|
38
|
+
|
|
39
|
+
The payload matches the current durable-extension entity-communication
|
|
40
|
+
schema.
|
|
41
|
+
|
|
42
|
+
Returns
|
|
43
|
+
-------
|
|
44
|
+
str:
|
|
45
|
+
A JSON-formatted string for an EntityContext to load via `from_json`
|
|
46
|
+
"""
|
|
47
|
+
context_json = {
|
|
48
|
+
"self": {
|
|
49
|
+
"name": self.name,
|
|
50
|
+
"key": self.key
|
|
51
|
+
},
|
|
52
|
+
"state": self.state,
|
|
53
|
+
"exists": self.exists,
|
|
54
|
+
"batch": self.batch
|
|
55
|
+
}
|
|
56
|
+
json_string = json.dumps(context_json)
|
|
57
57
|
return json_string
|
tests/test_utils/constants.py
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
RPC_BASE_URL = "http://127.0.0.1:17071/durabletask/"
|
|
1
|
+
RPC_BASE_URL = "http://127.0.0.1:17071/durabletask/"
|
tests/test_utils/json_utils.py
CHANGED
|
@@ -1,30 +1,30 @@
|
|
|
1
|
-
from typing import Any, Dict
|
|
2
|
-
|
|
3
|
-
from azure.durable_functions.models.history.HistoryEvent import HistoryEvent
|
|
4
|
-
from azure.durable_functions.models.utils.json_utils \
|
|
5
|
-
import add_attrib, add_json_attrib, add_datetime_attrib
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
def convert_history_event_to_json_dict(
|
|
9
|
-
history_event: HistoryEvent) -> Dict[str, Any]:
|
|
10
|
-
json_dict = {}
|
|
11
|
-
|
|
12
|
-
add_attrib(json_dict, history_event, 'event_id', 'EventId')
|
|
13
|
-
add_attrib(json_dict, history_event, 'event_type', 'EventType')
|
|
14
|
-
add_attrib(json_dict, history_event, 'is_played', 'IsPlayed')
|
|
15
|
-
add_datetime_attrib(json_dict, history_event, 'timestamp', 'Timestamp')
|
|
16
|
-
add_attrib(json_dict, history_event, 'Input')
|
|
17
|
-
add_attrib(json_dict, history_event, 'Reason')
|
|
18
|
-
add_attrib(json_dict, history_event, 'Details')
|
|
19
|
-
add_attrib(json_dict, history_event, 'Result')
|
|
20
|
-
add_attrib(json_dict, history_event, 'Version')
|
|
21
|
-
add_attrib(json_dict, history_event, 'RetryOptions')
|
|
22
|
-
add_attrib(json_dict, history_event, 'TaskScheduledId')
|
|
23
|
-
add_attrib(json_dict, history_event, 'Tags')
|
|
24
|
-
add_attrib(json_dict, history_event, 'FireAt')
|
|
25
|
-
add_attrib(json_dict, history_event, 'TimerId')
|
|
26
|
-
add_attrib(json_dict, history_event, 'Name')
|
|
27
|
-
add_attrib(json_dict, history_event, 'InstanceId')
|
|
28
|
-
add_json_attrib(json_dict, history_event,
|
|
29
|
-
'orchestration_instance', 'OrchestrationInstance')
|
|
30
|
-
return json_dict
|
|
1
|
+
from typing import Any, Dict
|
|
2
|
+
|
|
3
|
+
from azure.durable_functions.models.history.HistoryEvent import HistoryEvent
|
|
4
|
+
from azure.durable_functions.models.utils.json_utils \
|
|
5
|
+
import add_attrib, add_json_attrib, add_datetime_attrib
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
def convert_history_event_to_json_dict(
|
|
9
|
+
history_event: HistoryEvent) -> Dict[str, Any]:
|
|
10
|
+
json_dict = {}
|
|
11
|
+
|
|
12
|
+
add_attrib(json_dict, history_event, 'event_id', 'EventId')
|
|
13
|
+
add_attrib(json_dict, history_event, 'event_type', 'EventType')
|
|
14
|
+
add_attrib(json_dict, history_event, 'is_played', 'IsPlayed')
|
|
15
|
+
add_datetime_attrib(json_dict, history_event, 'timestamp', 'Timestamp')
|
|
16
|
+
add_attrib(json_dict, history_event, 'Input')
|
|
17
|
+
add_attrib(json_dict, history_event, 'Reason')
|
|
18
|
+
add_attrib(json_dict, history_event, 'Details')
|
|
19
|
+
add_attrib(json_dict, history_event, 'Result')
|
|
20
|
+
add_attrib(json_dict, history_event, 'Version')
|
|
21
|
+
add_attrib(json_dict, history_event, 'RetryOptions')
|
|
22
|
+
add_attrib(json_dict, history_event, 'TaskScheduledId')
|
|
23
|
+
add_attrib(json_dict, history_event, 'Tags')
|
|
24
|
+
add_attrib(json_dict, history_event, 'FireAt')
|
|
25
|
+
add_attrib(json_dict, history_event, 'TimerId')
|
|
26
|
+
add_attrib(json_dict, history_event, 'Name')
|
|
27
|
+
add_attrib(json_dict, history_event, 'InstanceId')
|
|
28
|
+
add_json_attrib(json_dict, history_event,
|
|
29
|
+
'orchestration_instance', 'OrchestrationInstance')
|
|
30
|
+
return json_dict
|