Skip to content

Commit 61f6c53

Browse files
committed
feat(gax): add ApiCallContext and request-level settings overloads to ResumableUploadCallable
Add 2-, 3-, and 4-argument overloads to ResumableUploadCallable for futureCall and resumeCall, supporting: 1. Default context and settings (used by generated ServiceClient convenience methods). 2. Per-request ApiCallContext overrides for transport metadata (extra headers, credentials). 3. Per-request ResumableUploadCallSettings overrides for state-machine knobs per go/sdk:java-scotty-design. 4. Full method accepting both call context and settings overrides. ResumableUploadCallableImpl performs a 3-tier precedence merge on settings and merges ApiCallContext.
1 parent 6d2528d commit 61f6c53

3 files changed

Lines changed: 173 additions & 10 deletions

File tree

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/ResumableUploadCallable.java

Lines changed: 83 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -48,32 +48,109 @@ public abstract class ResumableUploadCallable<RequestT, ResponseT> {
4848
protected ResumableUploadCallable() {}
4949

5050
/**
51-
* Performs a new resumable upload asynchronously.
51+
* Performs a new resumable upload asynchronously with default call context and default settings.
5252
*
5353
* <p>The provided {@code payload} stream is consumed asynchronously by the returned {@link
5454
* ResumableUploadFuture} and will be closed automatically upon completion, failure, or
5555
* cancellation.
5656
*
5757
* @param request the request message
5858
* @param payload the data payload input stream to upload and close
59-
* @param settings call settings overrides; may be {@code null}
59+
* @return future for tracking and controlling the upload
60+
*/
61+
public ResumableUploadFuture<ResponseT> futureCall(RequestT request, InputStream payload) {
62+
return futureCall(request, payload, null, null);
63+
}
64+
65+
/**
66+
* Performs a new resumable upload asynchronously with a call context override and default
67+
* settings.
68+
*
69+
* <p>The provided {@code payload} stream is consumed asynchronously by the returned {@link
70+
* ResumableUploadFuture} and will be closed automatically upon completion, failure, or
71+
* cancellation.
72+
*
73+
* @param request the request message
74+
* @param payload the data payload input stream to upload and close
75+
* @param context call context overrides (e.g. extra headers, credentials, timeout); may be {@code
76+
* null}
77+
* @return future for tracking and controlling the upload
78+
*/
79+
public ResumableUploadFuture<ResponseT> futureCall(
80+
RequestT request, InputStream payload, @Nullable ApiCallContext context) {
81+
return futureCall(request, payload, context, null);
82+
}
83+
84+
/**
85+
* Performs a new resumable upload asynchronously with call context and settings overrides.
86+
*
87+
* <p>The provided {@code payload} stream is consumed asynchronously by the returned {@link
88+
* ResumableUploadFuture} and will be closed automatically upon completion, failure, or
89+
* cancellation.
90+
*
91+
* @param request the request message
92+
* @param payload the data payload input stream to upload and close
93+
* @param context call context overrides; may be {@code null}
94+
* @param settings request-level call settings overrides; may be {@code null}
6095
* @return future for tracking and controlling the upload
6196
*/
6297
public abstract ResumableUploadFuture<ResponseT> futureCall(
63-
RequestT request, InputStream payload, @Nullable ResumableUploadCallSettings settings);
98+
RequestT request,
99+
InputStream payload,
100+
@Nullable ApiCallContext context,
101+
@Nullable ResumableUploadCallSettings settings);
102+
103+
/**
104+
* Resumes an existing resumable upload session asynchronously with default call context and
105+
* default settings.
106+
*
107+
* <p>The provided {@code payload} stream is consumed asynchronously by the returned {@link
108+
* ResumableUploadFuture} and will be closed automatically upon completion, failure, or
109+
* cancellation.
110+
*
111+
* @param sessionUrl the upload session URL
112+
* @param payload the data payload input stream to upload and close
113+
* @return future for tracking and controlling the upload
114+
*/
115+
public ResumableUploadFuture<ResponseT> resumeCall(String sessionUrl, InputStream payload) {
116+
return resumeCall(sessionUrl, payload, null, null);
117+
}
118+
119+
/**
120+
* Resumes an existing resumable upload session asynchronously with a call context override and
121+
* default settings.
122+
*
123+
* <p>The provided {@code payload} stream is consumed asynchronously by the returned {@link
124+
* ResumableUploadFuture} and will be closed automatically upon completion, failure, or
125+
* cancellation.
126+
*
127+
* @param sessionUrl the upload session URL
128+
* @param payload the data payload input stream to upload and close
129+
* @param context call context overrides; may be {@code null}
130+
* @return future for tracking and controlling the upload
131+
*/
132+
public ResumableUploadFuture<ResponseT> resumeCall(
133+
String sessionUrl, InputStream payload, @Nullable ApiCallContext context) {
134+
return resumeCall(sessionUrl, payload, context, null);
135+
}
64136

