Skip to content

Commit 35f0c2a

Browse files
committed
Now requiring UUID
1 parent 3d7e218 commit 35f0c2a

8 files changed

Lines changed: 60 additions & 68 deletions

File tree

temporal-sdk/src/main/java/io/temporal/client/NexusServiceClient.java

Lines changed: 8 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -50,20 +50,9 @@ static <T> NexusServiceClient<T> newInstance(
5050
}
5151

5252
/**
53-
* Executes an operation synchronously. Equivalent to {@link #start(BiFunction, Object)} followed
54-
* by {@link NexusOperationHandle#getResult()}.
55-
*
56-
* @param operation a method reference on {@code T} identifying the operation
57-
* @param input the operation input
58-
* @return the operation result
59-
* @throws RuntimeException if the operation failed, timed out, or was cancelled
60-
*/
61-
default <U, R> R execute(BiFunction<T, U, R> operation, U input) {
62-
return start(operation, input).getResult();
63-
}
64-
65-
/**
66-
* Executes an operation synchronously with per-call options.
53+
* Executes an operation synchronously with per-call options. The supplied {@link
54+
* StartNexusOperationOptions} must have its {@code id} set; the SDK does not generate operation
55+
* IDs on the caller's behalf.
6756
*
6857
* @param operation a method reference on {@code T} identifying the operation
6958
* @param input the operation input
@@ -77,18 +66,8 @@ default <U, R> R execute(
7766
}
7867

7968
/**
80-
* Starts an operation and returns a typed handle for tracking its execution.
81-
*
82-
* @param operation a method reference on {@code T} identifying the operation
83-
* @param input the operation input
84-
* @return a typed handle bound to the started operation
85-
*/
86-
default <U, R> NexusOperationHandle<R> start(BiFunction<T, U, R> operation, U input) {
87-
return start(operation, input, StartNexusOperationOptions.getDefaultInstance());
88-
}
89-
90-
/**
91-
* Starts an operation with per-call options and returns a typed handle.
69+
* Starts an operation with per-call options and returns a typed handle. The supplied {@link
70+
* StartNexusOperationOptions} must have its {@code id} set.
9271
*
9372
* @param operation a method reference on {@code T} identifying the operation
9473
* @param input the operation input
@@ -99,18 +78,9 @@ <U, R> NexusOperationHandle<R> start(
9978
BiFunction<T, U, R> operation, U input, StartNexusOperationOptions options);
10079

10180
/**
102-
* Async variant of {@link #execute(BiFunction, Object)}. Returns a {@link CompletableFuture} that
103-
* completes with the typed result, or completes exceptionally if the operation fails.
104-
*
105-
* @param operation a method reference on {@code T} identifying the operation
106-
* @param input the operation input
107-
*/
108-
default <U, R> CompletableFuture<R> executeAsync(BiFunction<T, U, R> operation, U input) {
109-
return start(operation, input).getResultAsync();
110-
}
111-
112-
/**
113-
* Async variant of {@link #execute(BiFunction, Object, StartNexusOperationOptions)}.
81+
* Async variant of {@link #execute(BiFunction, Object, StartNexusOperationOptions)}. Returns a
82+
* {@link CompletableFuture} that completes with the typed result, or completes exceptionally if
83+
* the operation fails.
11484
*
11585
* @param operation a method reference on {@code T} identifying the operation
11686
* @param input the operation input

temporal-sdk/src/main/java/io/temporal/client/StartNexusOperationOptions.java

Lines changed: 12 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.temporal.common.SearchAttributes;
77
import java.time.Duration;
88
import java.util.Objects;
9+
import javax.annotation.Nonnull;
910
import javax.annotation.Nullable;
1011

1112
/**
@@ -23,13 +24,6 @@ public static Builder newBuilder(StartNexusOperationOptions options) {
2324
return new Builder(options);
2425
}
2526

26-
private static final StartNexusOperationOptions DEFAULT_INSTANCE = newBuilder().build();
27-
28-
/** Returns an options instance with no per-call fields set. */
29-
public static StartNexusOperationOptions getDefaultInstance() {
30-
return DEFAULT_INSTANCE;
31-
}
32-
3327
public static final class Builder {
3428
private @Nullable String id;
3529
private @Nullable Duration scheduleToCloseTimeout;
@@ -57,11 +51,11 @@ private Builder(StartNexusOperationOptions options) {
5751
}
5852

5953
/**
60-
* Required. Unique identifier for this operation within its namespace. If left null, the SDK
61-
* generates a random UUID.
54+
* Required. Unique identifier for this operation within its namespace. Callers must supply this
55+
* explicitly; the SDK does not invent one on the caller's behalf.
6256
*/
63-
public Builder setId(@Nullable String id) {
64-
this.id = id;
57+
public Builder setId(@Nonnull String id) {
58+
this.id = Objects.requireNonNull(id, "id");
6559
return this;
6660
}
6761

@@ -108,11 +102,16 @@ public Builder setIdConflictPolicy(@Nullable NexusOperationIdConflictPolicy idCo
108102
}
109103

110104
public StartNexusOperationOptions build() {
105+
if (id == null) {
106+
throw new IllegalStateException(
107+
"StartNexusOperationOptions.Builder.setId(...) must be called with a non-null id "
108+
+ "before build(); the SDK does not generate operation IDs.");
109+
}
111110
return new StartNexusOperationOptions(this);
112111
}
113112
}
114113

115-
private final @Nullable String id;
114+
private final @Nonnull String id;
116115
private final @Nullable Duration scheduleToCloseTimeout;
117116
private final @Nullable Duration scheduleToStartTimeout;
118117
private final @Nullable Duration startToCloseTimeout;
@@ -136,7 +135,7 @@ public Builder toBuilder() {
136135
return new Builder(this);
137136
}
138137

139-
@Nullable
138+
@Nonnull
140139
public String getId() {
141140
return id;
142141
}

temporal-sdk/src/main/java/io/temporal/client/UntypedNexusServiceClientImpl.java

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,7 @@ public UntypedNexusOperationHandle start(
4545
Payload payload = serializeInput(arg);
4646
StartNexusOperationExecutionInput input =
4747
new StartNexusOperationExecutionInput(
48-
endpoint,
49-
serviceName,
50-
operation,
51-
payload,
52-
options != null ? options : StartNexusOperationOptions.getDefaultInstance(),
53-
Collections.emptyMap());
48+
endpoint, serviceName, operation, payload, options, Collections.emptyMap());
5449
StartNexusOperationExecutionOutput output = invoker.startNexusOperationExecution(input);
5550
return new NexusOperationHandleImpl(
5651
output.getOperationId(), output.getRunId(), invoker, dataConverter);

temporal-sdk/src/main/java/io/temporal/internal/client/RootNexusClientInvoker.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@
2828
import io.temporal.internal.common.ProtobufTimeUtils;
2929
import io.temporal.internal.common.WorkflowExecutionUtils;
3030
import io.temporal.serviceclient.StatusUtils;
31+
import java.util.Objects;
3132
import java.util.UUID;
3233
import java.util.concurrent.CompletableFuture;
3334
import java.util.concurrent.CompletionException;
@@ -53,7 +54,8 @@ public RootNexusClientInvoker(
5354
public StartNexusOperationExecutionOutput startNexusOperationExecution(
5455
StartNexusOperationExecutionInput input) {
5556
StartNexusOperationOptions options = input.getOptions();
56-
String operationId = options.getId() != null ? options.getId() : UUID.randomUUID().toString();
57+
// The builder validates that id is non-null; this is a defense-in-depth assertion.
58+
String operationId = Objects.requireNonNull(options.getId(), "StartNexusOperationOptions.id");
5759
StartNexusOperationExecutionRequest.Builder request =
5860
StartNexusOperationExecutionRequest.newBuilder()
5961
.setNamespace(clientOptions.getNamespace())

temporal-sdk/src/test/java/io/temporal/client/nexus/NexusAsyncApiTest.java

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import io.temporal.workflow.shared.TestNexusServices;
1717
import io.temporal.workflow.shared.TestWorkflows;
1818
import java.time.Duration;
19+
import java.util.UUID;
1920
import java.util.concurrent.CompletableFuture;
2021
import java.util.concurrent.ExecutionException;
2122
import java.util.concurrent.TimeUnit;
@@ -53,7 +54,8 @@ public static void requireServerWithStandaloneNexusSupport() {
5354
public void serviceClientExecuteAsyncReturnsResult() throws Exception {
5455
String result =
5556
buildServiceClient()
56-
.executeAsync(TestNexusServices.TestNexusService1::operation, "hello")
57+
.executeAsync(
58+
TestNexusServices.TestNexusService1::operation, "hello", newOptionsWithId())
5759
.get(FUTURE_GET_TIMEOUT.getSeconds(), TimeUnit.SECONDS);
5860

5961
Assert.assertEquals("echo:hello", result);
@@ -63,6 +65,7 @@ public void serviceClientExecuteAsyncReturnsResult() throws Exception {
6365
public void serviceClientExecuteAsyncWithOptionsReturnsResult() throws Exception {
6466
StartNexusOperationOptions options =
6567
StartNexusOperationOptions.newBuilder()
68+
.setId(UUID.randomUUID().toString())
6669
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
6770
.build();
6871

@@ -79,7 +82,8 @@ public void serviceClientExecuteAsyncWithOptionsReturnsResult() throws Exception
7982
@Test
8083
public void typedHandleGetResultAsyncReturnsResult() throws Exception {
8184
NexusOperationHandle<String> handle =
82-
buildServiceClient().start(TestNexusServices.TestNexusService1::operation, "typed");
85+
buildServiceClient()
86+
.start(TestNexusServices.TestNexusService1::operation, "typed", newOptionsWithId());
8387

8488
String result = handle.getResultAsync().get(FUTURE_GET_TIMEOUT.getSeconds(), TimeUnit.SECONDS);
8589

@@ -89,7 +93,8 @@ public void typedHandleGetResultAsyncReturnsResult() throws Exception {
8993
@Test
9094
public void typedHandleGetResultAsyncWithTimeoutReturnsResult() throws Exception {
9195
NexusOperationHandle<String> handle =
92-
buildServiceClient().start(TestNexusServices.TestNexusService1::operation, "typed-tm");
96+
buildServiceClient()
97+
.start(TestNexusServices.TestNexusService1::operation, "typed-tm", newOptionsWithId());
9398

9499
String result =
95100
handle
@@ -156,7 +161,8 @@ public void executeAsyncPropagatesOperationFailure() throws Exception {
156161
buildServiceClient()
157162
.executeAsync(
158163
TestNexusServices.TestNexusService1::operation,
159-
EchoNexusServiceImpl.FAIL_PREFIX + "boom");
164+
EchoNexusServiceImpl.FAIL_PREFIX + "boom",
165+
newOptionsWithId());
160166

161167
try {
162168
future.get(FUTURE_GET_TIMEOUT.getSeconds(), TimeUnit.SECONDS);
@@ -207,7 +213,12 @@ private UntypedNexusOperationHandle startUntyped(String input) {
207213
client.newUntypedNexusServiceClient(
208214
endpoint.getSpec().getName(),
209215
TestNexusServices.TestNexusService1.class.getSimpleName());
210-
return svcClient.start("operation", StartNexusOperationOptions.newBuilder().build(), input);
216+
return svcClient.start("operation", newOptionsWithId(), input);
217+
}
218+
219+
/** Builds a minimal {@link StartNexusOperationOptions} with a unique id. */
220+
private static StartNexusOperationOptions newOptionsWithId() {
221+
return StartNexusOperationOptions.newBuilder().setId(UUID.randomUUID().toString()).build();
211222
}
212223

213224
public static class PlaceholderWorkflowImpl implements TestWorkflows.TestWorkflow1 {

temporal-sdk/src/test/java/io/temporal/client/nexus/NexusClientTest.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,7 @@ public void runStandaloneNexusOperation() throws Exception {
8282
TestNexusServices.TestNexusService1.class.getSimpleName());
8383
StartNexusOperationOptions opts =
8484
StartNexusOperationOptions.newBuilder()
85+
.setId(UUID.randomUUID().toString())
8586
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
8687
.build();
8788
UntypedNexusOperationHandle handle = svcClient.start("operation", opts, inputValue);
@@ -161,6 +162,7 @@ private String startAndAwaitSyncOperation(String label) throws Exception {
161162
TestNexusServices.TestNexusService1.class.getSimpleName());
162163
StartNexusOperationOptions opts =
163164
StartNexusOperationOptions.newBuilder()
165+
.setId(UUID.randomUUID().toString())
164166
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
165167
.build();
166168
UntypedNexusOperationHandle handle =
@@ -184,6 +186,7 @@ public void untypedExecuteByClassReturnsResult() {
184186
"operation",
185187
String.class,
186188
StartNexusOperationOptions.newBuilder()
189+
.setId(UUID.randomUUID().toString())
187190
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
188191
.build(),
189192
"untyped-exec");
@@ -209,6 +212,7 @@ public void untypedExecuteByClassAndTypeReturnsResult() {
209212
String.class,
210213
String.class,
211214
StartNexusOperationOptions.newBuilder()
215+
.setId(UUID.randomUUID().toString())
212216
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
213217
.build(),
214218
"untyped-exec-typed");

temporal-sdk/src/test/java/io/temporal/client/nexus/NexusOperationHandleTest.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -254,6 +254,7 @@ private UntypedNexusOperationHandle startOperation(
254254
TestNexusServices.TestNexusService1.class.getSimpleName());
255255
StartNexusOperationOptions opts =
256256
StartNexusOperationOptions.newBuilder()
257+
.setId(UUID.randomUUID().toString())
257258
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
258259
.build();
259260
UntypedNexusOperationHandle handle = svcClient.start("operation", opts, inputValue);

temporal-sdk/src/test/java/io/temporal/client/nexus/NexusServiceClientTest.java

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,8 @@ public void executeReturnsTypedResult() {
4242
NexusServiceClient<TestNexusServices.TestNexusService1> client =
4343
buildServiceClient(testWorkflowRule.getNexusEndpoint());
4444

45-
String result = client.execute(TestNexusServices.TestNexusService1::operation, "hello");
45+
String result =
46+
client.execute(TestNexusServices.TestNexusService1::operation, "hello", newOptionsWithId());
4647

4748
Assert.assertEquals("echo:hello", result);
4849
}
@@ -53,18 +54,19 @@ public void startReturnsTypedHandleAndPollsResult() {
5354
buildServiceClient(testWorkflowRule.getNexusEndpoint());
5455

5556
NexusOperationHandle<String> handle =
56-
client.start(TestNexusServices.TestNexusService1::operation, "world");
57+
client.start(TestNexusServices.TestNexusService1::operation, "world", newOptionsWithId());
5758

5859
Assert.assertNotNull(handle.getNexusOperationId());
5960
Assert.assertEquals("echo:world", handle.getResult());
6061
}
6162

6263
@Test
6364
public void executeWithOptionsReturnsResult() {
64-
// Covers the 3-arg execute(op, input, options) overload — the no-options variant is already
65-
// covered by executeReturnsTypedResult.
65+
// Covers the 3-arg execute(op, input, options) overload — exercises a non-default
66+
// scheduleToCloseTimeout in addition to the required id.
6667
StartNexusOperationOptions options =
6768
StartNexusOperationOptions.newBuilder()
69+
.setId(UUID.randomUUID().toString())
6870
.setScheduleToCloseTimeout(Duration.ofSeconds(30))
6971
.build();
7072

@@ -102,7 +104,10 @@ public void clientSummaryReachesServer() {
102104
buildServiceClient(testWorkflowRule.getNexusEndpoint());
103105

104106
StartNexusOperationOptions startOptions =
105-
StartNexusOperationOptions.newBuilder().setSummary("per-call-summary").build();
107+
StartNexusOperationOptions.newBuilder()
108+
.setId(UUID.randomUUID().toString())
109+
.setSummary("per-call-summary")
110+
.build();
106111
NexusOperationHandle<String> handle =
107112
client.start(TestNexusServices.TestNexusService1::operation, "world", startOptions);
108113

@@ -121,6 +126,11 @@ public void clientSummaryReachesServer() {
121126
// attribute" until the namespace's Visibility index catches up. Reintroduce once the rule
122127
// (or the test) synchronously waits for the mapping to propagate.
123128

129+
/** Builds a minimal {@link StartNexusOperationOptions} with a unique id. */
130+
private static StartNexusOperationOptions newOptionsWithId() {
131+
return StartNexusOperationOptions.newBuilder().setId(UUID.randomUUID().toString()).build();
132+
}
133+
124134
private NexusServiceClient<TestNexusServices.TestNexusService1> buildServiceClient(
125135
Endpoint endpoint) {
126136
return NexusServiceClient.newInstance(

0 commit comments

Comments
 (0)