Skip to content

Commit 1b9002f

Browse files
committed
feat(generator): emit callable getter and convenience method for resumable upload RPCs
- In AbstractServiceClientClassComposer: emit public ResumableUploadCallable<RequestT, ResponseT> [method]Callable() delegating to stub - In AbstractServiceClientClassComposer: emit synchronous convenience method [method](RequestT request, InputStream payload) calling ApiExceptions.callAndTranslateApiException - Suppress method variants (flattened methods) for resumable upload RPCs - In ServiceClientCommentComposer: emit Javadoc call context override warning on both callable method and synchronous convenience method - Cover the generated client and its samples with ResumableUploadServiceClient.golden and the resumableuploadserviceclient sample goldens
1 parent c08a674 commit 1b9002f

13 files changed

Lines changed: 548 additions & 66 deletions

sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/comment/ServiceClientCommentComposer.java

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -33,13 +33,22 @@
3333
import java.util.Optional;
3434
import java.util.stream.Collectors;
3535
import org.jspecify.annotations.NullMarked;
36+
import org.jspecify.annotations.Nullable;
3637

3738
@NullMarked
3839
public class ServiceClientCommentComposer {
3940
// Tokens.
4041
private static final String EMPTY_STRING = "";
4142
private static final String API_EXCEPTION_TYPE_NAME = "com.google.api.gax.rpc.ApiException";
4243
private static final String EXCEPTION_CONDITION = "if the remote call fails";
44+
private static final String REQUEST_PARAM_NAME = "request";
45+
private static final String REQUEST_PARAM_DESCRIPTION =
46+
"The request object containing all of the parameters for the API call.";
47+
private static final String PAYLOAD_PARAM_NAME = "payload";
48+
private static final String PAYLOAD_PARAM_DESCRIPTION = "The payload data stream to upload.";
49+
private static final String CALL_SETTINGS_PARAM_NAME = "callSettings";
50+
private static final String CALL_SETTINGS_PARAM_DESCRIPTION =
51+
"The call settings to apply to this upload, or null to use defaults.";
4352

4453
// Constants.
4554
private static final String SERVICE_DESCRIPTION_INTRO_STRING =
@@ -105,9 +114,9 @@ public static List<CommentStatement> createClassHeaderComments(
105114
String classMethodSampleCode,
106115
String credentialsSampleCode,
107116
String endpointSampleCode,
108-
String transportSampleCode,
109-
String primaryTransport,
110-
String secondaryTransport) {
117+
@Nullable String transportSampleCode,
118+
@Nullable String primaryTransport,
119+
@Nullable String secondaryTransport) {
111120
JavaDocComment.Builder classHeaderJavadocBuilder = JavaDocComment.builder();
112121
if (service.hasDescription()) {
113122
String descriptionComment =
@@ -187,14 +196,13 @@ public static List<CommentStatement> createRpcMethodHeaderComment(
187196
methodJavadocBuilder = methodJavadocBuilder.addUnescapedComment(descriptionComment);
188197
}
189198

190-
if (sampleCodeOpt.isPresent()) {
199+
if (sampleCodeOpt.isPresent() && !method.isResumableUpload()) {
191200
methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING);
192201
methodJavadocBuilder.addSampleCode(sampleCodeOpt.get());
193202
}
194203

195204
if (methodArguments.isEmpty()) {
196-
methodJavadocBuilder.addParam(
197-
"request", "The request object containing all of the parameters for the API call.");
205+
methodJavadocBuilder.addParam(REQUEST_PARAM_NAME, REQUEST_PARAM_DESCRIPTION);
198206
} else {
199207
for (MethodArgument argument : methodArguments) {
200208
// TODO(miraleung): Remove the newline replacement when we support CommonMark.
@@ -204,6 +212,11 @@ public static List<CommentStatement> createRpcMethodHeaderComment(
204212
}
205213
}
206214

215+
if (method.isResumableUpload()) {
216+
methodJavadocBuilder.addParam(PAYLOAD_PARAM_NAME, PAYLOAD_PARAM_DESCRIPTION);
217+
methodJavadocBuilder.addParam(CALL_SETTINGS_PARAM_NAME, CALL_SETTINGS_PARAM_DESCRIPTION);
218+
}
219+
207220
methodJavadocBuilder.setThrows(API_EXCEPTION_TYPE_NAME, EXCEPTION_CONDITION);
208221

209222
if (method.isDeprecated()) {
@@ -348,8 +361,8 @@ public static List<CommentStatement> createRpcCallableMethodHeaderComment(
348361
methodJavadocBuilder = methodJavadocBuilder.addUnescapedComment(descriptionComment);
349362
}
350363

351-
methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING);
352-
if (sampleCodeOpt.isPresent()) {
364+
if (sampleCodeOpt.isPresent() && !method.isResumableUpload()) {
365+
methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING);
353366
methodJavadocBuilder.addSampleCode(sampleCodeOpt.get());
354367
}
355368

sdk-platform-java/gapic-generator-java/src/main/java/com/google/api/generator/gapic/composer/common/AbstractServiceClientClassComposer.java

Lines changed: 148 additions & 49 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,13 @@
2424
import com.google.api.gax.paging.AbstractFixedSizeCollection;
2525
import com.google.api.gax.paging.AbstractPage;
2626
import com.google.api.gax.paging.AbstractPagedListResponse;
27+
import com.google.api.gax.rpc.ApiExceptions;
2728
import com.google.api.gax.rpc.BidiStreamingCallable;
2829
import com.google.api.gax.rpc.ClientStreamingCallable;
2930
import com.google.api.gax.rpc.OperationCallable;
3031
import com.google.api.gax.rpc.PageContext;
32+
import com.google.api.gax.rpc.ResumableUploadCallSettings;
33+
import com.google.api.gax.rpc.ResumableUploadCallable;
3134
import com.google.api.gax.rpc.ServerStreamingCallable;
3235
import com.google.api.gax.rpc.UnaryCallable;
3336
import com.google.api.generator.engine.ast.AnnotationNode;
@@ -88,6 +91,7 @@
8891
import com.google.longrunning.Operation;
8992
import com.google.rpc.Status;
9093
import java.io.IOException;
94+
import java.io.InputStream;
9195
import java.util.ArrayList;
9296
import java.util.Arrays;
9397
import java.util.Collections;
@@ -109,6 +113,7 @@ public abstract class AbstractServiceClientClassComposer implements ClassCompose
109113
private static final String CALLABLE_NAME_PATTERN = "%sCallable";
110114
private static final String PAGED_CALLABLE_NAME_PATTERN = "%sPagedCallable";
111115
private static final String OPERATION_CALLABLE_NAME_PATTERN = "%sOperationCallable";
116+
private static final String REQUEST_VAR_NAME = "request";
112117

113118
private static final Reference LIST_REFERENCE = ConcreteReference.withClazz(List.class);
114119
private static final Reference MAP_REFERENCE = ConcreteReference.withClazz(Map.class);
@@ -650,42 +655,46 @@ private static List<MethodDefinition> createServiceMethods(
650655
methodVariantsForClientHeader.put(method.name(), new ArrayList<>());
651656
}
652657
if (method.stream().equals(Stream.NONE)) {
653-
List<MethodDefinition> generatedMethods =
654-
createMethodVariants(
655-
method,
656-
ClassNames.getServiceClientClassName(service),
657-
messageTypes,
658-
typeStore,
659-
resourceNames,
660-
samples,
661-
service);
662-
663-
// Collect data for gapic_metadata.json.
664-
grpcRpcToJavaMethodMetadata
665-
.get(method.name())
666-
.addAll(
667-
generatedMethods.stream()
668-
.map(m -> javaMethodNameFn.apply(m))
669-
.collect(Collectors.toList()));
670-
671-
// Collect data for Client header
672-
methodVariantsForClientHeader
673-
.get(method.name())
674-
.addAll(
675-
generatedMethods.stream()
676-
.map(AbstractServiceClientClassComposer::getJavaMethod)
677-
.collect(Collectors.toList()));
678-
javaMethods.addAll(generatedMethods);
658+
if (!method.isResumableUpload()) {
659+
List<MethodDefinition> generatedMethods =
660+
createMethodVariants(
661+
method,
662+
ClassNames.getServiceClientClassName(service),
663+
messageTypes,
664+
typeStore,
665+
resourceNames,
666+
samples,
667+
service);
668+
669+
// Collect data for gapic_metadata.json.
670+
grpcRpcToJavaMethodMetadata
671+
.get(method.name())
672+
.addAll(
673+
generatedMethods.stream()
674+
.map(m -> javaMethodNameFn.apply(m))
675+
.collect(Collectors.toList()));
676+
677+
// Collect data for Client header
678+
methodVariantsForClientHeader
679+
.get(method.name())
680+
.addAll(
681+
generatedMethods.stream()
682+
.map(AbstractServiceClientClassComposer::getJavaMethod)
683+
.collect(Collectors.toList()));
684+
javaMethods.addAll(generatedMethods);
685+
}
679686

680687
MethodDefinition generatedMethod =
681-
createMethodDefaultMethod(
682-
method,
683-
ClassNames.getServiceClientClassName(service),
684-
messageTypes,
685-
typeStore,
686-
resourceNames,
687-
samples,
688-
service);
688+
method.isResumableUpload()
689+
? createResumableUploadDefaultMethod(method, typeStore)
690+
: createMethodDefaultMethod(
691+
method,
692+
ClassNames.getServiceClientClassName(service),
693+
messageTypes,
694+
typeStore,
695+
resourceNames,
696+
samples,
697+
service);
689698

690699
// Collect data for gapic_metadata.json and client header.
691700
grpcRpcToJavaMethodMetadata.get(method.name()).add(javaMethodNameFn.apply(generatedMethod));
@@ -778,7 +787,8 @@ private static List<MethodDefinition> createMethodVariants(
778787
// Request proto builder.
779788
VariableExpr requestVarExpr =
780789
VariableExpr.builder()
781-
.setVariable(Variable.builder().setName("request").setType(methodInputType).build())
790+
.setVariable(
791+
Variable.builder().setName(REQUEST_VAR_NAME).setType(methodInputType).build())
782792
.setIsDecl(true)
783793
.build();
784794

@@ -873,7 +883,8 @@ private static MethodDefinition createMethodDefaultMethod(
873883
// Construct the method that accepts a request proto.
874884
VariableExpr requestArgVarExpr =
875885
VariableExpr.builder()
876-
.setVariable(Variable.builder().setName("request").setType(methodInputType).build())
886+
.setVariable(
887+
Variable.builder().setName(REQUEST_VAR_NAME).setType(methodInputType).build())
877888
.setIsDecl(true)
878889
.build();
879890
String callableMethodName =
@@ -885,9 +896,8 @@ private static MethodDefinition createMethodDefaultMethod(
885896
}
886897

887898
Optional<Sample> defaultMethodSample =
888-
Optional.of(
889-
ServiceClientMethodSampleComposer.composeCanonicalSample(
890-
method, typeStore.get(clientName), resourceNames, messageTypes, service));
899+
ServiceClientMethodSampleComposer.composeCanonicalSample(
900+
method, typeStore.get(clientName), resourceNames, messageTypes, service);
891901
Optional<String> defaultMethodDocSample = Optional.empty();
892902
if (defaultMethodSample.isPresent()) {
893903
samples.add(defaultMethodSample.get());
@@ -914,14 +924,98 @@ private static MethodDefinition createMethodDefaultMethod(
914924
.setName(String.format(method.hasLro() ? "%sAsync" : "%s", methodName))
915925
.setArguments(Arrays.asList(requestArgVarExpr));
916926

927+
if (method.hasLro()) {
928+
methodBuilder =
929+
methodBuilder.setReturnExpr(callableMethodExpr).setReturnType(methodOutputType);
930+
} else {
931+
if (isProtoEmptyType(methodOutputType)) {
932+
methodBuilder =
933+
methodBuilder
934+
.setBody(Arrays.asList(ExprStatement.withExpr(callableMethodExpr)))
935+
.setReturnType(TypeNode.VOID);
936+
} else {
937+
methodBuilder =
938+
methodBuilder.setReturnExpr(callableMethodExpr).setReturnType(methodOutputType);
939+
}
940+
}
941+
942+
methodBuilder.setAnnotations(createMethodAnnotations(method, typeStore));
943+
return methodBuilder.build();
944+
}
945+
946+
private static MethodDefinition createResumableUploadDefaultMethod(
947+
Method method, TypeStore typeStore) {
948+
String methodName = JavaStyle.toLowerCamelCase(method.name());
949+
TypeNode methodInputType = method.inputType();
950+
TypeNode methodOutputType = method.outputType();
951+
952+
VariableExpr requestArgVarExpr =
953+
VariableExpr.builder()
954+
.setVariable(
955+
Variable.builder().setName(REQUEST_VAR_NAME).setType(methodInputType).build())
956+
.setIsDecl(true)
957+
.build();
958+
VariableExpr payloadArgVarExpr =
959+
VariableExpr.builder()
960+
.setVariable(
961+
Variable.builder().setName("payload").setType(typeStore.get("InputStream")).build())
962+
.setIsDecl(true)
963+
.build();
964+
VariableExpr callSettingsArgVarExpr =
965+
VariableExpr.builder()
966+
.setVariable(
967+
Variable.builder()
968+
.setName("callSettings")
969+
.setType(
970+
TypeNode.withReference(
971+
typeStore
972+
.get("ResumableUploadCallSettings")
973+
.reference()
974+
.copyAndSetNullable(true)))
975+
.build())
976+
.setIsDecl(true)
977+
.build();
978+
979+
String callableMethodName = String.format(CALLABLE_NAME_PATTERN, methodName);
980+
MethodInvocationExpr callableMethodExpr =
981+
MethodInvocationExpr.builder().setMethodName(callableMethodName).build();
982+
MethodInvocationExpr futureCallExpr =
983+
MethodInvocationExpr.builder()
984+
.setExprReferenceExpr(callableMethodExpr)
985+
.setMethodName("futureCall")
986+
.setArguments(
987+
Arrays.asList(
988+
requestArgVarExpr.toBuilder().setIsDecl(false).build(),
989+
payloadArgVarExpr.toBuilder().setIsDecl(false).build(),
990+
callSettingsArgVarExpr.toBuilder().setIsDecl(false).build()))
991+
.build();
992+
993+
MethodInvocationExpr callAndTranslateExpr =
994+
MethodInvocationExpr.builder()
995+
.setStaticReferenceType(typeStore.get("ApiExceptions"))
996+
.setMethodName("callAndTranslateApiException")
997+
.setArguments(Arrays.asList(futureCallExpr))
998+
.setReturnType(methodOutputType)
999+
.build();
1000+
1001+
MethodDefinition.Builder methodBuilder =
1002+
MethodDefinition.builder()
1003+
.setHeaderCommentStatements(
1004+
ServiceClientCommentComposer.createRpcMethodHeaderComment(method, Optional.empty()))
1005+
.setScope(ScopeNode.PUBLIC)
1006+
.setIsFinal(true)
1007+
.setName(methodName)
1008+
.setArguments(
1009+
Arrays.asList(requestArgVarExpr, payloadArgVarExpr, callSettingsArgVarExpr));
1010+
9171011
if (isProtoEmptyType(methodOutputType)) {
9181012
methodBuilder =
9191013
methodBuilder
920-
.setBody(Arrays.asList(ExprStatement.withExpr(callableMethodExpr)))
1014+
.setBody(Arrays.asList(ExprStatement.withExpr(callAndTranslateExpr)))
9211015
.setReturnType(TypeNode.VOID);
9221016
} else {
9231017
methodBuilder =
924-
methodBuilder.setReturnExpr(callableMethodExpr).setReturnType(methodOutputType);
1018+
methodBuilder.setReturnExpr(callAndTranslateExpr).setReturnType(methodOutputType);
9251019
}
9261020

9271021
methodBuilder.setAnnotations(createMethodAnnotations(method, typeStore));
@@ -992,7 +1086,9 @@ private static MethodDefinition createCallableMethod(
9921086
case NONE:
9931087
// Fall through.
9941088
default:
995-
rawCallableReturnType = typeStore.get("UnaryCallable");
1089+
rawCallableReturnType =
1090+
typeStore.get(
1091+
method.isResumableUpload() ? "ResumableUploadCallable" : "UnaryCallable");
9961092
}
9971093
}
9981094

@@ -1038,13 +1134,12 @@ private static MethodDefinition createCallableMethod(
10381134
} else if (callableMethodKind.equals(CallableMethodKind.REGULAR)) {
10391135
if (method.stream().equals(Stream.NONE)) {
10401136
sampleCode =
1041-
Optional.of(
1042-
ServiceClientCallableMethodSampleComposer.composeRegularCallableMethod(
1043-
method,
1044-
typeStore.get(ClassNames.getServiceClientClassName(service)),
1045-
resourceNames,
1046-
messageTypes,
1047-
service));
1137+
ServiceClientCallableMethodSampleComposer.composeRegularCallableMethod(
1138+
method,
1139+
typeStore.get(ClassNames.getServiceClientClassName(service)),
1140+
resourceNames,
1141+
messageTypes,
1142+
service);
10481143
} else {
10491144
sampleCode =
10501145
Optional.of(
@@ -1797,6 +1892,7 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
17971892
List<Class<?>> concreteClazzes =
17981893
Arrays.asList(
17991894
AbstractPagedListResponse.class,
1895+
ApiExceptions.class,
18001896
ApiFunction.class,
18011897
ApiFuture.class,
18021898
ApiFutures.class,
@@ -1806,6 +1902,7 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
18061902
BidiStreamingCallable.class,
18071903
ClientStreamingCallable.class,
18081904
Generated.class,
1905+
InputStream.class,
18091906
InterruptedException.class,
18101907
IOException.class,
18111908
MoreExecutors.class,
@@ -1814,6 +1911,8 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
18141911
Operation.class,
18151912
OperationFuture.class,
18161913
OperationCallable.class,
1914+
ResumableUploadCallSettings.class,
1915+
ResumableUploadCallable.class,
18171916
ServerStreamingCallable.class,
18181917
Status.class,
18191918
Strings.class,

0 commit comments

Comments
 (0)