Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -48,19 +48,41 @@ public abstract class ResumableUploadCallable<RequestT, ResponseT> {
protected ResumableUploadCallable() {}

/**
* Performs a new resumable upload asynchronously.
* Performs a new resumable upload asynchronously with settings overrides and default call
* context.
*
* <p>The provided {@code payload} stream is consumed asynchronously by the returned {@link
* ResumableUploadFuture} and will be closed automatically upon completion, failure, or
* cancellation.
*
* @param request the request message
* @param payload the data payload input stream to upload and close
* @param settings call settings overrides; may be {@code null}
* @param settings request-level call settings overrides; may be {@code null}
* @return future for tracking and controlling the upload
*/
public ResumableUploadFuture<ResponseT> futureCall(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm leaning towards only expose two methods for now.
futureCall(RequestT request, InputStream payload, @Nullable ResumableUploadCallSettings settings)
and
futureCall(RequestT request, InputStream payload, @Nullable ApiCallContext context, @Nullable ResumableUploadCallSettings settings).

This is inline with other callables that most of them expose two methods. One with ApiCallContext, one without it. We could try to expose more at the generated client level later.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

RequestT request, InputStream payload, @Nullable ResumableUploadCallSettings settings) {
return futureCall(request, payload, null, settings);
}

/**
* Performs a new resumable upload asynchronously with call context and settings overrides.
*
* <p>The provided {@code payload} stream is consumed asynchronously by the returned {@link
* ResumableUploadFuture} and will be closed automatically upon completion, failure, or
* cancellation.
*
* @param request the request message
* @param payload the data payload input stream to upload and close
* @param context call context overrides; may be {@code null}
* @param settings request-level call settings overrides; may be {@code null}
* @return future for tracking and controlling the upload
*/
public abstract ResumableUploadFuture<ResponseT> futureCall(
RequestT request, InputStream payload, @Nullable ResumableUploadCallSettings settings);
RequestT request,
InputStream payload,
@Nullable ApiCallContext context,
@Nullable ResumableUploadCallSettings settings);
Comment on lines 81 to +85

This comment was marked as outdated.