65137
/**
66-
* Resumes an existing resumable upload session asynchronously using a saved session URL.
138+
* Resumes an existing resumable upload session asynchronously with call context and settings
139+
* overrides.
67140
*
68141
* <p>The provided {@code payload} stream is consumed asynchronously by the returned {@link
69142
* ResumableUploadFuture} and will be closed automatically upon completion, failure, or
70143
* cancellation.
71144
*
72145
* @param sessionUrl the upload session URL
73146
* @param payload the data payload input stream to upload and close
74-
* @param settings call settings overrides; may be {@code null}
147+
* @param context call context overrides; may be {@code null}
148+
* @param settings request-level call settings overrides; may be {@code null}
75149
* @return future for tracking and controlling the upload
76150
*/
77151
public abstract ResumableUploadFuture<ResponseT> resumeCall(
78-
String sessionUrl, InputStream payload, @Nullable ResumableUploadCallSettings settings);
152+
String sessionUrl,
153+
InputStream payload,
154+
@Nullable ApiCallContext context,
155+
@Nullable ResumableUploadCallSettings settings);
79156
}

sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/ResumableUploadCallableImpl.java

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,14 +75,18 @@ public ResumableUploadCallableImpl(
7575

7676
@Override
7777
public ResumableUploadFuture<ResponseT> futureCall(
78-
RequestT request, InputStream payload, @Nullable ResumableUploadCallSettings settings) {
78+
RequestT request,
79+
InputStream payload,
80+
@Nullable ApiCallContext context,
81+
@Nullable ResumableUploadCallSettings settings) {
7982
checkNotNull(request, "request must not be null");
8083
checkNotNull(payload, "payload must not be null");
8184
ResumableUploadCallSettings effectiveSettings = defaultCallSettings.merge(settings);
85+
ApiCallContext effectiveCallContext = defaultCallContext.merge(context);
8286

8387
ApiFuture<ResumableUploadSession> startFuture;
8488
try {
85-
startFuture = client.startUploadCallable().futureCall(request, defaultCallContext);
89+
startFuture = client.startUploadCallable().futureCall(request, effectiveCallContext);
8690
} catch (Throwable t) {
8791
startFuture = ApiFutures.immediateFailedFuture(t);
8892
}
@@ -92,13 +96,16 @@ public ResumableUploadFuture<ResponseT> futureCall(
9296
client.uploadChunkCallable(),
9397
payload,
9498
effectiveSettings,
95-
defaultCallContext,
99+
effectiveCallContext,
96100
executor);
97101
}
98102

99103
@Override
100104
public ResumableUploadFuture<ResponseT> resumeCall(
101-
String sessionUrl, InputStream payload, @Nullable ResumableUploadCallSettings settings) {
105+
String sessionUrl,
106+
InputStream payload,
107+
@Nullable ApiCallContext context,
108+
@Nullable ResumableUploadCallSettings settings) {
102109
throw new UnsupportedOperationException("Session resumption is not yet implemented.");
103110
}
104111
}

sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/ResumableUploadCallableImplTest.java

Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -354,6 +354,85 @@ public void execute(Runnable command) {
354354
}
355355
}
356356

