diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java index 54e3086371f8..e7092bbcc28c 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java @@ -33,6 +33,7 @@ import java.util.Optional; import java.util.stream.Collectors; import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; @NullMarked public class ServiceClientCommentComposer { @@ -40,6 +41,11 @@ public class ServiceClientCommentComposer { private static final String EMPTY_STRING = ""; private static final String API_EXCEPTION_TYPE_NAME = "com.google.api.gax.rpc.ApiException"; private static final String EXCEPTION_CONDITION = "if the remote call fails"; + private static final String REQUEST_PARAM_NAME = "request"; + private static final String REQUEST_PARAM_DESCRIPTION = + "The request object containing all of the parameters for the API call."; + private static final String PAYLOAD_PARAM_NAME = "payload"; + private static final String PAYLOAD_PARAM_DESCRIPTION = "The payload data stream to upload."; // Constants. private static final String SERVICE_DESCRIPTION_INTRO_STRING = @@ -93,6 +99,11 @@ public class ServiceClientCommentComposer { + " that it is easy to make a subclass, but otherwise, the static factory methods" + " should be preferred."; + private static final String RESUMABLE_UPLOAD_CALL_CONTEXT_WARNING = + "Call context overrides (such as withTimeout, withRetrySettings, or credentials) apply" + + " strictly to the start request (session initiation). Per-chunk PUT calls rely on" + + " the configured timeout and retry settings from ResumableUploadCallSettings."; + // Comments. public static final CommentStatement GET_OPERATIONS_CLIENT_METHOD_COMMENT = toSimpleComment( @@ -105,9 +116,9 @@ public static List createClassHeaderComments( String classMethodSampleCode, String credentialsSampleCode, String endpointSampleCode, - String transportSampleCode, - String primaryTransport, - String secondaryTransport) { + @Nullable String transportSampleCode, + @Nullable String primaryTransport, + @Nullable String secondaryTransport) { JavaDocComment.Builder classHeaderJavadocBuilder = JavaDocComment.builder(); if (service.hasDescription()) { String descriptionComment = @@ -187,14 +198,17 @@ public static List createRpcMethodHeaderComment( methodJavadocBuilder = methodJavadocBuilder.addUnescapedComment(descriptionComment); } - if (sampleCodeOpt.isPresent()) { + if (method.isResumableUpload()) { + methodJavadocBuilder.addParagraph(RESUMABLE_UPLOAD_CALL_CONTEXT_WARNING); + } + + if (sampleCodeOpt.isPresent() && !method.isResumableUpload()) { methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING); methodJavadocBuilder.addSampleCode(sampleCodeOpt.get()); } if (methodArguments.isEmpty()) { - methodJavadocBuilder.addParam( - "request", "The request object containing all of the parameters for the API call."); + methodJavadocBuilder.addParam(REQUEST_PARAM_NAME, REQUEST_PARAM_DESCRIPTION); } else { for (MethodArgument argument : methodArguments) { // TODO(miraleung): Remove the newline replacement when we support CommonMark. @@ -204,6 +218,10 @@ public static List createRpcMethodHeaderComment( } } + if (method.isResumableUpload()) { + methodJavadocBuilder.addParam(PAYLOAD_PARAM_NAME, PAYLOAD_PARAM_DESCRIPTION); + } + methodJavadocBuilder.setThrows(API_EXCEPTION_TYPE_NAME, EXCEPTION_CONDITION); if (method.isDeprecated()) { @@ -233,13 +251,17 @@ private static MethodAndVariants createMethodAndVariants( private static String createTableOfMethods(List methodAndVariantsList) { String FLATTENED_METHODS = - "

\"Flattened\" method variants have converted the fields of the request object into function parameters to enable multiple ways to call the same method.

\n"; + "

\"Flattened\" method variants have converted the fields of the request object into" + + " function parameters to enable multiple ways to call the same method.

\n"; String REQUEST_OBJECT_METHODS = - "

Request object method variants only take one parameter, a request object, which must be constructed before the call.

\n"; + "

Request object method variants only take one parameter, a request object, which must be" + + " constructed before the call.

\n"; String CALLABLE_METHODS = - "

Callable method variants take no parameters and return an immutable API callable object, which can be used to initiate calls to the service.

\n"; + "

Callable method variants take no parameters and return an immutable API callable" + + " object, which can be used to initiate calls to the service.

\n"; String ASYNC_METHODS = - "

Methods that return long-running operations have \"Async\" method variants that return `OperationFuture`, which is used to track polling of the service.

\n"; + "

Methods that return long-running operations have \"Async\" method variants that return" + + " `OperationFuture`, which is used to track polling of the service.

\n"; StringBuilder tableBuilder = new StringBuilder(); tableBuilder @@ -348,8 +370,12 @@ public static List createRpcCallableMethodHeaderComment( methodJavadocBuilder = methodJavadocBuilder.addUnescapedComment(descriptionComment); } - methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING); - if (sampleCodeOpt.isPresent()) { + if (method.isResumableUpload()) { + methodJavadocBuilder.addParagraph(RESUMABLE_UPLOAD_CALL_CONTEXT_WARNING); + } + + if (sampleCodeOpt.isPresent() && !method.isResumableUpload()) { + methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING); methodJavadocBuilder.addSampleCode(sampleCodeOpt.get()); } diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/SettingsCommentComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/SettingsCommentComposer.java index f4eef98019cd..44c523cef284 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/SettingsCommentComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/SettingsCommentComposer.java @@ -136,21 +136,14 @@ public static CommentStatement createCallSettingsGetterComment( isMethodInternal); } + private static final String RESUMABLE_UPLOAD_CALL_SETTINGS_DOC_NOTE = + "Note that custom retry settings and headers configured via ApiCallContext" + + " apply strictly to the initial session initiation request."; + public static CommentStatement createResumableUploadCallSettingsGetterComment( String javaMethodName, boolean isMethodDeprecated, boolean isMethodInternal) { - JavaDocComment.Builder docBuilder = - JavaDocComment.builder() - .addComment(String.format(CALL_SETTINGS_METHOD_DOC_PATTERN, javaMethodName)) - .addParagraph( - "Note that custom retry settings and headers configured via ApiCallContext" - + " apply strictly to the initial session initiation request."); - if (isMethodDeprecated) { - docBuilder.setDeprecated(CommentComposer.DEPRECATED_METHOD_STRING); - } - if (isMethodInternal) { - docBuilder.setInternalOnly(CommentComposer.INTERNAL_ONLY_METHOD_STRING); - } - return CommentStatement.withComment(docBuilder.build()); + return createResumableUploadCallSettingsComment( + CALL_SETTINGS_METHOD_DOC_PATTERN, javaMethodName, isMethodDeprecated, isMethodInternal); } public static CommentStatement createBuilderClassComment(String outerClassName) { @@ -165,12 +158,19 @@ public static CommentStatement createCallSettingsBuilderGetterComment( public static CommentStatement createResumableUploadCallSettingsBuilderGetterComment( String javaMethodName, boolean isMethodDeprecated, boolean isMethodInternal) { + return createResumableUploadCallSettingsComment( + CALL_SETTINGS_BUILDER_METHOD_DOC_PATTERN, + javaMethodName, + isMethodDeprecated, + isMethodInternal); + } + + private static CommentStatement createResumableUploadCallSettingsComment( + String pattern, String javaMethodName, boolean isMethodDeprecated, boolean isMethodInternal) { JavaDocComment.Builder docBuilder = JavaDocComment.builder() - .addComment(String.format(CALL_SETTINGS_BUILDER_METHOD_DOC_PATTERN, javaMethodName)) - .addParagraph( - "Note that custom retry settings and headers configured via ApiCallContext" - + " apply strictly to the initial session initiation request."); + .addComment(String.format(pattern, javaMethodName)) + .addParagraph(RESUMABLE_UPLOAD_CALL_SETTINGS_DOC_NOTE); if (isMethodDeprecated) { docBuilder.setDeprecated(CommentComposer.DEPRECATED_METHOD_STRING); } diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientClassComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientClassComposer.java index ec32a4649596..2287f8410e0f 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientClassComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientClassComposer.java @@ -24,10 +24,12 @@ import com.google.api.gax.paging.AbstractFixedSizeCollection; import com.google.api.gax.paging.AbstractPage; import com.google.api.gax.paging.AbstractPagedListResponse; +import com.google.api.gax.rpc.ApiExceptions; import com.google.api.gax.rpc.BidiStreamingCallable; import com.google.api.gax.rpc.ClientStreamingCallable; import com.google.api.gax.rpc.OperationCallable; import com.google.api.gax.rpc.PageContext; +import com.google.api.gax.rpc.ResumableUploadCallable; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.api.generator.engine.ast.AnnotationNode; @@ -88,6 +90,7 @@ import com.google.longrunning.Operation; import com.google.rpc.Status; import java.io.IOException; +import java.io.InputStream; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -109,6 +112,7 @@ public abstract class AbstractServiceClientClassComposer implements ClassCompose private static final String CALLABLE_NAME_PATTERN = "%sCallable"; private static final String PAGED_CALLABLE_NAME_PATTERN = "%sPagedCallable"; private static final String OPERATION_CALLABLE_NAME_PATTERN = "%sOperationCallable"; + private static final String REQUEST_VAR_NAME = "request"; private static final Reference LIST_REFERENCE = ConcreteReference.withClazz(List.class); private static final Reference MAP_REFERENCE = ConcreteReference.withClazz(Map.class); @@ -650,42 +654,46 @@ private static List createServiceMethods( methodVariantsForClientHeader.put(method.name(), new ArrayList<>()); } if (method.stream().equals(Stream.NONE)) { - List generatedMethods = - createMethodVariants( - method, - ClassNames.getServiceClientClassName(service), - messageTypes, - typeStore, - resourceNames, - samples, - service); - - // Collect data for gapic_metadata.json. - grpcRpcToJavaMethodMetadata - .get(method.name()) - .addAll( - generatedMethods.stream() - .map(m -> javaMethodNameFn.apply(m)) - .collect(Collectors.toList())); - - // Collect data for Client header - methodVariantsForClientHeader - .get(method.name()) - .addAll( - generatedMethods.stream() - .map(AbstractServiceClientClassComposer::getJavaMethod) - .collect(Collectors.toList())); - javaMethods.addAll(generatedMethods); + if (!method.isResumableUpload()) { + List generatedMethods = + createMethodVariants( + method, + ClassNames.getServiceClientClassName(service), + messageTypes, + typeStore, + resourceNames, + samples, + service); + + // Collect data for gapic_metadata.json. + grpcRpcToJavaMethodMetadata + .get(method.name()) + .addAll( + generatedMethods.stream() + .map(m -> javaMethodNameFn.apply(m)) + .collect(Collectors.toList())); + + // Collect data for Client header + methodVariantsForClientHeader + .get(method.name()) + .addAll( + generatedMethods.stream() + .map(AbstractServiceClientClassComposer::getJavaMethod) + .collect(Collectors.toList())); + javaMethods.addAll(generatedMethods); + } MethodDefinition generatedMethod = - createMethodDefaultMethod( - method, - ClassNames.getServiceClientClassName(service), - messageTypes, - typeStore, - resourceNames, - samples, - service); + method.isResumableUpload() + ? createResumableUploadDefaultMethod(method, typeStore) + : createMethodDefaultMethod( + method, + ClassNames.getServiceClientClassName(service), + messageTypes, + typeStore, + resourceNames, + samples, + service); // Collect data for gapic_metadata.json and client header. grpcRpcToJavaMethodMetadata.get(method.name()).add(javaMethodNameFn.apply(generatedMethod)); @@ -778,7 +786,8 @@ private static List createMethodVariants( // Request proto builder. VariableExpr requestVarExpr = VariableExpr.builder() - .setVariable(Variable.builder().setName("request").setType(methodInputType).build()) + .setVariable( + Variable.builder().setName(REQUEST_VAR_NAME).setType(methodInputType).build()) .setIsDecl(true) .build(); @@ -873,7 +882,8 @@ private static MethodDefinition createMethodDefaultMethod( // Construct the method that accepts a request proto. VariableExpr requestArgVarExpr = VariableExpr.builder() - .setVariable(Variable.builder().setName("request").setType(methodInputType).build()) + .setVariable( + Variable.builder().setName(REQUEST_VAR_NAME).setType(methodInputType).build()) .setIsDecl(true) .build(); String callableMethodName = @@ -885,9 +895,8 @@ private static MethodDefinition createMethodDefaultMethod( } Optional defaultMethodSample = - Optional.of( - ServiceClientMethodSampleComposer.composeCanonicalSample( - method, typeStore.get(clientName), resourceNames, messageTypes, service)); + ServiceClientMethodSampleComposer.composeCanonicalSample( + method, typeStore.get(clientName), resourceNames, messageTypes, service); Optional defaultMethodDocSample = Optional.empty(); if (defaultMethodSample.isPresent()) { samples.add(defaultMethodSample.get()); @@ -914,14 +923,82 @@ private static MethodDefinition createMethodDefaultMethod( .setName(String.format(method.hasLro() ? "%sAsync" : "%s", methodName)) .setArguments(Arrays.asList(requestArgVarExpr)); + if (method.hasLro()) { + methodBuilder = + methodBuilder.setReturnExpr(callableMethodExpr).setReturnType(methodOutputType); + } else { + if (isProtoEmptyType(methodOutputType)) { + methodBuilder = + methodBuilder + .setBody(Arrays.asList(ExprStatement.withExpr(callableMethodExpr))) + .setReturnType(TypeNode.VOID); + } else { + methodBuilder = + methodBuilder.setReturnExpr(callableMethodExpr).setReturnType(methodOutputType); + } + } + + methodBuilder.setAnnotations(createMethodAnnotations(method, typeStore)); + return methodBuilder.build(); + } + + private static MethodDefinition createResumableUploadDefaultMethod( + Method method, TypeStore typeStore) { + String methodName = JavaStyle.toLowerCamelCase(method.name()); + TypeNode methodInputType = method.inputType(); + TypeNode methodOutputType = method.outputType(); + + VariableExpr requestArgVarExpr = + VariableExpr.builder() + .setVariable( + Variable.builder().setName(REQUEST_VAR_NAME).setType(methodInputType).build()) + .setIsDecl(true) + .build(); + VariableExpr payloadArgVarExpr = + VariableExpr.builder() + .setVariable( + Variable.builder().setName("payload").setType(typeStore.get("InputStream")).build()) + .setIsDecl(true) + .build(); + + String callableMethodName = String.format(CALLABLE_NAME_PATTERN, methodName); + MethodInvocationExpr callableMethodExpr = + MethodInvocationExpr.builder().setMethodName(callableMethodName).build(); + MethodInvocationExpr futureCallExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(callableMethodExpr) + .setMethodName("futureCall") + .setArguments( + Arrays.asList( + requestArgVarExpr.toBuilder().setIsDecl(false).build(), + payloadArgVarExpr.toBuilder().setIsDecl(false).build())) + .build(); + + MethodInvocationExpr callAndTranslateExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(typeStore.get("ApiExceptions")) + .setMethodName("callAndTranslateApiException") + .setArguments(Arrays.asList(futureCallExpr)) + .setReturnType(methodOutputType) + .build(); + + MethodDefinition.Builder methodBuilder = + MethodDefinition.builder() + .setHeaderCommentStatements( + ServiceClientCommentComposer.createRpcMethodHeaderComment(method, Optional.empty())) + .setScope(ScopeNode.PUBLIC) + .setIsFinal(true) + .setName(methodName) + .setArguments(Arrays.asList(requestArgVarExpr, payloadArgVarExpr)); + if (isProtoEmptyType(methodOutputType)) { methodBuilder = methodBuilder - .setBody(Arrays.asList(ExprStatement.withExpr(callableMethodExpr))) + .setBody(Arrays.asList(ExprStatement.withExpr(callAndTranslateExpr))) .setReturnType(TypeNode.VOID); } else { methodBuilder = - methodBuilder.setReturnExpr(callableMethodExpr).setReturnType(methodOutputType); + methodBuilder.setReturnExpr(callAndTranslateExpr).setReturnType(methodOutputType); } methodBuilder.setAnnotations(createMethodAnnotations(method, typeStore)); @@ -992,7 +1069,9 @@ private static MethodDefinition createCallableMethod( case NONE: // Fall through. default: - rawCallableReturnType = typeStore.get("UnaryCallable"); + rawCallableReturnType = + typeStore.get( + method.isResumableUpload() ? "ResumableUploadCallable" : "UnaryCallable"); } } @@ -1038,13 +1117,12 @@ private static MethodDefinition createCallableMethod( } else if (callableMethodKind.equals(CallableMethodKind.REGULAR)) { if (method.stream().equals(Stream.NONE)) { sampleCode = - Optional.of( - ServiceClientCallableMethodSampleComposer.composeRegularCallableMethod( - method, - typeStore.get(ClassNames.getServiceClientClassName(service)), - resourceNames, - messageTypes, - service)); + ServiceClientCallableMethodSampleComposer.composeRegularCallableMethod( + method, + typeStore.get(ClassNames.getServiceClientClassName(service)), + resourceNames, + messageTypes, + service); } else { sampleCode = Optional.of( @@ -1797,6 +1875,7 @@ private static TypeStore createTypes(Service service, Map messa List> concreteClazzes = Arrays.asList( AbstractPagedListResponse.class, + ApiExceptions.class, ApiFunction.class, ApiFuture.class, ApiFutures.class, @@ -1806,6 +1885,7 @@ private static TypeStore createTypes(Service service, Map messa BidiStreamingCallable.class, ClientStreamingCallable.class, Generated.class, + InputStream.class, InterruptedException.class, IOException.class, MoreExecutors.class, @@ -1814,6 +1894,7 @@ private static TypeStore createTypes(Service service, Map messa Operation.class, OperationFuture.class, OperationCallable.class, + ResumableUploadCallable.class, ServerStreamingCallable.class, Status.class, Strings.class, diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientTestClassComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientTestClassComposer.java index b6666e1707c8..56471be71b1b 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientTestClassComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientTestClassComposer.java @@ -81,6 +81,7 @@ import org.junit.Assert; import org.junit.Before; import org.junit.BeforeClass; +import org.junit.Ignore; import org.junit.Test; @NullMarked @@ -237,6 +238,10 @@ private List createTestMethods( javaMethods.add(createUnsupportedTestMethod(method)); continue; } + if (method.isResumableUpload()) { + javaMethods.add(createResumableUploadTestMethod(method)); + continue; + } Service matchingService = service; if (method.isMixin()) { int dotIndex = method.mixedInApiName().lastIndexOf("."); @@ -786,6 +791,35 @@ protected MethodDefinition createUnsupportedTestMethod(Method method) { .build(); } + protected MethodDefinition createResumableUploadTestMethod(Method method) { + String javaMethodName = JavaStyle.toLowerCamelCase(method.name()); + String testMethodName = String.format("%sTest", javaMethodName); + + List methodBody = + Collections.singletonList( + CommentStatement.withComment( + LineComment.withComment( + "The " + + javaMethodName + + "() method requires a live HTTP/REST server supporting the resumable" + + " upload protocol and is tested in integration tests."))); + + return MethodDefinition.builder() + .setAnnotations( + Arrays.asList( + TEST_ANNOTATION, + AnnotationNode.withTypeAndDescription( + FIXED_TYPESTORE.get("Ignore"), + "Requires live HTTP/REST server supporting resumable upload protocol; tested in" + + " integration tests."))) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.VOID) + .setName(testMethodName) + .setThrowsExceptions(Arrays.asList(TypeNode.withExceptionClazz(Exception.class))) + .setBody(methodBody) + .build(); + } + protected List createRpcExceptionTestStatements( Method method, List methodSignature, @@ -927,6 +961,7 @@ private static TypeStore createStaticTypes() { ServerStreamingCallable.class, StatusCode.class, Test.class, + Ignore.class, UUID.class); return new TypeStore(concreteClazzes); } diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubClassComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubClassComposer.java index 51e6f410c079..3579a34585dd 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubClassComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceStubClassComposer.java @@ -21,6 +21,7 @@ import com.google.api.gax.rpc.ClientStreamingCallable; import com.google.api.gax.rpc.LongRunningClient; import com.google.api.gax.rpc.OperationCallable; +import com.google.api.gax.rpc.ResumableUploadCallable; import com.google.api.gax.rpc.ServerStreamingCallable; import com.google.api.gax.rpc.UnaryCallable; import com.google.api.generator.engine.ast.AnnotationNode; @@ -52,6 +53,7 @@ import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import javax.annotation.Generated; import org.jspecify.annotations.NullMarked; @@ -162,22 +164,9 @@ private MethodDefinition createCallableGetter(Method method, TypeStore typeStore private MethodDefinition createCallableGetterHelper( Method method, TypeStore typeStore, boolean isLroCallable, boolean isPaged) { - TypeNode returnType; - switch (method.stream()) { - case CLIENT: - returnType = typeStore.get("ClientStreamingCallable"); - break; - case SERVER: - returnType = typeStore.get("ServerStreamingCallable"); - break; - case BIDI: - returnType = typeStore.get("BidiStreamingCallable"); - break; - case NONE: - // Fall through. - default: - returnType = typeStore.get(isLroCallable ? "OperationCallable" : "UnaryCallable"); - } + TypeNode returnType = + resolveStreamingCallableType(method, typeStore) + .orElseGet(() -> typeStore.get(isLroCallable ? "OperationCallable" : "UnaryCallable")); String methodName = String.format( @@ -270,6 +259,7 @@ private static TypeStore createTypes(Service service, Map messa Generated.class, Operation.class, OperationCallable.class, + ResumableUploadCallable.class, ServerStreamingCallable.class, UnaryCallable.class, UnsupportedOperationException.class, @@ -328,4 +318,22 @@ protected MethodDefinition createOperationsStubGetterMethodDefinition( .build()))) .build(); } + + private static Optional resolveStreamingCallableType( + Method method, TypeStore typeStore) { + if (method.isResumableUpload()) { + return Optional.of(typeStore.get("ResumableUploadCallable")); + } + switch (method.stream()) { + case CLIENT: + return Optional.of(typeStore.get("ClientStreamingCallable")); + case SERVER: + return Optional.of(typeStore.get("ServerStreamingCallable")); + case BIDI: + return Optional.of(typeStore.get("BidiStreamingCallable")); + case NONE: + default: + return Optional.empty(); + } + } } diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientCallableMethodSampleComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientCallableMethodSampleComposer.java index bde75f09cedb..b8073a911ef8 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientCallableMethodSampleComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientCallableMethodSampleComposer.java @@ -57,6 +57,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import org.jspecify.annotations.NullMarked; @@ -545,12 +546,16 @@ public static Sample composePagedCallableMethod( } // Compose sample code for the method where it is CallableMethodKind.REGULAR. - public static Sample composeRegularCallableMethod( + public static Optional composeRegularCallableMethod( Method method, TypeNode clientType, Map resourceNames, Map messageTypes, Service service) { + if (method.isResumableUpload()) { + return Optional.empty(); + } + VariableExpr clientVarExpr = VariableExpr.withVariable( Variable.builder() @@ -601,7 +606,7 @@ public static Sample composeRegularCallableMethod( .setTryBody(bodyStatements) .setIsSampleCode(true) .build()); - return Sample.builder().setBody(body).setRegionTag(regionTag).build(); + return Optional.of(Sample.builder().setBody(body).setRegionTag(regionTag).build()); } public static Sample composeStreamCallableMethod( diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientHeaderSampleComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientHeaderSampleComposer.java index 8a99010ff102..1b454e0fcd25 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientHeaderSampleComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientHeaderSampleComposer.java @@ -56,10 +56,10 @@ public static Sample composeClassHeaderSample( Map messageTypes) { List publicMethods = service.methods().stream() - .filter(m -> m.isInternalApi() == false) + .filter(m -> !m.isInternalApi() && !m.isResumableUpload()) .collect(Collectors.toList()); - // If all generated methods are INTERNAL, generate an empty service sample. + // If all generated methods are INTERNAL or resumable upload, generate an empty service sample. if (publicMethods.isEmpty()) { return ServiceClientMethodSampleComposer.composeEmptyServiceSample(clientType, service); } @@ -75,7 +75,11 @@ public static Sample composeClassHeaderSample( if (method.stream() == Method.Stream.NONE) { if (method.methodSignatures().isEmpty()) { return ServiceClientMethodSampleComposer.composeCanonicalSample( - method, clientType, resourceNames, messageTypes, service); + method, clientType, resourceNames, messageTypes, service) + .orElseGet( + () -> + ServiceClientMethodSampleComposer.composeEmptyServiceSample( + clientType, service)); } return composeShowcaseMethodSample( method, diff --git a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientMethodSampleComposer.java b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientMethodSampleComposer.java index 998813c6b7a8..ca8e43f329a1 100644 --- a/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientMethodSampleComposer.java +++ b/sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientMethodSampleComposer.java @@ -40,6 +40,7 @@ import java.util.Arrays; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.stream.Collectors; import org.jspecify.annotations.NullMarked; @@ -72,12 +73,16 @@ public static Sample composeEmptyServiceSample(TypeNode clientType, Service serv return Sample.builder().setBody(body).setRegionTag(regionTag).setIsCanonical(true).build(); } - public static Sample composeCanonicalSample( + public static Optional composeCanonicalSample( Method method, TypeNode clientType, Map resourceNames, Map messageTypes, Service service) { + if (method.isResumableUpload()) { + return Optional.empty(); + } + VariableExpr clientVarExpr = VariableExpr.withVariable( Variable.builder() @@ -137,7 +142,8 @@ public static Sample composeCanonicalSample( .setTryBody(bodyStatements) .setIsSampleCode(true) .build()); - return Sample.builder().setBody(body).setRegionTag(regionTag).setIsCanonical(true).build(); + return Optional.of( + Sample.builder().setBody(body).setRegionTag(regionTag).setIsCanonical(true).build()); } static Sample composeSample( diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/common/ResumableUploadServiceClientComposerTest.java b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/common/ResumableUploadServiceClientComposerTest.java new file mode 100644 index 000000000000..07558cabfdbd --- /dev/null +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/common/ResumableUploadServiceClientComposerTest.java @@ -0,0 +1,153 @@ +// Copyright 2026 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.api.generator.gapic.composer.common; + +import static com.google.common.truth.Truth.assertThat; + +import com.google.api.generator.engine.ast.ConcreteReference; +import com.google.api.generator.engine.ast.TypeNode; +import com.google.api.generator.engine.writer.JavaWriterVisitor; +import com.google.api.generator.gapic.composer.grpc.ServiceClientTestClassComposer; +import com.google.api.generator.gapic.composer.rest.ServiceClientClassComposer; +import com.google.api.generator.gapic.composer.rest.ServiceStubClassComposer; +import com.google.api.generator.gapic.model.GapicClass; +import com.google.api.generator.gapic.model.GapicContext; +import com.google.api.generator.gapic.model.Method; +import com.google.api.generator.gapic.model.Service; +import com.google.api.generator.test.protoloader.RestTestProtoLoader; +import com.google.protobuf.Empty; +import java.util.Arrays; +import org.junit.jupiter.api.Test; + +class ResumableUploadServiceClientComposerTest { + + @Test + void generateServiceStub_resumableUploadCallable() { + GapicContext context = RestTestProtoLoader.instance().parseShowcaseResumableUpload(); + Service service = context.services().get(0); + + GapicClass clazz = ServiceStubClassComposer.instance().generate(context, service); + JavaWriterVisitor visitor = new JavaWriterVisitor(); + clazz.classDefinition().accept(visitor); + String code = visitor.write(); + + // Verify import of ResumableUploadCallable + assertThat(code).contains("import com.google.api.gax.rpc.ResumableUploadCallable;"); + + // Verify abstract callable getter on stub + assertThat(code) + .contains( + "public ResumableUploadCallable" + + " uploadMediaCallable() {\n" + + " throw new UnsupportedOperationException(\"Not implemented:" + + " uploadMediaCallable()\");\n" + + " }"); + } + + @Test + void generateServiceClient_resumableUploadCallableAndConvenienceMethod() { + GapicContext context = RestTestProtoLoader.instance().parseShowcaseResumableUpload(); + Service service = context.services().get(0); + + GapicClass clazz = ServiceClientClassComposer.instance().generate(context, service); + JavaWriterVisitor visitor = new JavaWriterVisitor(); + clazz.classDefinition().accept(visitor); + String code = visitor.write(); + + // Verify imports + assertThat(code).contains("import com.google.api.gax.rpc.ApiExceptions;"); + assertThat(code).contains("import com.google.api.gax.rpc.ResumableUploadCallable;"); + assertThat(code).contains("import java.io.InputStream;"); + + // Verify callable getter on client delegating to stub + assertThat(code) + .contains( + "public final ResumableUploadCallable\n" + + " uploadMediaCallable() {\n" + + " return stub.uploadMediaCallable();\n" + + " }"); + + // Verify synchronous convenience method calling ApiExceptions.callAndTranslateApiException + assertThat(code) + .contains( + "public final UploadMediaResponse uploadMedia(UploadMediaRequest request, InputStream" + + " payload) {\n" + + " return ApiExceptions.callAndTranslateApiException(\n" + + " uploadMediaCallable().futureCall(request, payload));\n" + + " }"); + + // Verify method variants (flattened methods) are suppressed + assertThat(code).doesNotContain("uploadMedia(String"); + + // Verify Javadoc warning on methods + assertThat(code) + .contains( + "Call context overrides (such as withTimeout, withRetrySettings, or credentials)"); + assertThat(code).contains("to the start request (session initiation)"); + assertThat(code).contains("Per-chunk PUT calls rely on the configured timeout"); + assertThat(code).contains("retry settings from ResumableUploadCallSettings."); + + // Verify @param request and @param payload in Javadoc + assertThat(code).contains("@param request The request object"); + assertThat(code).contains("@param payload The payload data stream to upload."); + } + + @Test + void generateServiceClientTest_resumableUploadTest() { + GapicContext context = RestTestProtoLoader.instance().parseShowcaseResumableUpload(); + Service service = context.services().get(0); + + GapicClass clazz = ServiceClientTestClassComposer.instance().generate(context, service); + JavaWriterVisitor visitor = new JavaWriterVisitor(); + clazz.classDefinition().accept(visitor); + String code = visitor.write(); + + // Verify test placeholder for resumable upload method + assertThat(code).contains("public void uploadMediaTest() throws Exception"); + assertThat(code) + .contains( + "@Ignore(\n" + + " \"Requires live HTTP/REST server supporting resumable upload protocol;" + + " tested in integration tests.\")"); + assertThat(code).contains("The uploadMedia() method requires a live HTTP/REST server"); + assertThat(code).contains("protocol and is tested in integration tests."); + + // Verify no exception test is generated + assertThat(code).doesNotContain("uploadMediaExceptionTest"); + } + + @Test + void generateServiceClient_resumableUploadVoidReturn() { + GapicContext context = RestTestProtoLoader.instance().parseShowcaseResumableUpload(); + Service service = context.services().get(0); + TypeNode emptyType = TypeNode.withReference(ConcreteReference.withClazz(Empty.class)); + Method emptyOutputMethod = + service.methods().get(0).toBuilder().setOutputType(emptyType).build(); + Service emptyOutputService = + service.toBuilder().setMethods(Arrays.asList(emptyOutputMethod)).build(); + + GapicClass clazz = ServiceClientClassComposer.instance().generate(context, emptyOutputService); + JavaWriterVisitor visitor = new JavaWriterVisitor(); + clazz.classDefinition().accept(visitor); + String code = visitor.write(); + + assertThat(code) + .contains( + "public final void uploadMedia(UploadMediaRequest request, InputStream payload) {\n" + + " ApiExceptions.callAndTranslateApiException(uploadMediaCallable().futureCall(request," + + " payload));\n" + + " }"); + } +} diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceClientClassComposerTest.java b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceClientClassComposerTest.java index 2654eb601531..c679df2d30ea 100644 --- a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceClientClassComposerTest.java +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceClientClassComposerTest.java @@ -64,7 +64,13 @@ private static Stream data() { GrpcTestProtoLoader.instance().parseSelectiveGenerationTesting(), "localhost:7469", "v1beta1", - 1)); + 1), + Arguments.of( + "ResumableUploadServiceClient", + GrpcTestProtoLoader.instance().parseShowcaseResumableUpload(), + "localhost:7469", + "v1beta1", + 0)); } @ParameterizedTest diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceStubClassComposerTest.java b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceStubClassComposerTest.java index 21d57f02b8a3..5c8d522ec286 100644 --- a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceStubClassComposerTest.java +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/ServiceStubClassComposerTest.java @@ -39,7 +39,13 @@ static Stream data() { TestProtoLoader.instance().parseSelectiveGenerationTesting(), "", "", - 1)); + 1), + Arguments.of( + "ResumableUploadServiceStub", + TestProtoLoader.instance().parseShowcaseResumableUpload(), + "", + "", + 0)); } @ParameterizedTest diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/ResumableUploadServiceClient.golden b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/ResumableUploadServiceClient.golden new file mode 100644 index 000000000000..de308beb5d3f --- /dev/null +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/ResumableUploadServiceClient.golden @@ -0,0 +1,207 @@ +package com.google.showcase.v1beta1; + +import com.google.api.core.BetaApi; +import com.google.api.gax.core.BackgroundResource; +import com.google.api.gax.rpc.ApiExceptions; +import com.google.api.gax.rpc.ResumableUploadCallable; +import com.google.showcase.v1beta1.stub.ResumableUploadServiceStub; +import com.google.showcase.v1beta1.stub.ResumableUploadServiceStubSettings; +import java.io.IOException; +import java.io.InputStream; +import java.util.concurrent.TimeUnit; +import javax.annotation.Generated; +import org.jspecify.annotations.NullMarked; +import org.jspecify.annotations.Nullable; + +// AUTO-GENERATED DOCUMENTATION AND CLASS. +/** + * This class provides the ability to make remote calls to the backing service through method calls + * that map to API methods. Sample code to get started: + * + *
{@code
+ * // This snippet has been automatically generated and should be regarded as a code template only.
+ * // It will require modifications to work:
+ * // - It may require correct/in-range values for request initialization.
+ * // - It may require specifying regional endpoints when creating the service client as shown in
+ * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+ * try (ResumableUploadServiceClient resumableUploadServiceClient =
+ *     ResumableUploadServiceClient.create()) {}
+ * }
+ * + *

Note: close() needs to be called on the ResumableUploadServiceClient object to clean up + * resources such as threads. In the example above, try-with-resources is used, which automatically + * calls close(). + * + * + * + * + * + * + * + * + * + * + * + * + * + *
Methods
MethodDescriptionMethod Variants

UploadMedia

+ *

Request object method variants only take one parameter, a request object, which must be constructed before the call.

+ *
    + *
  • uploadMedia(UploadMediaRequest request, InputStream payload) + *

+ *

Callable method variants take no parameters and return an immutable API callable object, which can be used to initiate calls to the service.

+ *
    + *
  • uploadMediaCallable() + *

+ *
+ * + *

See the individual methods for example code. + * + *

Many parameters require resource names to be formatted in a particular way. To assist with + * these names, this class includes a format method for each type of name, and additionally a parse + * method to extract the individual identifiers contained within names that are returned. + * + *

This class can be customized by passing in a custom instance of ResumableUploadServiceSettings + * to create(). For example: + * + *

To customize credentials: + * + *

{@code
+ * // This snippet has been automatically generated and should be regarded as a code template only.
+ * // It will require modifications to work:
+ * // - It may require correct/in-range values for request initialization.
+ * // - It may require specifying regional endpoints when creating the service client as shown in
+ * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+ * ResumableUploadServiceSettings resumableUploadServiceSettings =
+ *     ResumableUploadServiceSettings.newBuilder()
+ *         .setCredentialsProvider(FixedCredentialsProvider.create(myCredentials))
+ *         .build();
+ * ResumableUploadServiceClient resumableUploadServiceClient =
+ *     ResumableUploadServiceClient.create(resumableUploadServiceSettings);
+ * }
+ * + *

To customize the endpoint: + * + *

{@code
+ * // This snippet has been automatically generated and should be regarded as a code template only.
+ * // It will require modifications to work:
+ * // - It may require correct/in-range values for request initialization.
+ * // - It may require specifying regional endpoints when creating the service client as shown in
+ * // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library
+ * ResumableUploadServiceSettings resumableUploadServiceSettings =
+ *     ResumableUploadServiceSettings.newBuilder().setEndpoint(myEndpoint).build();
+ * ResumableUploadServiceClient resumableUploadServiceClient =
+ *     ResumableUploadServiceClient.create(resumableUploadServiceSettings);
+ * }
+ * + *

Please refer to the GitHub repository's samples for more quickstart code snippets. + */ +@NullMarked +@BetaApi +@Generated("by gapic-generator-java") +public class ResumableUploadServiceClient implements BackgroundResource { + private final @Nullable ResumableUploadServiceSettings settings; + private final ResumableUploadServiceStub stub; + + /** Constructs an instance of ResumableUploadServiceClient with default settings. */ + public static final ResumableUploadServiceClient create() throws IOException { + return create(ResumableUploadServiceSettings.newBuilder().build()); + } + + /** + * Constructs an instance of ResumableUploadServiceClient, using the given settings. The channels + * are created based on the settings passed in, or defaults for any settings that are not set. + */ + public static final ResumableUploadServiceClient create(ResumableUploadServiceSettings settings) + throws IOException { + return new ResumableUploadServiceClient(settings); + } + + /** + * Constructs an instance of ResumableUploadServiceClient, using the given stub for making calls. + * This is for advanced usage - prefer using create(ResumableUploadServiceSettings). + */ + public static final ResumableUploadServiceClient create(ResumableUploadServiceStub stub) { + return new ResumableUploadServiceClient(stub); + } + + /** + * Constructs an instance of ResumableUploadServiceClient, using the given settings. This is + * protected so that it is easy to make a subclass, but otherwise, the static factory methods + * should be preferred. + */ + protected ResumableUploadServiceClient(ResumableUploadServiceSettings settings) + throws IOException { + this.settings = settings; + this.stub = ((ResumableUploadServiceStubSettings) settings.getStubSettings()).createStub(); + } + + protected ResumableUploadServiceClient(ResumableUploadServiceStub stub) { + this.settings = null; + this.stub = stub; + } + + public final @Nullable ResumableUploadServiceSettings getSettings() { + return settings; + } + + public ResumableUploadServiceStub getStub() { + return stub; + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Call context overrides (such as withTimeout, withRetrySettings, or credentials) apply strictly + * to the start request (session initiation). Per-chunk PUT calls rely on the configured timeout + * and retry settings from ResumableUploadCallSettings. + * + * @param request The request object containing all of the parameters for the API call. + * @param payload The payload data stream to upload. + * @throws com.google.api.gax.rpc.ApiException if the remote call fails + */ + public final UploadMediaResponse uploadMedia(UploadMediaRequest request, InputStream payload) { + return ApiExceptions.callAndTranslateApiException( + uploadMediaCallable().futureCall(request, payload)); + } + + // AUTO-GENERATED DOCUMENTATION AND METHOD. + /** + * Call context overrides (such as withTimeout, withRetrySettings, or credentials) apply strictly + * to the start request (session initiation). Per-chunk PUT calls rely on the configured timeout + * and retry settings from ResumableUploadCallSettings. + */ + public final ResumableUploadCallable + uploadMediaCallable() { + return stub.uploadMediaCallable(); + } + + @Override + public final void close() { + stub.close(); + } + + @Override + public void shutdown() { + stub.shutdown(); + } + + @Override + public boolean isShutdown() { + return stub.isShutdown(); + } + + @Override + public boolean isTerminated() { + return stub.isTerminated(); + } + + @Override + public void shutdownNow() { + stub.shutdownNow(); + } + + @Override + public boolean awaitTermination(long duration, TimeUnit unit) throws InterruptedException { + return stub.awaitTermination(duration, unit); + } +} diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/ResumableUploadServiceStub.golden b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/ResumableUploadServiceStub.golden new file mode 100644 index 000000000000..02423b36738e --- /dev/null +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/ResumableUploadServiceStub.golden @@ -0,0 +1,28 @@ +package com.google.showcase.v1beta1.stub; + +import com.google.api.core.BetaApi; +import com.google.api.gax.core.BackgroundResource; +import com.google.api.gax.rpc.ResumableUploadCallable; +import com.google.showcase.v1beta1.UploadMediaRequest; +import com.google.showcase.v1beta1.UploadMediaResponse; +import javax.annotation.Generated; +import org.jspecify.annotations.NullMarked; + +// AUTO-GENERATED DOCUMENTATION AND CLASS. +/** + * Base stub class for the ResumableUploadService service API. + * + *

This class is for advanced usage and reflects the underlying API directly. + */ +@NullMarked +@BetaApi +@Generated("by gapic-generator-java") +public abstract class ResumableUploadServiceStub implements BackgroundResource { + + public ResumableUploadCallable uploadMediaCallable() { + throw new UnsupportedOperationException("Not implemented: uploadMediaCallable()"); + } + + @Override + public abstract void close(); +} diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncCreateSetCredentialsProvider.golden b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncCreateSetCredentialsProvider.golden new file mode 100644 index 000000000000..61f79a3354cf --- /dev/null +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncCreateSetCredentialsProvider.golden @@ -0,0 +1,45 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.samples; + +// [START goldensample_generated_ResumableUploadService_Create_SetCredentialsProvider_sync] +import com.google.api.gax.core.FixedCredentialsProvider; +import com.google.showcase.v1beta1.ResumableUploadServiceClient; +import com.google.showcase.v1beta1.ResumableUploadServiceSettings; +import com.google.showcase.v1beta1.myCredentials; + +public class SyncCreateSetCredentialsProvider { + + public static void main(String[] args) throws Exception { + syncCreateSetCredentialsProvider(); + } + + public static void syncCreateSetCredentialsProvider() throws Exception { + // This snippet has been automatically generated and should be regarded as a code template only. + // It will require modifications to work: + // - It may require correct/in-range values for request initialization. + // - It may require specifying regional endpoints when creating the service client as shown in + // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library + ResumableUploadServiceSettings resumableUploadServiceSettings = + ResumableUploadServiceSettings.newBuilder() + .setCredentialsProvider(FixedCredentialsProvider.create(myCredentials)) + .build(); + ResumableUploadServiceClient resumableUploadServiceClient = + ResumableUploadServiceClient.create(resumableUploadServiceSettings); + } +} +// [END goldensample_generated_ResumableUploadService_Create_SetCredentialsProvider_sync] diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncCreateSetEndpoint.golden b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncCreateSetEndpoint.golden new file mode 100644 index 000000000000..a41fe16336d8 --- /dev/null +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncCreateSetEndpoint.golden @@ -0,0 +1,42 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.samples; + +// [START goldensample_generated_ResumableUploadService_Create_SetEndpoint_sync] +import com.google.showcase.v1beta1.ResumableUploadServiceClient; +import com.google.showcase.v1beta1.ResumableUploadServiceSettings; +import com.google.showcase.v1beta1.myEndpoint; + +public class SyncCreateSetEndpoint { + + public static void main(String[] args) throws Exception { + syncCreateSetEndpoint(); + } + + public static void syncCreateSetEndpoint() throws Exception { + // This snippet has been automatically generated and should be regarded as a code template only. + // It will require modifications to work: + // - It may require correct/in-range values for request initialization. + // - It may require specifying regional endpoints when creating the service client as shown in + // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library + ResumableUploadServiceSettings resumableUploadServiceSettings = + ResumableUploadServiceSettings.newBuilder().setEndpoint(myEndpoint).build(); + ResumableUploadServiceClient resumableUploadServiceClient = + ResumableUploadServiceClient.create(resumableUploadServiceSettings); + } +} +// [END goldensample_generated_ResumableUploadService_Create_SetEndpoint_sync] diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncEmtpy.golden b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncEmtpy.golden new file mode 100644 index 000000000000..a3713f4609f3 --- /dev/null +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/grpc/goldens/samples/resumableuploadserviceclient/SyncEmtpy.golden @@ -0,0 +1,38 @@ +/* + * Copyright 2025 Google LLC + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.google.showcase.v1beta1.samples; + +// [START goldensample_generated_ResumableUploadService_Emtpy_sync] +import com.google.showcase.v1beta1.ResumableUploadServiceClient; + +public class SyncEmtpy { + + public static void main(String[] args) throws Exception { + syncEmtpy(); + } + + public static void syncEmtpy() throws Exception { + // This snippet has been automatically generated and should be regarded as a code template only. + // It will require modifications to work: + // - It may require correct/in-range values for request initialization. + // - It may require specifying regional endpoints when creating the service client as shown in + // https://cloud.google.com/java/docs/setup#configure_endpoints_for_the_client_library + try (ResumableUploadServiceClient resumableUploadServiceClient = + ResumableUploadServiceClient.create()) {} + } +} +// [END goldensample_generated_ResumableUploadService_Emtpy_sync] diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientCallableMethodSampleComposerTest.java b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientCallableMethodSampleComposerTest.java index 2b058aca7c64..605d3f1ec806 100644 --- a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientCallableMethodSampleComposerTest.java +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientCallableMethodSampleComposerTest.java @@ -30,6 +30,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; +import java.util.Optional; import org.junit.Assert; import org.junit.jupiter.api.Test; @@ -462,7 +463,8 @@ void valid_composeStreamCallableMethod_serverStream() { LineFormatter.lines( "try (EchoClient echoClient = EchoClient.create()) {\n", " ExpandRequest request =\n", - " ExpandRequest.newBuilder().setContent(\"content951530617\").setInfo(\"info3237038\").build();\n", + " " + + " ExpandRequest.newBuilder().setContent(\"content951530617\").setInfo(\"info3237038\").build();\n", " ServerStream stream = echoClient.expandCallable().call(request);\n", " for (EchoResponse response : stream) {\n", " // Do something when a response is received.\n", @@ -1184,6 +1186,58 @@ void invalid_composeRegularCallableMethod_noRepeatedResponsePagedRpc() { method, clientType, resourceNames, messageTypes, service)); } + @Test + void resumableUploadMethod_returnsEmpty() { + Descriptors.FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoRequest") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoResponse") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + Method method = + Method.builder() + .setName("Echo") + .setInputType(inputType) + .setOutputType(outputType) + .setIsResumableUpload(true) + .setMethodSignatures(Collections.emptyList()) + .build(); + Service service = + Service.builder() + .setName("Echo") + .setDefaultHost("localhost:7469") + .setOauthScopes(Arrays.asList("https://www.googleapis.com/auth/cloud-platform")) + .setPakkage(SHOWCASE_PACKAGE_NAME) + .setProtoPakkage(SHOWCASE_PACKAGE_NAME) + .setOriginalJavaPackage(SHOWCASE_PACKAGE_NAME) + .setMethods(Arrays.asList(method)) + .setOverriddenName("Echo") + .build(); + Optional sampleOpt = + ServiceClientCallableMethodSampleComposer.composeRegularCallableMethod( + method, clientType, resourceNames, messageTypes, service); + Assert.assertFalse(sampleOpt.isPresent()); + } + + private String writeStatements(Optional sampleOpt) { + return writeStatements(sampleOpt.get()); + } + private String writeStatements(Sample sample) { return SampleCodeWriter.write(sample.body()); } diff --git a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientMethodSampleComposerTest.java b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientMethodSampleComposerTest.java index d1b93a932ab3..4bbf1eead00b 100644 --- a/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientMethodSampleComposerTest.java +++ b/sdk-platform-java/gapic-generator-java/src/test/java/com/google/api/generator/gapic/composer/samplecode/ServiceClientMethodSampleComposerTest.java @@ -29,6 +29,7 @@ import java.util.Arrays; import java.util.Collections; import java.util.Map; +import java.util.Optional; import org.junit.Assert; import org.junit.jupiter.api.Test; @@ -405,6 +406,58 @@ void valid_composeDefaultSample_pureUnaryReturnResponse() { Assert.assertEquals(results, expected); } + @Test + void resumableUploadMethod_returnsEmpty() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + TypeNode clientType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoClient") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode inputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoRequest") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + TypeNode outputType = + TypeNode.withReference( + VaporReference.builder() + .setName("EchoResponse") + .setPakkage(SHOWCASE_PACKAGE_NAME) + .build()); + Method method = + Method.builder() + .setName("Echo") + .setInputType(inputType) + .setOutputType(outputType) + .setIsResumableUpload(true) + .setMethodSignatures(Collections.emptyList()) + .build(); + Service service = + Service.builder() + .setName("Echo") + .setDefaultHost("localhost:7469") + .setOauthScopes(Arrays.asList("https://www.googleapis.com/auth/cloud-platform")) + .setPakkage(SHOWCASE_PACKAGE_NAME) + .setProtoPakkage(SHOWCASE_PACKAGE_NAME) + .setOriginalJavaPackage(SHOWCASE_PACKAGE_NAME) + .setMethods(Arrays.asList(method)) + .setOverriddenName("Echo") + .build(); + Optional sampleOpt = + ServiceClientMethodSampleComposer.composeCanonicalSample( + method, clientType, resourceNames, messageTypes, service); + Assert.assertFalse(sampleOpt.isPresent()); + } + + private String writeStatements(Optional sampleOpt) { + return writeStatements(sampleOpt.get()); + } + private String writeStatements(Sample sample) { return SampleCodeWriter.write(sample.body()); }