Skip to content

Commit 7b4070a

Browse files
authored
Merge branch 'main' into andystaples/grpc-interceptors-improvements
2 parents 2eaf8fc + f5fb2ff commit 7b4070a

9 files changed

Lines changed: 981 additions & 3 deletions

File tree

.github/copilot-instructions.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,9 +15,19 @@ building durable orchestrations. The repo contains two packages:
1515
- Update `CHANGELOG.md` for core SDK changes and
1616
`durabletask-azuremanaged/CHANGELOG.md` for provider changes.
1717
- If a change affects both packages, update both changelogs.
18+
- Include changelog entries for externally observable outcomes only, such as
19+
new public APIs, behavior changes, bug fixes users can notice, breaking
20+
changes, and new configuration capabilities.
1821
- Do NOT document internal-only changes in changelogs, including CI/workflow
1922
updates, test-only changes, refactors with no user-visible behavior change,
2023
and implementation details that do not affect public behavior or API.
24+
- When in doubt, write the changelog entry in terms of user impact (what users
25+
can now do or what behavior changed), not implementation mechanism (how it
26+
was implemented internally).
27+
28+
Examples:
29+
- Include: "Added `get_orchestration_history()` to retrieve orchestration history from the client."
30+
- Exclude: "Added internal helper functions to aggregate streamed history chunks."
2131

2232
## Language and Style
2333

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ ADDED
1616
`TaskHubGrpcClient`, `AsyncTaskHubGrpcClient`, and `TaskHubGrpcWorker` to
1717
support pre-configured channel passthrough and low-level gRPC channel
1818
customization.
19+
- Added `get_orchestration_history()` and `list_instance_ids()` to the sync and async gRPC clients.
20+
- Added in-memory backend support for `StreamInstanceHistory` and `ListInstanceIds` so local orchestration tests can retrieve history and page terminal instance IDs by completion window.
1921

2022
## v1.4.0
2123

durabletask/client.py

Lines changed: 86 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,15 +6,17 @@
66
from dataclasses import dataclass
77
from datetime import datetime
88
from enum import Enum
9-
from typing import Any, List, Optional, Sequence, TypeVar, Union
9+
from typing import Any, Generic, List, Optional, Sequence, TypeVar, Union
1010

1111
import grpc
1212
import grpc.aio
1313

14+
import durabletask.history as history
1415
from durabletask.entities import EntityInstanceId
1516
from durabletask.entities.entity_metadata import EntityMetadata
1617
from durabletask.grpc_options import GrpcChannelOptions
1718
import durabletask.internal.helpers as helpers
19+
import durabletask.internal.history_helpers as history_helpers
1820
import durabletask.internal.orchestrator_service_pb2 as pb
1921
import durabletask.internal.orchestrator_service_pb2_grpc as stubs
2022
import durabletask.internal.shared as shared
@@ -38,6 +40,7 @@
3840

3941
TInput = TypeVar('TInput')
4042
TOutput = TypeVar('TOutput')
43+
TItem = TypeVar('TItem')
4144

4245

4346
class OrchestrationStatus(Enum):
@@ -100,6 +103,12 @@ class PurgeInstancesResult:
100103
is_complete: bool
101104

102105