/**
* Resumes an existing resumable upload session asynchronously using a saved session URL.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,15 +70,18 @@

@Override
public ResumableUploadFuture<ResponseT> futureCall(
RequestT request, InputStream payload, @Nullable ResumableUploadCallSettings settings) {
RequestT request,
InputStream payload,
@Nullable ApiCallContext context,
@Nullable ResumableUploadCallSettings settings) {
checkNotNull(request, "request must not be null");
checkNotNull(payload, "payload must not be null");
ResumableUploadCallSettings effectiveSettings = defaultCallSettings.merge(settings);
ApiCallContext effectiveCallContext = clientContext.getDefaultCallContext().merge(context);

Check warning on line 80 in sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/ResumableUploadCallableImpl.java

View check run for this annotation

SonarQubeCloud / [gapic-generator-java-root] SonarCloud Code Analysis

Annotate the parameter with @javax.annotation.Nullable in method 'merge' declaration, or make sure that null can not be passed as argument.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-cloud-java_showcase&issues=AaCI68VciYxSFXV9CG2-&open=AaCI68VciYxSFXV9CG2-&pullRequest=14251

ApiFuture<ResumableUploadSession> startFuture;
try {
startFuture =
client.startUploadCallable().futureCall(request, clientContext.getDefaultCallContext());
startFuture = client.startUploadCallable().futureCall(request, effectiveCallContext);
} catch (Throwable t) {
startFuture = ApiFutures.immediateFailedFuture(t);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -313,6 +313,87 @@
assertThat(stream.closed).isTrue();
}

@Test
void testUploadCallable_withApiCallContext_mergesAndPassesContext() throws Exception {
stubStartSession("https://upload.url/context");
when(mockChunkCallable.futureCall(any(ChunkUploadRequest.class), any()))
.thenReturn(ApiFutures.immediateFuture(ChunkUploadResponse.create(true, "done-ctx")));

ApiCallContext customContext =
FakeCallContext.createDefault()
.withExtraHeaders(
java.util.Collections.singletonMap(
"X-Custom", java.util.Collections.singletonList("val")));
ResumableUploadFuture<String> future =
callable.futureCall("resource-path", streamOf("data"), customContext, null);
assertThat(future.get()).isEqualTo("done-ctx");

ArgumentCaptor<ApiCallContext> startContextCaptor =
ArgumentCaptor.forClass(ApiCallContext.class);
verify(mockStartCallable).futureCall(any(), startContextCaptor.capture());
assertThat(startContextCaptor.getValue()).isNotNull();
assertThat(((FakeCallContext) startContextCaptor.getValue()).getExtraHeaders())
.containsKey("X-Custom");

ArgumentCaptor<ApiCallContext> chunkContextCaptor =
ArgumentCaptor.forClass(ApiCallContext.class);
verify(mockChunkCallable).futureCall(any(), chunkContextCaptor.capture());
assertThat(chunkContextCaptor.getValue()).isNotNull();
assertThat(((FakeCallContext) chunkContextCaptor.getValue()).getExtraHeaders())
.doesNotContainKey("X-Custom");
}

@Test
void testUploadCallable_withSettings_mergesAndAppliesSettings() throws Exception {
stubStartSession("https://upload.url/settings");
when(mockChunkCallable.futureCall(any(ChunkUploadRequest.class), any()))
.thenReturn(ApiFutures.immediateFuture(ChunkUploadResponse.create(true, "done-settings")));

ResumableUploadCallSettings customSettings =
ResumableUploadCallSettings.newBuilder().setChunkSize(16).build();

ResumableUploadFuture<String> future =
callable.futureCall("resource-path", streamOf("data"), null, customSettings);
assertThat(future.get()).isEqualTo("done-settings");
}

@Test
void testUploadCallable_withSettings_delegatesWithNullContext() throws Exception {
stubStartSession("https://upload.url/settings-convenience");
when(mockChunkCallable.futureCall(any(ChunkUploadRequest.class), any()))
.thenReturn(
ApiFutures.immediateFuture(ChunkUploadResponse.create(true, "done-settings-conv")));

ResumableUploadCallSettings customSettings =
ResumableUploadCallSettings.newBuilder().setChunkSize(16).build();

ResumableUploadFuture<String> future =
callable.futureCall("resource-path", streamOf("data"), customSettings);
assertThat(future.get()).isEqualTo("done-settings-conv");
}

@Test
void testUploadCallable_withContextAndSettings_appliesBoth() throws Exception {
stubStartSession("https://upload.url/ctx-settings");
when(mockChunkCallable.futureCall(any(ChunkUploadRequest.class), any()))
.thenReturn(ApiFutures.immediateFuture(ChunkUploadResponse.create(true, "done-both")));

FakeCallContext customContext = FakeCallContext.createDefault();
ResumableUploadCallSettings customSettings =
ResumableUploadCallSettings.newBuilder().setChunkSize(16).build();

ResumableUploadFuture<String> future =
callable.futureCall("resource-path", streamOf("data"), customContext, customSettings);
assertThat(future.get()).isEqualTo("done-both");
}

@Test
void testResumeCall_throwsUnsupportedOperationException() {
assertThrows(

Check warning on line 392 in sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/ResumableUploadCallableImplTest.java

View check run for this annotation

SonarQubeCloud / [gapic-generator-java-root] SonarCloud Code Analysis

Refactor the code of the lambda to have only one invocation possibly throwing a runtime exception.

See more on https://sonarcloud.io/project/issues?id=googleapis_google-cloud-java_showcase&issues=AaBu0MUVwtYwlDuGFbrx&open=AaBu0MUVwtYwlDuGFbrx&pullRequest=14251
UnsupportedOperationException.class,
() -> callable.resumeCall("https://upload.url/session", streamOf("data"), null));
}

private void stubStartSession(String uploadUrl) {
when(mockStartCallable.futureCall(any(), any()))
.thenReturn(
Expand Down
Loading