357+
@Test
358+
void testUploadCallable_convenienceOverload_noContext() throws Exception {
359+
stubStartSession("https://upload.url/convenience");
360+
when(mockChunkCallable.futureCall(any(ChunkUploadRequest.class), any()))
361+
.thenReturn(ApiFutures.immediateFuture(ChunkUploadResponse.create(true, "done-conv")));
362+
363+
ResumableUploadFuture<String> future = callable.futureCall("resource-path", streamOf("data"));
364+
assertThat(future.get()).isEqualTo("done-conv");
365+
}
366+
367+
@Test
368+
void testUploadCallable_withApiCallContext_mergesAndPassesContext() throws Exception {
369+
stubStartSession("https://upload.url/context");
370+
when(mockChunkCallable.futureCall(any(ChunkUploadRequest.class), any()))
371+
.thenReturn(ApiFutures.immediateFuture(ChunkUploadResponse.create(true, "done-ctx")));
372+
373+
ApiCallContext customContext =
374+
FakeCallContext.createDefault()
375+
.withExtraHeaders(
376+
java.util.Collections.singletonMap(
377+
"X-Custom", java.util.Collections.singletonList("val")));
378+
ResumableUploadFuture<String> future =
379+
callable.futureCall("resource-path", streamOf("data"), customContext);
380+
assertThat(future.get()).isEqualTo("done-ctx");
381+
382+
ArgumentCaptor<ApiCallContext> contextCaptor = ArgumentCaptor.forClass(ApiCallContext.class);
383+
verify(mockStartCallable).futureCall(any(), contextCaptor.capture());
384+
assertThat(contextCaptor.getValue()).isNotNull();
385+
assertThat(((FakeCallContext) contextCaptor.getValue()).getExtraHeaders())
386+
.containsKey("X-Custom");
387+
}
388+
389+
@Test
390+
void testUploadCallable_withSettings_mergesAndAppliesSettings() throws Exception {
391+
stubStartSession("https://upload.url/settings");
392+
when(mockChunkCallable.futureCall(any(ChunkUploadRequest.class), any()))
393+
.thenReturn(ApiFutures.immediateFuture(ChunkUploadResponse.create(true, "done-settings")));
394+
395+
ResumableUploadCallSettings customSettings =
396+
ResumableUploadCallSettings.newBuilder().setChunkSize(16).build();
397+
398+
ResumableUploadFuture<String> future =
399+
callable.futureCall("resource-path", streamOf("data"), null, customSettings);
400+
assertThat(future.get()).isEqualTo("done-settings");
401+
}
402+
403+
@Test
404+
void testUploadCallable_withContextAndSettings_appliesBoth() throws Exception {
405+
stubStartSession("https://upload.url/ctx-settings");
406+
when(mockChunkCallable.futureCall(any(ChunkUploadRequest.class), any()))
407+
.thenReturn(ApiFutures.immediateFuture(ChunkUploadResponse.create(true, "done-both")));
408+
409+
FakeCallContext customContext = FakeCallContext.createDefault();
410+
ResumableUploadCallSettings customSettings =
411+
ResumableUploadCallSettings.newBuilder().setChunkSize(16).build();
412+
413+
ResumableUploadFuture<String> future =
414+
callable.futureCall("resource-path", streamOf("data"), customContext, customSettings);
415+
assertThat(future.get()).isEqualTo("done-both");
416+
}
417+
418+
@Test
419+
void testResumeCall_overloadsThrowUnsupported() {
420+
assertThrows(
421+
UnsupportedOperationException.class,
422+
() -> callable.resumeCall("https://upload.url", streamOf("data")));
423+
assertThrows(
424+
UnsupportedOperationException.class,
425+
() -> callable.resumeCall("https://upload.url", streamOf("data"), callContext));
426+
assertThrows(
427+
UnsupportedOperationException.class,
428+
() -> callable.resumeCall("https://upload.url", streamOf("data"), null, defaultSettings));
429+
assertThrows(
430+
UnsupportedOperationException.class,
431+
() ->
432+
callable.resumeCall(
433+
"https://upload.url", streamOf("data"), callContext, defaultSettings));
434+
}
435+
357436
private void stubStartSession(String uploadUrl) {
358437
when(mockStartCallable.futureCall(any(), any()))
359438
.thenReturn(

0 commit comments

Comments
 (0)