diff --git a/CHANGELOG.md b/CHANGELOG.md index 0751fd6..41fbf38 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,10 @@ All notable changes to this project will be documented in this file. - Type-hint-driven validation via `df_loads(s, expected_type=...)`: when the V2 programming model provides a return-type annotation for an activity or sub-orchestrator, the annotation is threaded through call sites so the SDK's `df_loads` can validate the deserialized payload against that type (when available). On older `azure-functions` releases the argument is accepted but ignored. - Return-type discovery for V2 decorated activities/sub-orchestrators (`azure.durable_functions.models.utils.type_discovery`): resolves the concrete return annotation from the user's registered function, used to supply `expected_type` to `df_loads`. +### Changed + +- `purge_instance_history_by` now raises a clear `ValueError` when the required `created_time_from` argument is omitted, instead of sending a request that the Durable extension rejects. + ## 1.0.0b6 - [Create timer](https://github.com/Azure/azure-functions-durable-python/issues/35) functionality available diff --git a/azure/durable_functions/models/DurableOrchestrationClient.py b/azure/durable_functions/models/DurableOrchestrationClient.py index 3f587e2..c3d32c6 100644 --- a/azure/durable_functions/models/DurableOrchestrationClient.py +++ b/azure/durable_functions/models/DurableOrchestrationClient.py @@ -423,7 +423,8 @@ async def purge_instance_history_by( Parameters ---------- created_time_from : Optional[datetime] - Delete orchestration history which were created after this Date. + Delete orchestration history which were created after this Date. This argument + is required. created_time_to: Optional[datetime] Delete orchestration history which were created before this Date. runtime_status: Optional[List[OrchestrationRuntimeStatus]] @@ -434,7 +435,15 @@ async def purge_instance_history_by( ------- PurgeHistoryResult The results of the request to purge history + + Raises + ------ + ValueError + When `created_time_from` is not provided. """ + if created_time_from is None: + raise ValueError("created_time_from is required when purging instance history") + options = RpcManagementOptions(created_time_from=created_time_from, created_time_to=created_time_to, runtime_status=runtime_status) diff --git a/tests/models/test_DurableOrchestrationClient.py b/tests/models/test_DurableOrchestrationClient.py index 38a1059..27df446 100644 --- a/tests/models/test_DurableOrchestrationClient.py +++ b/tests/models/test_DurableOrchestrationClient.py @@ -1,4 +1,5 @@ import json +from datetime import datetime from typing import Any import pytest @@ -366,40 +367,61 @@ async def test_delete_500_purge_instance_history(binding_string): @pytest.mark.asyncio async def test_delete_200_purge_instance_history_by(binding_string): - mock_request = MockRequest(expected_url=f"{RPC_BASE_URL}instances/?runtimeStatus=Running", - response=[200, dict(instancesDeleted=1)]) + mock_request = MockRequest( + expected_url=f"{RPC_BASE_URL}instances/" + "?createdTimeFrom=2000-01-01T00:00:00.000000Z" + "&runtimeStatus=Completed", + response=[200, dict(instancesDeleted=1)]) client = DurableOrchestrationClient(binding_string) client._delete_async_request = mock_request.delete result = await client.purge_instance_history_by( - runtime_status=[OrchestrationRuntimeStatus.Running]) + created_time_from=datetime(2000, 1, 1), + runtime_status=[OrchestrationRuntimeStatus.Completed]) assert result is not None assert result.instances_deleted == 1 @pytest.mark.asyncio async def test_delete_404_purge_instance_history_by(binding_string): - mock_request = MockRequest(expected_url=f"{RPC_BASE_URL}instances/?runtimeStatus=Running", - response=[404, MESSAGE_404]) + mock_request = MockRequest( + expected_url=f"{RPC_BASE_URL}instances/" + "?createdTimeFrom=2000-01-01T00:00:00.000000Z" + "&runtimeStatus=Completed", + response=[404, MESSAGE_404]) client = DurableOrchestrationClient(binding_string) client._delete_async_request = mock_request.delete result = await client.purge_instance_history_by( - runtime_status=[OrchestrationRuntimeStatus.Running]) + created_time_from=datetime(2000, 1, 1), + runtime_status=[OrchestrationRuntimeStatus.Completed]) assert result is not None assert result.instances_deleted == 0 @pytest.mark.asyncio async def test_delete_500_purge_instance_history_by(binding_string): - mock_request = MockRequest(expected_url=f"{RPC_BASE_URL}instances/?runtimeStatus=Running", - response=[500, MESSAGE_500]) + mock_request = MockRequest( + expected_url=f"{RPC_BASE_URL}instances/" + "?createdTimeFrom=2000-01-01T00:00:00.000000Z" + "&runtimeStatus=Completed", + response=[500, MESSAGE_500]) client = DurableOrchestrationClient(binding_string) client._delete_async_request = mock_request.delete with pytest.raises(Exception): await client.purge_instance_history_by( - runtime_status=[OrchestrationRuntimeStatus.Running]) + created_time_from=datetime(2000, 1, 1), + runtime_status=[OrchestrationRuntimeStatus.Completed]) + + +@pytest.mark.asyncio +async def test_purge_instance_history_by_requires_created_time_from(binding_string): + client = DurableOrchestrationClient(binding_string) + + with pytest.raises(ValueError, match="created_time_from is required"): + await client.purge_instance_history_by( + runtime_status=[OrchestrationRuntimeStatus.Completed]) @pytest.mark.asyncio