Skip to content

Commit b30d0a6

Browse files
authored
Fix History Event None Return (#466)
* check activity return not none * avoid long line * add check for subo and event * fix indent * update by review * add unit test for call activity with none return * update test
1 parent 7d21ac9 commit b30d0a6

File tree

2 files changed

+50
-7
lines changed

2 files changed

+50
-7
lines changed

azure/durable_functions/models/TaskOrchestrationExecutor.py

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -165,11 +165,14 @@ def parse_history_event(directive_result):
165165

166166
# We provide the ability to deserialize custom objects, because the output of this
167167
# will be passed directly to the orchestrator as the output of some activity
168-
if event_type == HistoryEventType.SUB_ORCHESTRATION_INSTANCE_COMPLETED:
168+
if (event_type == HistoryEventType.SUB_ORCHESTRATION_INSTANCE_COMPLETED
169+
and directive_result.Result is not None):
169170
return json.loads(directive_result.Result, object_hook=_deserialize_custom_object)
170-
if event_type == HistoryEventType.TASK_COMPLETED:
171+
if (event_type == HistoryEventType.TASK_COMPLETED
172+
and directive_result.Result is not None):
171173
return json.loads(directive_result.Result, object_hook=_deserialize_custom_object)
172-
if event_type == HistoryEventType.EVENT_RAISED:
174+
if (event_type == HistoryEventType.EVENT_RAISED
175+
and directive_result.Input is not None):
173176
# TODO: Investigate why the payload is in "Input" instead of "Result"
174177
response = json.loads(directive_result.Input,
175178
object_hook=_deserialize_custom_object)

tests/orchestrator/test_sequential_orchestrator.py

Lines changed: 44 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -257,10 +257,31 @@ def generator_function_call_activity_with_orchestrator(context):
257257

258258
return outputs
259259

260+
def generator_function_call_activity_with_none_return(context):
261+
"""Simple orchestrator that call activity function which can return None"""
262+
outputs = []
263+
264+
task1 = yield context.call_activity(hello_return_none, "Tokyo")
265+
task2 = yield context.call_activity(hello_return_none, "Seattle")
266+
task3 = yield context.call_activity(hello_return_none, "London")
267+
268+
outputs.append(task1)
269+
outputs.append(task2)
270+
outputs.append(task3)
271+
272+
return outputs
273+
260274
@app.activity_trigger(input_name = "myArg")
261275
def Hello(myArg: str):
262276
return "Hello" + myArg
263277

278+
@app.activity_trigger(input_name = "myArg")
279+
def hello_return_none(myArg: str):
280+
if myArg == "London":
281+
return None
282+
else:
283+
return "Hello" + myArg
284+
264285
def base_expected_state(output=None, replay_schema: ReplaySchema = ReplaySchema.V1) -> OrchestratorState:
265286
return OrchestratorState(is_done=False, actions=[], output=output, replay_schema=replay_schema)
266287

@@ -270,13 +291,13 @@ def add_timer_fired_events(context_builder: ContextBuilder, id_: int, timestamp:
270291
context_builder.add_orchestrator_started_event()
271292
context_builder.add_timer_fired_event(id_=id_, fire_at=fire_at)
272293

273-
def add_hello_action(state: OrchestratorState, input_: str):
274-
action = CallActivityAction(function_name='Hello', input_=input_)
294+
def add_hello_action(state: OrchestratorState, input_: str, activity_name="Hello"):
295+
action = CallActivityAction(function_name=activity_name, input_=input_)
275296
state.actions.append([action])
276297

277298
def add_hello_completed_events(
278-
context_builder: ContextBuilder, id_: int, result: str, is_played=False):
279-
context_builder.add_task_scheduled_event(name='Hello', id_=id_)
299+
context_builder: ContextBuilder, id_: int, result: str, is_played=False, activity_name="Hello"):
300+
context_builder.add_task_scheduled_event(name=activity_name, id_=id_)
280301
context_builder.add_orchestrator_completed_event()
281302
context_builder.add_orchestrator_started_event()
282303
context_builder.add_task_completed_event(id_=id_, result=result, is_played=is_played)
@@ -365,6 +386,25 @@ def test_call_activity_with_name():
365386
assert_valid_schema(result)
366387
assert_orchestration_state_equals(expected, result)
367388

389+
def test_call_activity_with_none_return():
390+
context_builder = ContextBuilder('test_call_activity_with_none_return')
391+
add_hello_completed_events(context_builder, 0, "\"Hello Tokyo!\"", "hello_return_none")
392+
add_hello_completed_events(context_builder, 1, "\"Hello Seattle!\"", "hello_return_none")
393+
add_hello_completed_events(context_builder, 2, None, "hello_return_none")
394+
result = get_orchestration_state_result(
395+
context_builder, generator_function_call_activity_with_none_return)
396+
397+
expected_state = base_expected_state(
398+
['Hello Tokyo!', 'Hello Seattle!', None])
399+
add_hello_action(expected_state, 'Tokyo', "hello_return_none")
400+
add_hello_action(expected_state, 'Seattle', "hello_return_none")
401+
add_hello_action(expected_state, 'London', "hello_return_none")
402+
expected_state._is_done = True
403+
expected = expected_state.to_json()
404+
405+
assert_valid_schema(result)
406+
assert_orchestration_state_equals(expected, result)
407+
368408
def test_call_activity_function_callable_exception():
369409
context_builder = ContextBuilder('test_call_activity_by_name_exception')
370410

0 commit comments

Comments
 (0)