Skip to content

Commit 358a0d0

Browse files
committed
initial commit
1 parent dd2564b commit 358a0d0

64 files changed

Lines changed: 4555 additions & 0 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

client/src/main/java/com/microsoft/durabletask/DurableTaskClient.java

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,9 @@
44

55
import javax.annotation.Nullable;
66
import java.time.Duration;
7+
import java.time.Instant;
8+
import java.util.Collection;
9+
import java.util.List;
710
import java.util.concurrent.TimeoutException;
811

912
/**
@@ -494,4 +497,46 @@ public CleanEntityStorageResult cleanEntityStorage(CleanEntityStorageRequest req
494497
}
495498

496499
// endregion
500+
501+
// region History and Instance Listing APIs
502+
503+
/**
504+
* Retrieves the full history of the specified orchestration instance.
505+
* <p>
506+
* Each history event is returned as a transport-neutral {@link OrchestrationHistoryEvent} that
507+
* decouples the public API from the underlying wire format.
508+
*
509+
* @param instanceId the orchestration instance ID
510+
* @return list of history events for the orchestration
511+
* @throws UnsupportedOperationException if this client implementation does not support history retrieval
512+
* @throws IllegalArgumentException if the instance is not found
513+
*/
514+
public List<OrchestrationHistoryEvent> getOrchestrationHistory(String instanceId) {
515+
throw new UnsupportedOperationException(
516+
this.getClass().getName() + " does not support retrieving orchestration history.");
517+
}
518+
519+
/**
520+
* Lists orchestration instance IDs that match the specified runtime status and
521+
* completed time range, using key-based pagination.
522+
*
523+
* @param runtimeStatus optional set of runtime statuses to filter by; if {@code null}, all statuses are included
524+
* @param completedTimeFrom inclusive lower bound of the orchestration completed time filter
525+
* @param completedTimeTo inclusive upper bound of the orchestration completed time filter
526+
* @param pageSize maximum number of instance IDs to return in a single page
527+
* @param lastInstanceKey continuation key from the previous page; {@code null} to start from the beginning
528+
* @return a page of orchestration instance IDs along with a continuation token
529+
* @throws UnsupportedOperationException if this client implementation does not support instance ID listing
530+
*/
531+
public InstanceIdPage listInstanceIds(
532+
@Nullable Collection<OrchestrationRuntimeStatus> runtimeStatus,
533+
@Nullable Instant completedTimeFrom,
534+
@Nullable Instant completedTimeTo,
535+
int pageSize,
536+
@Nullable String lastInstanceKey) {
537+
throw new UnsupportedOperationException(
538+
this.getClass().getName() + " does not support listing instance IDs by completed time.");
539+
}
540+
541+
// endregion
497542
}

client/src/main/java/com/microsoft/durabletask/DurableTaskGrpcClient.java

Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -411,4 +411,79 @@ public DurableEntityClient getEntities() {
411411
}
412412