106+
@dataclass
107+
class Page(Generic[TItem]):
108+
items: List[TItem]
109+
continuation_token: Optional[str]
110+
111+
103112
@dataclass
104113
class CleanEntityStorageResult:
105114
empty_entities_removed: int
@@ -230,6 +239,44 @@ def get_orchestration_state(self, instance_id: str, *, fetch_payloads: bool = Tr
230239
payload_helpers.deexternalize_payloads(res, self._payload_store)
231240
return new_orchestration_state(req.instanceId, res)
232241

242+
def get_orchestration_history(self,
243+
instance_id: str, *,
244+
execution_id: Optional[str] = None,
245+
for_work_item_processing: bool = False) -> List[history.HistoryEvent]:
246+
req = pb.StreamInstanceHistoryRequest(
247+
instanceId=instance_id,
248+
executionId=helpers.get_string_value(execution_id),
249+
forWorkItemProcessing=for_work_item_processing,
250+
)
251+
self._logger.info(f"Retrieving history for instance '{instance_id}'.")
252+
stream = self._stub.StreamInstanceHistory(req)
253+
return history_helpers.collect_history_events(stream, self._payload_store)
254+
255+
def list_instance_ids(self,
256+
runtime_status: Optional[List[OrchestrationStatus]] = None,
257+
completed_time_from: Optional[datetime] = None,
258+
completed_time_to: Optional[datetime] = None,
259+
page_size: Optional[int] = None,
260+
continuation_token: Optional[str] = None) -> Page[str]:
261+
req = pb.ListInstanceIdsRequest(
262+
runtimeStatus=[status.value for status in runtime_status] if runtime_status else [],
263+
completedTimeFrom=helpers.new_timestamp(completed_time_from) if completed_time_from else None,
264+
completedTimeTo=helpers.new_timestamp(completed_time_to) if completed_time_to else None,
265+
pageSize=page_size or 0,
266+
lastInstanceKey=helpers.get_string_value(continuation_token),
267+
)
268+
self._logger.info(
269+
"Listing terminal instance IDs with filters: "
270+
f"runtime_status={[str(status) for status in runtime_status] if runtime_status else None}, "
271+
f"completed_time_from={completed_time_from}, "
272+
f"completed_time_to={completed_time_to}, "
273+
f"page_size={page_size}, "
274+
f"continuation_token={continuation_token}"
275+
)
276+
resp: pb.ListInstanceIdsResponse = self._stub.ListInstanceIds(req)
277+
next_token = resp.lastInstanceKey.value if resp.HasField("lastInstanceKey") else None
278+
return Page(items=list(resp.instanceIds), continuation_token=next_token)
279+
233280
def get_all_orchestration_states(self,
234281
orchestration_query: Optional[OrchestrationQuery] = None
235282
) -> List[OrchestrationState]:
@@ -525,6 +572,44 @@ async def get_orchestration_state(self, instance_id: str, *,
525572
await payload_helpers.deexternalize_payloads_async(res, self._payload_store)
526573
return new_orchestration_state(req.instanceId, res)
527574

575+
async def get_orchestration_history(self,
576+
instance_id: str, *,
577+
execution_id: Optional[str] = None,
578+
for_work_item_processing: bool = False) -> List[history.HistoryEvent]:
579+
req = pb.StreamInstanceHistoryRequest(
580+
instanceId=instance_id,
581+
executionId=helpers.get_string_value(execution_id),
582+
forWorkItemProcessing=for_work_item_processing,
583+
)
584+
self._logger.info(f"Retrieving history for instance '{instance_id}'.")
585+
stream = self._stub.StreamInstanceHistory(req)
586+
return await history_helpers.collect_history_events_async(stream, self._payload_store)
587+
588+
async def list_instance_ids(self,
589+
runtime_status: Optional[List[OrchestrationStatus]] = None,
590+
completed_time_from: Optional[datetime] = None,
591+
completed_time_to: Optional[datetime] = None,
592+
page_size: Optional[int] = None,
593+
continuation_token: Optional[str] = None) -> Page[str]:
594+
req = pb.ListInstanceIdsRequest(
595+
runtimeStatus=[status.value for status in runtime_status] if runtime_status else [],
596+
completedTimeFrom=helpers.new_timestamp(completed_time_from) if completed_time_from else None,
597+
completedTimeTo=helpers.new_timestamp(completed_time_to) if completed_time_to else None,
598+
pageSize=page_size or 0,
599+
lastInstanceKey=helpers.get_string_value(continuation_token),
600+
)
601+
self._logger.info(
602+
"Listing terminal instance IDs with filters: "
603+
f"runtime_status={[str(status) for status in runtime_status] if runtime_status else None}, "
604+
f"completed_time_from={completed_time_from}, "
605+
f"completed_time_to={completed_time_to}, "
606+
f"page_size={page_size}, "
607+
f"continuation_token={continuation_token}"
608+
)
609+
resp: pb.ListInstanceIdsResponse = await self._stub.ListInstanceIds(req)
610+
next_token = resp.lastInstanceKey.value if resp.HasField("lastInstanceKey") else None
611+
return Page(items=list(resp.instanceIds), continuation_token=next_token)
612+
528613
async def get_all_orchestration_states(self,
529614
orchestration_query: Optional[OrchestrationQuery] = None
530615
) -> List[OrchestrationState]:

0 commit comments

Comments
 (0)