From 00173b20c4446b366a4c029c20970e771e204cc3 Mon Sep 17 00:00:00 2001 From: whowes Date: Wed, 2 Sep 2026 16:21:28 +0000 Subject: [PATCH] feat(gax): add ResumableUploadCallable creation to Callables and HttpJsonCallableFactory --- .../gax/httpjson/HttpJsonCallableFactory.java | 25 ++++++++++++++ .../httpjson/HttpJsonCallableFactoryTest.java | 33 +++++++++++++++++++ .../com/google/api/gax/rpc/Callables.java | 21 ++++++++++++ .../com/google/api/gax/rpc/CallableTest.java | 19 +++++++++++ 4 files changed, 98 insertions(+) diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java index c0ea2599a54c..07c03017f5ce 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonCallableFactory.java @@ -29,8 +29,10 @@ */ package com.google.api.gax.httpjson; +import com.google.api.core.BetaApi; import com.google.api.core.ObsoleteApi; import com.google.api.gax.longrunning.OperationSnapshot; +import com.google.api.gax.resumable.ResumableUploadClient; import com.google.api.gax.rpc.BatchingCallSettings; import com.google.api.gax.rpc.Callables; import com.google.api.gax.rpc.ClientContext; @@ -39,6 +41,8 @@ import com.google.api.gax.rpc.OperationCallSettings; import com.google.api.gax.rpc.OperationCallable; import com.google.api.gax.rpc.PagedCallSettings; +import com.google.api.gax.rpc.ResumableUploadCallSettings; +import com.google.api.gax.rpc.ResumableUploadCallable; import com.google.api.gax.rpc.ServerStreamingCallSettings; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallSettings; @@ -221,6 +225,27 @@ ServerStreamingCallable createServerStreamingCallable( return callable.withDefaultCallContext(clientContext.getDefaultCallContext()); } + /** + * Creates a {@link ResumableUploadCallable} to execute resumable uploads. Designed for use by + * generated code. + * + * @param httpJsonCallSettings the http/json call settings + * @param callSettings settings configuring chunk size + * @param clientContext client context providing default call context + * @return {@link ResumableUploadCallable} callable object + */ + @BetaApi + public static + ResumableUploadCallable createResumableUploadCallable( + HttpJsonCallSettings httpJsonCallSettings, + ResumableUploadCallSettings callSettings, + ClientContext clientContext) { + ResumableUploadClient uploadClient = + HttpJsonResumableUploadClient.create( + clientContext, httpJsonCallSettings.getMethodDescriptor()); + return Callables.resumableUpload(uploadClient, callSettings, clientContext); + } + static ApiTracerContext getApiTracerContext(ApiMethodDescriptor methodDescriptor) { return ApiTracerContext.newBuilder() .setFullMethodName(methodDescriptor.getFullMethodName()) diff --git a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonCallableFactoryTest.java b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonCallableFactoryTest.java index d205db3ad1de..a90401a73c75 100644 --- a/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonCallableFactoryTest.java +++ b/sdk-platform-java/gax-java/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonCallableFactoryTest.java @@ -34,6 +34,9 @@ import static org.mockito.Mockito.mock; import com.google.api.client.http.HttpMethods; +import com.google.api.gax.rpc.ClientContext; +import com.google.api.gax.rpc.ResumableUploadCallSettings; +import com.google.api.gax.rpc.ResumableUploadCallable; import com.google.api.gax.tracing.ApiTracerContext; import com.google.api.gax.tracing.SpanName; import com.google.api.pathtemplate.PathTemplate; @@ -107,4 +110,34 @@ void testGetApiTracerContextInvalid() { assertThat(actualError).isNotNull(); } } + + @Test + void testCreateResumableUploadCallable() { + @SuppressWarnings("unchecked") + ApiMethodDescriptor descriptor = + ApiMethodDescriptor.newBuilder() + .setFullMethodName("test/upload") + .setHttpMethod(HttpMethods.POST) + .setRequestFormatter(createMockRequestFormatter()) + .setResponseParser( + mock(HttpResponseParser.class, Mockito.withSettings().withoutAnnotations())) + .build(); + + HttpJsonCallSettings httpJsonCallSettings = + HttpJsonCallSettings.newBuilder().setMethodDescriptor(descriptor).build(); + + ResumableUploadCallSettings callSettings = + ResumableUploadCallSettings.newBuilder().setChunkSize(256 * 1024).build(); + + ClientContext clientContext = + ClientContext.newBuilder() + .setDefaultCallContext(HttpJsonCallContext.createDefault()) + .build(); + + ResumableUploadCallable callable = + HttpJsonCallableFactory.createResumableUploadCallable( + httpJsonCallSettings, callSettings, clientContext); + + assertThat(callable).isNotNull(); + } } diff --git a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/Callables.java b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/Callables.java index 17a7ff6b55e3..d819cda8fe0e 100644 --- a/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/Callables.java +++ b/sdk-platform-java/gax-java/gax/src/main/java/com/google/api/gax/rpc/Callables.java @@ -33,6 +33,7 @@ import com.google.api.gax.batching.BatchingSettings; import com.google.api.gax.longrunning.OperationResponsePollAlgorithm; import com.google.api.gax.longrunning.OperationSnapshot; +import com.google.api.gax.resumable.ResumableUploadClient; import com.google.api.gax.retrying.ExponentialRetryAlgorithm; import com.google.api.gax.retrying.RetryAlgorithm; import com.google.api.gax.retrying.RetrySettings; @@ -270,6 +271,26 @@ OperationCallableImpl longRunningOperationImpl( initialCallable, scheduler, longRunningClient, operationCallSettings); } + /** + * Creates a {@link ResumableUploadCallable} to execute resumable uploads. Designed for use by + * generated code. + * + * @param uploadClient client executing the wire-level upload protocol + * @param callSettings settings configuring chunk size + * @param clientContext client context providing default call context and executor + * @return {@link ResumableUploadCallable} callable object + */ + public static ResumableUploadCallable resumableUpload( + ResumableUploadClient uploadClient, + ResumableUploadCallSettings callSettings, + ClientContext clientContext) { + return new ResumableUploadCallableImpl<>( + uploadClient, + callSettings, + clientContext.getDefaultCallContext(), + clientContext.getExecutor()); + } + private static boolean areRetriesDisabled( Collection retryableCodes, RetrySettings retrySettings) { return retrySettings.getMaxAttempts() == 1 diff --git a/sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/CallableTest.java b/sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/CallableTest.java index fcdab36d9a6d..5fa732290819 100644 --- a/sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/CallableTest.java +++ b/sdk-platform-java/gax-java/gax/src/test/java/com/google/api/gax/rpc/CallableTest.java @@ -30,6 +30,7 @@ package com.google.api.gax.rpc; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.mockito.ArgumentMatchers.any; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; @@ -42,6 +43,7 @@ import com.google.api.core.ApiClock; import com.google.api.core.ApiFuture; import com.google.api.core.SettableApiFuture; +import com.google.api.gax.resumable.ResumableUploadClient; import com.google.api.gax.retrying.RetrySettings; import com.google.api.gax.rpc.testing.FakeCallContext; import java.util.concurrent.ScheduledExecutorService; @@ -207,4 +209,21 @@ void testWatched_usesJavaTimeMethods() { verify(callContext, atLeastOnce()).withStreamIdleTimeoutDuration(eq(timeout)); verify(callContext, atLeastOnce()).withStreamWaitTimeoutDuration(eq(timeout)); } + + @Test + void testResumableUploadCallable() { + @SuppressWarnings("unchecked") + ResumableUploadClient uploadClient = + mock(ResumableUploadClient.class, Mockito.withSettings().withoutAnnotations()); + ResumableUploadCallSettings settings = + ResumableUploadCallSettings.newBuilder().setChunkSize(1024).build(); + ScheduledExecutorService executor = + mock(ScheduledExecutorService.class, Mockito.withSettings().withoutAnnotations()); + ClientContext context = clientContext.toBuilder().setExecutor(executor).build(); + + ResumableUploadCallable callable = + Callables.resumableUpload(uploadClient, settings, context); + + assertNotNull(callable); + } }