Skip to content

Commit 5aae164

Browse files
committed
initial commit
1 parent 4ab004b commit 5aae164

3 files changed

Lines changed: 117 additions & 1 deletion

File tree

azure/durable_functions/models/DurableOrchestrationClient.py

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -767,6 +767,44 @@ async def suspend(self, instance_id: str, reason: str) -> None:
767767
if error_message:
768768
raise Exception(error_message)
769769

770+
async def restart(self, instance_id: str,
771+
restart_with_new_instance_id: bool = True) -> str:
772+
"""Restart an orchestration instance with its original input.
773+
774+
Parameters
775+
----------
776+
instance_id : str
777+
The ID of the orchestration instance to restart.
778+
restart_with_new_instance_id : bool
779+
If True, the restarted instance will use a new instance ID.
780+
If False, the restarted instance will reuse the original instance ID.
781+
782+
Raises
783+
------
784+
Exception:
785+
When the instance with the given ID is not found.
786+
787+
Returns
788+
-------
789+
str
790+
The instance ID of the restarted orchestration.
791+
"""
792+
status = await self.get_status(instance_id, show_input=True)
793+
794+
if not status or status.name is None:
795+
raise Exception(
796+
f"An orchestration with the instanceId {instance_id} was not found.")
797+
798+
if restart_with_new_instance_id:
799+
return await self.start_new(
800+
orchestration_function_name=status.name,
801+
client_input=status.input_)
802+
else:
803+
return await self.start_new(
804+
orchestration_function_name=status.name,
805+
instance_id=status.instance_id,
806+
client_input=status.input_)
807+
770808
async def resume(self, instance_id: str, reason: str) -> None:
771809
"""Resume the specified orchestration instance.
772810

tests/conftest.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,8 @@ def get_binding_string():
4646
"resumePostUri": f"{BASE_URL}/instances/INSTANCEID/resume?reason="
4747
"{text}&taskHub="
4848
f"{TASK_HUB_NAME}&connection=Storage&code={AUTH_CODE}",
49+
"restartPostUri": f"{BASE_URL}/instances/INSTANCEID/restart?taskHub="
50+
f"{TASK_HUB_NAME}&connection=Storage&code={AUTH_CODE}",
4951
},
5052
"rpcBaseUrl": RPC_BASE_URL
5153
}

tests/models/test_DurableOrchestrationClient.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -156,7 +156,11 @@ def test_create_check_status_response(binding_string):
156156
"resumePostUri":
157157
r"http://test_azure.net/runtime/webhooks/durabletask/instances/"
158158
r"2e2568e7-a906-43bd-8364-c81733c5891e/resume"
159-
r"?reason={text}&taskHub=TASK_HUB_NAME&connection=Storage&code=AUTH_CODE"
159+
r"?reason={text}&taskHub=TASK_HUB_NAME&connection=Storage&code=AUTH_CODE",
160+
"restartPostUri":
161+
r"http://test_azure.net/runtime/webhooks/durabletask/instances/"
162+
r"2e2568e7-a906-43bd-8364-c81733c5891e/restart"
163+
r"?taskHub=TASK_HUB_NAME&connection=Storage&code=AUTH_CODE"
160164
}
161165
for key, _ in http_management_payload.items():
162166
http_management_payload[key] = replace_stand_in_bits(http_management_payload[key])
@@ -739,3 +743,75 @@ async def test_post_500_resume(binding_string):
739743

740744
with pytest.raises(Exception):
741745
await client.resume(TEST_INSTANCE_ID, raw_reason)
746+
747+
748+
@pytest.mark.asyncio
749+
async def test_restart_with_new_instance_id(binding_string):
750+
"""Test restart creates a new instance with a new ID by default."""
751+
orchestrator_name = "MyOrchestrator"
752+
original_input = {"key": "value"}
753+
new_instance_id = "new-instance-id-1234"
754+
755+
get_mock = MockRequest(
756+
expected_url=f"{RPC_BASE_URL}instances/{TEST_INSTANCE_ID}?showInput=True",
757+
response=[200, dict(
758+
name=orchestrator_name,
759+
instanceId=TEST_INSTANCE_ID,
760+
createdTime=TEST_CREATED_TIME,
761+
lastUpdatedTime=TEST_LAST_UPDATED_TIME,
762+
runtimeStatus="Completed",
763+
input=original_input)])
764+
765+
post_mock = MockRequest(
766+
expected_url=f"{RPC_BASE_URL}orchestrators/{orchestrator_name}",
767+
response=[202, {"id": new_instance_id}])
768+
769+
client = DurableOrchestrationClient(binding_string)
770+
client._get_async_request = get_mock.get
771+
client._post_async_request = post_mock.post
772+
773+
result = await client.restart(TEST_INSTANCE_ID)
774+
assert result == new_instance_id
775+
776+
777+
@pytest.mark.asyncio
778+
async def test_restart_with_same_instance_id(binding_string):
779+
"""Test restart reuses the original instance ID when restartWithNewInstanceId is False."""
780+
orchestrator_name = "MyOrchestrator"
781+
original_input = {"key": "value"}
782+
783+
get_mock = MockRequest(
784+
expected_url=f"{RPC_BASE_URL}instances/{TEST_INSTANCE_ID}?showInput=True",
785+
response=[200, dict(
786+
name=orchestrator_name,
787+
instanceId=TEST_INSTANCE_ID,
788+
createdTime=TEST_CREATED_TIME,
789+
lastUpdatedTime=TEST_LAST_UPDATED_TIME,
790+
runtimeStatus="Completed",
791+
input=original_input)])
792+
793+
post_mock = MockRequest(
794+
expected_url=f"{RPC_BASE_URL}orchestrators/{orchestrator_name}/{TEST_INSTANCE_ID}",
795+
response=[202, {"id": TEST_INSTANCE_ID}])
796+
797+
client = DurableOrchestrationClient(binding_string)
798+
client._get_async_request = get_mock.get
799+
client._post_async_request = post_mock.post
800+
801+
result = await client.restart(TEST_INSTANCE_ID, restart_with_new_instance_id=False)
802+
assert result == TEST_INSTANCE_ID
803+
804+
805+
@pytest.mark.asyncio
806+
async def test_restart_instance_not_found(binding_string):
807+
"""Test restart raises exception when instance is not found."""
808+
get_mock = MockRequest(
809+
expected_url=f"{RPC_BASE_URL}instances/{TEST_INSTANCE_ID}?showInput=True",
810+
response=[404, dict(createdTime=None, lastUpdatedTime=None)])
811+
812+
client = DurableOrchestrationClient(binding_string)
813+
client._get_async_request = get_mock.get
814+
815+
with pytest.raises(Exception) as ex:
816+
await client.restart(TEST_INSTANCE_ID)
817+
assert f"instanceId {TEST_INSTANCE_ID} was not found" in str(ex.value)

0 commit comments

Comments
 (0)