413413
// endregion
414+
415+
// region History and Instance Listing APIs
416+
417+
@Override
418+
public List<OrchestrationHistoryEvent> getOrchestrationHistory(String instanceId) {
419+
Helpers.throwIfArgumentNull(instanceId, "instanceId");
420+
421+
StreamInstanceHistoryRequest request = StreamInstanceHistoryRequest.newBuilder()
422+
.setInstanceId(instanceId)
423+
.setForWorkItemProcessing(false)
424+
.build();
425+
426+
try {
427+
Iterator<HistoryChunk> chunks = this.sidecarClient.streamInstanceHistory(request);
428+
List<OrchestrationHistoryEvent> events = new ArrayList<>();
429+
while (chunks.hasNext()) {
430+
HistoryChunk chunk = chunks.next();
431+
for (HistoryEvent protoEvent : chunk.getEventsList()) {
432+
events.add(OrchestrationHistoryEventMapper.fromProto(protoEvent));
433+
}
434+
}
435+
return events;
436+
} catch (StatusRuntimeException e) {
437+
if (e.getStatus().getCode() == Status.Code.NOT_FOUND) {
438+
throw new IllegalArgumentException(
439+
"An orchestration with instanceId '" + instanceId + "' was not found.", e);
440+
}
441+
if (e.getStatus().getCode() == Status.Code.CANCELLED) {
442+
throw new IllegalStateException(
443+
"The getOrchestrationHistory operation was canceled.", e);
444+
}
445+
throw e;
446+
}
447+
}
448+
449+
@Override
450+
public InstanceIdPage listInstanceIds(
451+
@Nullable Collection<OrchestrationRuntimeStatus> runtimeStatus,
452+
@Nullable Instant completedTimeFrom,
453+
@Nullable Instant completedTimeTo,
454+
int pageSize,
455+
@Nullable String lastInstanceKey) {
456+
457+
ListInstanceIdsRequest.Builder builder = ListInstanceIdsRequest.newBuilder()
458+
.setPageSize(pageSize);
459+
460+
if (runtimeStatus != null) {
461+
for (OrchestrationRuntimeStatus status : runtimeStatus) {
462+
builder.addRuntimeStatus(OrchestrationRuntimeStatus.toProtobuf(status));
463+
}
464+
}
465+
if (completedTimeFrom != null) {
466+
builder.setCompletedTimeFrom(Timestamp.newBuilder()
467+
.setSeconds(completedTimeFrom.getEpochSecond())
468+
.setNanos(completedTimeFrom.getNano())
469+
.build());
470+
}
471+
if (completedTimeTo != null) {
472+
builder.setCompletedTimeTo(Timestamp.newBuilder()
473+
.setSeconds(completedTimeTo.getEpochSecond())
474+
.setNanos(completedTimeTo.getNano())
475+
.build());
476+
}
477+
if (lastInstanceKey != null) {
478+
builder.setLastInstanceKey(StringValue.of(lastInstanceKey));
479+
}
480+
481+
ListInstanceIdsResponse response = this.sidecarClient.listInstanceIds(builder.build());
482+
String nextKey = response.hasLastInstanceKey()
483+
? response.getLastInstanceKey().getValue()
484+
: null;
485+
return new InstanceIdPage(response.getInstanceIdsList(), nextKey);
486+
}
487+
488+
// endregion
414489
}
Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.microsoft.durabletask;
4+
5+
import javax.annotation.Nonnull;
6+
import javax.annotation.Nullable;
7+
import java.util.Collections;
8+
import java.util.List;
9+
10+
/**
11+
* Represents a single page of orchestration instance IDs returned by
12+
* {@link DurableTaskClient#listInstanceIds}.
13+
* <p>
14+
* Includes a continuation token for key-based pagination. If the continuation token is
15+
* {@code null}, there are no more results.
16+
*/
17+
public final class InstanceIdPage {
18+
19+
private final List<String> instanceIds;
20+
private final String continuationToken;
21+
22+
/**
23+
* Creates a new {@code InstanceIdPage}.
24+
*
25+
* @param instanceIds the list of instance IDs in this page
26+
* @param continuationToken the continuation token for the next page, or {@code null} if no more results
27+
*/
28+
public InstanceIdPage(
29+
@Nonnull List<String> instanceIds,
30+
@Nullable String continuationToken) {
31+
if (instanceIds == null) {
32+
throw new IllegalArgumentException("instanceIds must not be null.");
33+
}
34+
this.instanceIds = Collections.unmodifiableList(instanceIds);
35+
this.continuationToken = continuationToken;
36+
}
37+
38+
/**
39+
* Gets the list of orchestration instance IDs in this page.
40+
*
41+
* @return unmodifiable list of instance IDs
42+
*/
43+
@Nonnull
44+
public List<String> getInstanceIds() {
45+
return this.instanceIds;
46+
}
47+
48+
/**
49+
* Gets the continuation token for fetching the next page of results.
50+
*
51+
* @return the continuation token, or {@code null} if there are no more results
52+
*/
53+
@Nullable
54+
public String getContinuationToken() {
55+
return this.continuationToken;
56+
}
57+
}
Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
// Copyright (c) Microsoft Corporation. All rights reserved.
2+
// Licensed under the MIT License.
3+
package com.microsoft.durabletask;
4+
5+
import javax.annotation.Nonnull;
6+
import javax.annotation.Nullable;
7+
import java.time.Instant;
8+
import java.util.Collections;
9+
import java.util.Map;
10+
11+
/**
12+
* Transport-neutral representation of a single orchestration history event.
13+
* <p>
14+
* This type decouples the public SDK API from the underlying gRPC/protobuf transport types,
15+
* ensuring API stability even if the wire format evolves. It is returned by
16+
* {@link DurableTaskClient#getOrchestrationHistory(String)}.
17+
*/
18+
public final class OrchestrationHistoryEvent {
19+
20+
private final int eventId;
21+
private final Instant timestamp;
22+
private final String eventType;
23+
private final Map<String, Object> data;
24+
25+
/**
26+
* Creates a new {@code OrchestrationHistoryEvent}.
27+
*
28+
* @param eventId the sequence ID of the event within the orchestration history
29+
* @param timestamp the UTC timestamp when the event was recorded
30+
* @param eventType the type name of the event (e.g., "ExecutionStarted", "TaskScheduled")
31+
* @param data the event-specific data fields, or an empty map if none
32+
*/
33+
public OrchestrationHistoryEvent(
34+
int eventId,
35+
@Nonnull Instant timestamp,
36+
@Nonnull String eventType,
37+
@Nonnull Map<String, Object> data) {
38+
if (timestamp == null) {
39+
throw new IllegalArgumentException("timestamp must not be null.");
40+
}
41+
if (eventType == null || eventType.isEmpty()) {
42+
throw new IllegalArgumentException("eventType must not be null or empty.");
43+
}
44+
if (data == null) {
45+
throw new IllegalArgumentException("data must not be null.");
46+
}
47+
this.eventId = eventId;
48+
this.timestamp = timestamp;
49+
this.eventType = eventType;
50+
this.data = Collections.unmodifiableMap(data);
51+
}
52+
53+
/**
54+
* Gets the sequence ID of this event within the orchestration history.
55+
*
56+
* @return the event ID
57+
*/
58+
public int getEventId() {
59+
return this.eventId;
60+
}
61+
62+
/**
63+
* Gets the UTC timestamp when this event was recorded.
64+
*
65+
* @return the event timestamp
66+
*/
67+
@Nonnull
68+
public Instant getTimestamp() {
69+
return this.timestamp;
70+
}
71+
72+
/**
73+
* Gets the type name of this event (e.g., "ExecutionStarted", "TaskScheduled", "TaskCompleted").
74+
*
75+
* @return the event type name
76+
*/
77+
@Nonnull
78+
public String getEventType() {
79+
return this.eventType;
80+
}
81+
82+
/**
83+
* Gets the event-specific data fields as an unmodifiable map.
84+
* <p>
85+
* The keys and structure of this map depend on the event type. For example, an
86+
* "ExecutionStarted" event may contain "name", "input", and "version" fields.
87+
*
88+
* @return unmodifiable map of event data
89+
*/
90+
@Nonnull
91+
public Map<String, Object> getData() {
92+
return this.data;
93+
}
94+
}

0 commit comments

Comments
 (0)