azure-functions-durable 1.2.9__py3-none-any.whl → 1.3.0__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 +260 -249
- azure/durable_functions/decorators/metadata.py +109 -109
- azure/durable_functions/entity.py +129 -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 +812 -781
- azure/durable_functions/models/DurableOrchestrationContext.py +761 -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 -32
- azure/durable_functions/models/OrchestratorState.py +117 -116
- azure/durable_functions/models/PurgeHistoryResult.py +33 -33
- azure/durable_functions/models/ReplaySchema.py +9 -8
- azure/durable_functions/models/RetryOptions.py +69 -69
- azure/durable_functions/models/RpcManagementOptions.py +86 -86
- azure/durable_functions/models/Task.py +540 -426
- azure/durable_functions/models/TaskOrchestrationExecutor.py +352 -336
- azure/durable_functions/models/TokenSource.py +56 -56
- azure/durable_functions/models/__init__.py +26 -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 +94 -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 -27
- 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 +80 -69
- azure/durable_functions/models/utils/json_utils.py +96 -56
- azure/durable_functions/orchestrator.py +73 -71
- azure/durable_functions/testing/OrchestratorGeneratorWrapper.py +42 -0
- azure/durable_functions/testing/__init__.py +6 -0
- {azure_functions_durable-1.2.9.dist-info → azure_functions_durable-1.3.0.dist-info}/LICENSE +21 -21
- {azure_functions_durable-1.2.9.dist-info → azure_functions_durable-1.3.0.dist-info}/METADATA +59 -58
- azure_functions_durable-1.3.0.dist-info/RECORD +103 -0
- {azure_functions_durable-1.2.9.dist-info → azure_functions_durable-1.3.0.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 -68
- tests/models/test_DurableOrchestrationClient.py +730 -730
- 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 +397 -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 -841
- 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_long_timers.py +70 -0
- 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.9.data/data/_manifest/bsi.json +0 -1
- azure_functions_durable-1.2.9.data/data/_manifest/manifest.cat +0 -0
- azure_functions_durable-1.2.9.data/data/_manifest/manifest.spdx.json +0 -11985
- azure_functions_durable-1.2.9.data/data/_manifest/manifest.spdx.json.sha256 +0 -1
- azure_functions_durable-1.2.9.dist-info/RECORD +0 -102
- {azure_functions_durable-1.2.9.dist-info → azure_functions_durable-1.3.0.dist-info}/top_level.txt +0 -0
|
@@ -1,127 +1,127 @@
|
|
|
1
|
-
from azure.durable_functions.models.ReplaySchema import ReplaySchema
|
|
2
|
-
from azure.durable_functions.models.actions.WhenAnyAction import WhenAnyAction
|
|
3
|
-
from tests.test_utils.ContextBuilder import ContextBuilder
|
|
4
|
-
from .orchestrator_test_utils \
|
|
5
|
-
import get_orchestration_state_result, assert_orchestration_state_equals, assert_valid_schema
|
|
6
|
-
from azure.durable_functions.models.actions.CreateTimerAction import CreateTimerAction
|
|
7
|
-
from azure.durable_functions.models.OrchestratorState import OrchestratorState
|
|
8
|
-
from azure.durable_functions.constants import DATETIME_STRING_FORMAT
|
|
9
|
-
from datetime import datetime, timedelta, timezone
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
def base_expected_state(output=None, replay_schema: ReplaySchema = ReplaySchema.V1) -> OrchestratorState:
|
|
13
|
-
return OrchestratorState(is_done=False, actions=[], output=output, replay_schema=replay_schema)
|
|
14
|
-
|
|
15
|
-
def add_timer_fired_events(context_builder: ContextBuilder, id_: int, timestamp: str):
|
|
16
|
-
fire_at: str = context_builder.add_timer_created_event(id_, timestamp)
|
|
17
|
-
context_builder.add_orchestrator_completed_event()
|
|
18
|
-
context_builder.add_orchestrator_started_event()
|
|
19
|
-
context_builder.add_timer_fired_event(id_=id_, fire_at=fire_at)
|
|
20
|
-
|
|
21
|
-
def generator_function(context):
|
|
22
|
-
|
|
23
|
-
# Create a timezone aware datetime object, just like a normal
|
|
24
|
-
# call to `context.current_utc_datetime` would create
|
|
25
|
-
timestamp = "2020-07-23T21:56:54.936700Z"
|
|
26
|
-
fire_at = datetime.strptime(timestamp, DATETIME_STRING_FORMAT)
|
|
27
|
-
fire_at = fire_at.replace(tzinfo=timezone.utc)
|
|
28
|
-
|
|
29
|
-
yield context.create_timer(fire_at)
|
|
30
|
-
return "Done!"
|
|
31
|
-
|
|
32
|
-
def generator_function_timer_can_be_cancelled(context):
|
|
33
|
-
time_limit1 = context.current_utc_datetime + timedelta(minutes=5)
|
|
34
|
-
timer_task1 = context.create_timer(time_limit1)
|
|
35
|
-
|
|
36
|
-
time_limit2 = context.current_utc_datetime + timedelta(minutes=10)
|
|
37
|
-
timer_task2 = context.create_timer(time_limit2)
|
|
38
|
-
|
|
39
|
-
winner = yield context.task_any([timer_task1, timer_task2])
|
|
40
|
-
if winner == timer_task1:
|
|
41
|
-
timer_task2.cancel()
|
|
42
|
-
return "Done!"
|
|
43
|
-
else:
|
|
44
|
-
raise Exception("timer task 1 should complete before timer task 2")
|
|
45
|
-
|
|
46
|
-
def add_timer_action(state: OrchestratorState, fire_at: datetime):
|
|
47
|
-
action = CreateTimerAction(fire_at=fire_at)
|
|
48
|
-
state._actions.append([action])
|
|
49
|
-
|
|
50
|
-
def test_timers_comparison_with_relaxed_precision():
|
|
51
|
-
"""Test if that two `datetime` different but equivalent
|
|
52
|
-
serializations of timer deadlines are found to be equivalent.
|
|
53
|
-
|
|
54
|
-
The Durable Extension may sometimes drop redundant zeroes on
|
|
55
|
-
a datetime object. For instance, the date
|
|
56
|
-
2020-07-23T21:56:54.936700Z
|
|
57
|
-
may get transformed into
|
|
58
|
-
2020-07-23T21:56:54.9367Z
|
|
59
|
-
This test ensures that dropping redundant zeroes does not affect
|
|
60
|
-
our ability to recognize that a timer has been fired.
|
|
61
|
-
"""
|
|
62
|
-
|
|
63
|
-
# equivalent to 2020-07-23T21:56:54.936700Z
|
|
64
|
-
relaxed_timestamp = "2020-07-23T21:56:54.9367Z"
|
|
65
|
-
fire_at = datetime.strptime(relaxed_timestamp, DATETIME_STRING_FORMAT)
|
|
66
|
-
|
|
67
|
-
context_builder = ContextBuilder("relaxed precision")
|
|
68
|
-
add_timer_fired_events(context_builder, 0, relaxed_timestamp)
|
|
69
|
-
|
|
70
|
-
result = get_orchestration_state_result(
|
|
71
|
-
context_builder, generator_function)
|
|
72
|
-
|
|
73
|
-
expected_state = base_expected_state(output='Done!')
|
|
74
|
-
add_timer_action(expected_state, fire_at)
|
|
75
|
-
|
|
76
|
-
expected_state._is_done = True
|
|
77
|
-
expected = expected_state.to_json()
|
|
78
|
-
|
|
79
|
-
#assert_valid_schema(result)
|
|
80
|
-
# TODO: getting the following error when validating the schema
|
|
81
|
-
# "Additional properties are not allowed ('fireAt', 'isCanceled' were unexpected)">
|
|
82
|
-
assert_orchestration_state_equals(expected, result)
|
|
83
|
-
|
|
84
|
-
def test_timers_can_be_cancelled():
|
|
85
|
-
|
|
86
|
-
context_builder = ContextBuilder("test_timers_can_be_cancelled")
|
|
87
|
-
fire_at1 = context_builder.current_datetime + timedelta(minutes=5)
|
|
88
|
-
fire_at2 = context_builder.current_datetime + timedelta(minutes=10)
|
|
89
|
-
add_timer_fired_events(context_builder, 0, str(fire_at1))
|
|
90
|
-
add_timer_fired_events(context_builder, 1, str(fire_at2))
|
|
91
|
-
|
|
92
|
-
result = get_orchestration_state_result(
|
|
93
|
-
context_builder, generator_function_timer_can_be_cancelled)
|
|
94
|
-
|
|
95
|
-
expected_state = base_expected_state(output='Done!')
|
|
96
|
-
expected_state._actions.append(
|
|
97
|
-
[CreateTimerAction(fire_at=fire_at1), CreateTimerAction(fire_at=fire_at2, is_cancelled=True)])
|
|
98
|
-
|
|
99
|
-
expected_state._is_done = True
|
|
100
|
-
expected = expected_state.to_json()
|
|
101
|
-
|
|
102
|
-
assert_orchestration_state_equals(expected, result)
|
|
103
|
-
assert result["actions"][0][1]["isCanceled"]
|
|
104
|
-
|
|
105
|
-
def test_timers_can_be_cancelled_replayV2():
|
|
106
|
-
|
|
107
|
-
context_builder = ContextBuilder("test_timers_can_be_cancelled", replay_schema=ReplaySchema.V2)
|
|
108
|
-
fire_at1 = context_builder.current_datetime + timedelta(minutes=5)
|
|
109
|
-
fire_at2 = context_builder.current_datetime + timedelta(minutes=10)
|
|
110
|
-
add_timer_fired_events(context_builder, 0, str(fire_at1))
|
|
111
|
-
add_timer_fired_events(context_builder, 1, str(fire_at2))
|
|
112
|
-
|
|
113
|
-
result = get_orchestration_state_result(
|
|
114
|
-
context_builder, generator_function_timer_can_be_cancelled)
|
|
115
|
-
|
|
116
|
-
expected_state = base_expected_state(output='Done!', replay_schema=ReplaySchema.V2)
|
|
117
|
-
expected_state._actions = [
|
|
118
|
-
[WhenAnyAction(
|
|
119
|
-
[CreateTimerAction(fire_at=fire_at1), CreateTimerAction(fire_at=fire_at2, is_cancelled=True)]
|
|
120
|
-
)]
|
|
121
|
-
]
|
|
122
|
-
|
|
123
|
-
expected_state._is_done = True
|
|
124
|
-
expected = expected_state.to_json()
|
|
125
|
-
|
|
126
|
-
assert_orchestration_state_equals(expected, result)
|
|
1
|
+
from azure.durable_functions.models.ReplaySchema import ReplaySchema
|
|
2
|
+
from azure.durable_functions.models.actions.WhenAnyAction import WhenAnyAction
|
|
3
|
+
from tests.test_utils.ContextBuilder import ContextBuilder
|
|
4
|
+
from .orchestrator_test_utils \
|
|
5
|
+
import get_orchestration_state_result, assert_orchestration_state_equals, assert_valid_schema
|
|
6
|
+
from azure.durable_functions.models.actions.CreateTimerAction import CreateTimerAction
|
|
7
|
+
from azure.durable_functions.models.OrchestratorState import OrchestratorState
|
|
8
|
+
from azure.durable_functions.constants import DATETIME_STRING_FORMAT
|
|
9
|
+
from datetime import datetime, timedelta, timezone
|
|
10
|
+
|
|
11
|
+
|
|
12
|
+
def base_expected_state(output=None, replay_schema: ReplaySchema = ReplaySchema.V1) -> OrchestratorState:
|
|
13
|
+
return OrchestratorState(is_done=False, actions=[], output=output, replay_schema=replay_schema)
|
|
14
|
+
|
|
15
|
+
def add_timer_fired_events(context_builder: ContextBuilder, id_: int, timestamp: str):
|
|
16
|
+
fire_at: str = context_builder.add_timer_created_event(id_, timestamp)
|
|
17
|
+
context_builder.add_orchestrator_completed_event()
|
|
18
|
+
context_builder.add_orchestrator_started_event()
|
|
19
|
+
context_builder.add_timer_fired_event(id_=id_, fire_at=fire_at)
|
|
20
|
+
|
|
21
|
+
def generator_function(context):
|
|
22
|
+
|
|
23
|
+
# Create a timezone aware datetime object, just like a normal
|
|
24
|
+
# call to `context.current_utc_datetime` would create
|
|
25
|
+
timestamp = "2020-07-23T21:56:54.936700Z"
|
|
26
|
+
fire_at = datetime.strptime(timestamp, DATETIME_STRING_FORMAT)
|
|
27
|
+
fire_at = fire_at.replace(tzinfo=timezone.utc)
|
|
28
|
+
|
|
29
|
+
yield context.create_timer(fire_at)
|
|
30
|
+
return "Done!"
|
|
31
|
+
|
|
32
|
+
def generator_function_timer_can_be_cancelled(context):
|
|
33
|
+
time_limit1 = context.current_utc_datetime + timedelta(minutes=5)
|
|
34
|
+
timer_task1 = context.create_timer(time_limit1)
|
|
35
|
+
|
|
36
|
+
time_limit2 = context.current_utc_datetime + timedelta(minutes=10)
|
|
37
|
+
timer_task2 = context.create_timer(time_limit2)
|
|
38
|
+
|
|
39
|
+
winner = yield context.task_any([timer_task1, timer_task2])
|
|
40
|
+
if winner == timer_task1:
|
|
41
|
+
timer_task2.cancel()
|
|
42
|
+
return "Done!"
|
|
43
|
+
else:
|
|
44
|
+
raise Exception("timer task 1 should complete before timer task 2")
|
|
45
|
+
|
|
46
|
+
def add_timer_action(state: OrchestratorState, fire_at: datetime):
|
|
47
|
+
action = CreateTimerAction(fire_at=fire_at)
|
|
48
|
+
state._actions.append([action])
|
|
49
|
+
|
|
50
|
+
def test_timers_comparison_with_relaxed_precision():
|
|
51
|
+
"""Test if that two `datetime` different but equivalent
|
|
52
|
+
serializations of timer deadlines are found to be equivalent.
|
|
53
|
+
|
|
54
|
+
The Durable Extension may sometimes drop redundant zeroes on
|
|
55
|
+
a datetime object. For instance, the date
|
|
56
|
+
2020-07-23T21:56:54.936700Z
|
|
57
|
+
may get transformed into
|
|
58
|
+
2020-07-23T21:56:54.9367Z
|
|
59
|
+
This test ensures that dropping redundant zeroes does not affect
|
|
60
|
+
our ability to recognize that a timer has been fired.
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
# equivalent to 2020-07-23T21:56:54.936700Z
|
|
64
|
+
relaxed_timestamp = "2020-07-23T21:56:54.9367Z"
|
|
65
|
+
fire_at = datetime.strptime(relaxed_timestamp, DATETIME_STRING_FORMAT)
|
|
66
|
+
|
|
67
|
+
context_builder = ContextBuilder("relaxed precision")
|
|
68
|
+
add_timer_fired_events(context_builder, 0, relaxed_timestamp)
|
|
69
|
+
|
|
70
|
+
result = get_orchestration_state_result(
|
|
71
|
+
context_builder, generator_function)
|
|
72
|
+
|
|
73
|
+
expected_state = base_expected_state(output='Done!')
|
|
74
|
+
add_timer_action(expected_state, fire_at)
|
|
75
|
+
|
|
76
|
+
expected_state._is_done = True
|
|
77
|
+
expected = expected_state.to_json()
|
|
78
|
+
|
|
79
|
+
#assert_valid_schema(result)
|
|
80
|
+
# TODO: getting the following error when validating the schema
|
|
81
|
+
# "Additional properties are not allowed ('fireAt', 'isCanceled' were unexpected)">
|
|
82
|
+
assert_orchestration_state_equals(expected, result)
|
|
83
|
+
|
|
84
|
+
def test_timers_can_be_cancelled():
|
|
85
|
+
|
|
86
|
+
context_builder = ContextBuilder("test_timers_can_be_cancelled")
|
|
87
|
+
fire_at1 = context_builder.current_datetime + timedelta(minutes=5)
|
|
88
|
+
fire_at2 = context_builder.current_datetime + timedelta(minutes=10)
|
|
89
|
+
add_timer_fired_events(context_builder, 0, str(fire_at1))
|
|
90
|
+
add_timer_fired_events(context_builder, 1, str(fire_at2))
|
|
91
|
+
|
|
92
|
+
result = get_orchestration_state_result(
|
|
93
|
+
context_builder, generator_function_timer_can_be_cancelled)
|
|
94
|
+
|
|
95
|
+
expected_state = base_expected_state(output='Done!')
|
|
96
|
+
expected_state._actions.append(
|
|
97
|
+
[CreateTimerAction(fire_at=fire_at1), CreateTimerAction(fire_at=fire_at2, is_cancelled=True)])
|
|
98
|
+
|
|
99
|
+
expected_state._is_done = True
|
|
100
|
+
expected = expected_state.to_json()
|
|
101
|
+
|
|
102
|
+
assert_orchestration_state_equals(expected, result)
|
|
103
|
+
assert result["actions"][0][1]["isCanceled"]
|
|
104
|
+
|
|
105
|
+
def test_timers_can_be_cancelled_replayV2():
|
|
106
|
+
|
|
107
|
+
context_builder = ContextBuilder("test_timers_can_be_cancelled", replay_schema=ReplaySchema.V2)
|
|
108
|
+
fire_at1 = context_builder.current_datetime + timedelta(minutes=5)
|
|
109
|
+
fire_at2 = context_builder.current_datetime + timedelta(minutes=10)
|
|
110
|
+
add_timer_fired_events(context_builder, 0, str(fire_at1))
|
|
111
|
+
add_timer_fired_events(context_builder, 1, str(fire_at2))
|
|
112
|
+
|
|
113
|
+
result = get_orchestration_state_result(
|
|
114
|
+
context_builder, generator_function_timer_can_be_cancelled)
|
|
115
|
+
|
|
116
|
+
expected_state = base_expected_state(output='Done!', replay_schema=ReplaySchema.V2)
|
|
117
|
+
expected_state._actions = [
|
|
118
|
+
[WhenAnyAction(
|
|
119
|
+
[CreateTimerAction(fire_at=fire_at1), CreateTimerAction(fire_at=fire_at2, is_cancelled=True)]
|
|
120
|
+
)]
|
|
121
|
+
]
|
|
122
|
+
|
|
123
|
+
expected_state._is_done = True
|
|
124
|
+
expected = expected_state.to_json()
|
|
125
|
+
|
|
126
|
+
assert_orchestration_state_equals(expected, result)
|
|
127
127
|
assert result["actions"][0][0]['compoundActions'][1]["isCanceled"]
|