Skip to content

Commit c202895

Browse files
committed
feat(generator): emit callable getter and convenience method for resumable upload RPCs
- In AbstractServiceStubClassComposer: emit public ResumableUploadCallable<RequestT, ResponseT> [method]Callable() throwing UnsupportedOperationException - 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 - Add unit test ResumableUploadServiceClientComposerTest verifying generated stub and client methods
1 parent 14d6389 commit c202895

5 files changed

Lines changed: 331 additions & 55 deletions

File tree

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

Lines changed: 52 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -93,6 +93,11 @@ public class ServiceClientCommentComposer {
9393
+ " that it is easy to make a subclass, but otherwise, the static factory methods"
9494
+ " should be preferred.";
9595

96+
private static final String RESUMABLE_UPLOAD_CALL_CONTEXT_WARNING =
97+
"Call context overrides (such as withTimeout, withRetrySettings, or credentials) apply"
98+
+ " strictly to the start request (session initiation). Per-chunk PUT calls rely on"
99+
+ " the configured timeout and retry settings from ResumableUploadCallSettings.";
100+
96101
// Comments.
97102
public static final CommentStatement GET_OPERATIONS_CLIENT_METHOD_COMMENT =
98103
toSimpleComment(
@@ -233,13 +238,17 @@ private static MethodAndVariants createMethodAndVariants(
233238

234239
private static String createTableOfMethods(List<MethodAndVariants> methodAndVariantsList) {
235240
String FLATTENED_METHODS =
236-
"<p>\"Flattened\" method variants have converted the fields of the request object into function parameters to enable multiple ways to call the same method.</p>\n";
241+
"<p>\"Flattened\" method variants have converted the fields of the request object into"
242+
+ " function parameters to enable multiple ways to call the same method.</p>\n";
237243
String REQUEST_OBJECT_METHODS =
238-
"<p>Request object method variants only take one parameter, a request object, which must be constructed before the call.</p>\n";
244+
"<p>Request object method variants only take one parameter, a request object, which must be"
245+
+ " constructed before the call.</p>\n";
239246
String CALLABLE_METHODS =
240-
"<p>Callable method variants take no parameters and return an immutable API callable object, which can be used to initiate calls to the service.</p>\n";
247+
"<p>Callable method variants take no parameters and return an immutable API callable"
248+
+ " object, which can be used to initiate calls to the service.</p>\n";
241249
String ASYNC_METHODS =
242-
"<p>Methods that return long-running operations have \"Async\" method variants that return `OperationFuture`, which is used to track polling of the service.</p>\n";
250+
"<p>Methods that return long-running operations have \"Async\" method variants that return"
251+
+ " `OperationFuture`, which is used to track polling of the service.</p>\n";
243252

244253
StringBuilder tableBuilder = new StringBuilder();
245254
tableBuilder
@@ -348,8 +357,12 @@ public static List<CommentStatement> createRpcCallableMethodHeaderComment(
348357
methodJavadocBuilder = methodJavadocBuilder.addUnescapedComment(descriptionComment);
349358
}
350359

351-
methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING);
360+
if (method.isResumableUpload()) {
361+
methodJavadocBuilder.addParagraph(RESUMABLE_UPLOAD_CALL_CONTEXT_WARNING);
362+
}
363+
352364
if (sampleCodeOpt.isPresent()) {
365+
methodJavadocBuilder.addParagraph(METHOD_DESCRIPTION_SAMPLE_CODE_SUMMARY_STRING);
353366
methodJavadocBuilder.addSampleCode(sampleCodeOpt.get());
354367
}
355368

@@ -366,6 +379,40 @@ public static List<CommentStatement> createRpcCallableMethodHeaderComment(
366379
CommentStatement.withComment(methodJavadocBuilder.build()));
367380
}
368381

382+
public static List<CommentStatement> createResumableUploadRpcMethodHeaderComment(Method method) {
383+
JavaDocComment.Builder methodJavadocBuilder = JavaDocComment.builder();
384+
385+
if (method.hasDescription()) {
386+
String descriptionComment =
387+
CommentFormatter.formatAsJavaDocComment(method.description(), null);
388+
methodJavadocBuilder = methodJavadocBuilder.addUnescapedComment(descriptionComment);
389+
}
390+
391+
methodJavadocBuilder.addParagraph(RESUMABLE_UPLOAD_CALL_CONTEXT_WARNING);
392+
393+
methodJavadocBuilder.addParam(
394+
"request", "The request object containing all of the parameters for the API call.");
395+
methodJavadocBuilder.addParam("payload", "The payload data stream to upload.");
396+
397+
methodJavadocBuilder.setThrows(API_EXCEPTION_TYPE_NAME, EXCEPTION_CONDITION);
398+
399+
if (method.isDeprecated()) {
400+
methodJavadocBuilder.setDeprecated(CommentComposer.DEPRECATED_METHOD_STRING);
401+
}
402+
403+
if (method.isInternalApi()) {
404+
methodJavadocBuilder.setInternalOnly(CommentComposer.INTERNAL_ONLY_METHOD_STRING);
405+
}
406+
407+
List<CommentStatement> comments = new ArrayList<>();
408+
comments.add(CommentComposer.AUTO_GENERATED_METHOD_COMMENT);
409+
if (!methodJavadocBuilder.emptyComments()) {
410+
comments.add(CommentStatement.withComment(methodJavadocBuilder.build()));
411+
}
412+
413+
return comments;
414+
}
415+
369416
private static CommentStatement toSimpleComment(String comment) {
370417
return CommentStatement.withComment(JavaDocComment.withComment(comment));
371418
}

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

Lines changed: 112 additions & 36 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,12 @@
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.ResumableUploadCallable;
3133
import com.google.api.gax.rpc.ServerStreamingCallable;
3234
import com.google.api.gax.rpc.UnaryCallable;
3335
import com.google.api.generator.engine.ast.AnnotationNode;
@@ -88,6 +90,7 @@
8890
import com.google.longrunning.Operation;
8991
import com.google.rpc.Status;
9092
import java.io.IOException;
93+
import java.io.InputStream;
9194
import java.util.ArrayList;
9295
import java.util.Arrays;
9396
import java.util.Collections;
@@ -650,42 +653,46 @@ private static List<MethodDefinition> createServiceMethods(
650653
methodVariantsForClientHeader.put(method.name(), new ArrayList<>());
651654
}
652655
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);
656+
if (!method.isResumableUpload()) {
657+
List<MethodDefinition> generatedMethods =
658+
createMethodVariants(
659+
method,
660+
ClassNames.getServiceClientClassName(service),
661+
messageTypes,
662+
typeStore,
663+
resourceNames,
664+
samples,
665+
service);
666+
667+
// Collect data for gapic_metadata.json.
668+
grpcRpcToJavaMethodMetadata
669+
.get(method.name())
670+
.addAll(
671+
generatedMethods.stream()
672+
.map(m -> javaMethodNameFn.apply(m))
673+
.collect(Collectors.toList()));
674+
675+
// Collect data for Client header
676+
methodVariantsForClientHeader
677+
.get(method.name())
678+
.addAll(
679+
generatedMethods.stream()
680+
.map(AbstractServiceClientClassComposer::getJavaMethod)
681+
.collect(Collectors.toList()));
682+
javaMethods.addAll(generatedMethods);
683+
}
679684

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

690697
// Collect data for gapic_metadata.json and client header.
691698
grpcRpcToJavaMethodMetadata.get(method.name()).add(javaMethodNameFn.apply(generatedMethod));
@@ -928,6 +935,68 @@ private static MethodDefinition createMethodDefaultMethod(
928935
return methodBuilder.build();
929936
}
930937

938+
private static MethodDefinition createResumableUploadDefaultMethod(
939+
Method method, TypeStore typeStore) {
940+
String methodName = JavaStyle.toLowerCamelCase(method.name());
941+
TypeNode methodInputType = method.inputType();
942+
TypeNode methodOutputType = method.outputType();
943+
944+
VariableExpr requestArgVarExpr =
945+
VariableExpr.builder()
946+
.setVariable(Variable.builder().setName("request").setType(methodInputType).build())
947+
.setIsDecl(true)
948+
.build();
949+
VariableExpr payloadArgVarExpr =
950+
VariableExpr.builder()
951+
.setVariable(
952+
Variable.builder().setName("payload").setType(typeStore.get("InputStream")).build())
953+
.setIsDecl(true)
954+
.build();
955+
956+
String callableMethodName = String.format(CALLABLE_NAME_PATTERN, methodName);
957+
MethodInvocationExpr callableMethodExpr =
958+
MethodInvocationExpr.builder().setMethodName(callableMethodName).build();
959+
MethodInvocationExpr futureCallExpr =
960+
MethodInvocationExpr.builder()
961+
.setExprReferenceExpr(callableMethodExpr)
962+
.setMethodName("futureCall")
963+
.setArguments(
964+
Arrays.asList(
965+
requestArgVarExpr.toBuilder().setIsDecl(false).build(),
966+
payloadArgVarExpr.toBuilder().setIsDecl(false).build()))
967+
.build();
968+
969+
MethodInvocationExpr callAndTranslateExpr =
970+
MethodInvocationExpr.builder()
971+
.setStaticReferenceType(typeStore.get("ApiExceptions"))
972+
.setMethodName("callAndTranslateApiException")
973+
.setArguments(Arrays.asList(futureCallExpr))
974+
.setReturnType(methodOutputType)
975+
.build();
976+
977+
MethodDefinition.Builder methodBuilder =
978+
MethodDefinition.builder()
979+
.setHeaderCommentStatements(
980+
ServiceClientCommentComposer.createResumableUploadRpcMethodHeaderComment(method))
981+
.setScope(ScopeNode.PUBLIC)
982+
.setIsFinal(true)
983+
.setName(methodName)
984+
.setArguments(Arrays.asList(requestArgVarExpr, payloadArgVarExpr));
985+
986+
if (isProtoEmptyType(methodOutputType)) {
987+
methodBuilder =
988+
methodBuilder
989+
.setBody(Arrays.asList(ExprStatement.withExpr(callAndTranslateExpr)))
990+
.setReturnType(TypeNode.VOID);
991+
} else {
992+
methodBuilder =
993+
methodBuilder.setReturnExpr(callAndTranslateExpr).setReturnType(methodOutputType);
994+
}
995+
996+
methodBuilder.setAnnotations(createMethodAnnotations(method, typeStore));
997+
return methodBuilder.build();
998+
}
999+
9311000
private static MethodDefinition createLroCallableMethod(
9321001
Service service,
9331002
Method method,
@@ -992,7 +1061,9 @@ private static MethodDefinition createCallableMethod(
9921061
case NONE:
9931062
// Fall through.
9941063
default:
995-
rawCallableReturnType = typeStore.get("UnaryCallable");
1064+
rawCallableReturnType =
1065+
typeStore.get(
1066+
method.isResumableUpload() ? "ResumableUploadCallable" : "UnaryCallable");
9961067
}
9971068
}
9981069

@@ -1036,7 +1107,9 @@ private static MethodDefinition createCallableMethod(
10361107
messageTypes,
10371108
service));
10381109
} else if (callableMethodKind.equals(CallableMethodKind.REGULAR)) {
1039-
if (method.stream().equals(Stream.NONE)) {
1110+
if (method.isResumableUpload()) {
1111+
sampleCode = Optional.empty();
1112+
} else if (method.stream().equals(Stream.NONE)) {
10401113
sampleCode =
10411114
Optional.of(
10421115
ServiceClientCallableMethodSampleComposer.composeRegularCallableMethod(
@@ -1797,6 +1870,7 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
17971870
List<Class<?>> concreteClazzes =
17981871
Arrays.asList(
17991872
AbstractPagedListResponse.class,
1873+
ApiExceptions.class,
18001874
ApiFunction.class,
18011875
ApiFuture.class,
18021876
ApiFutures.class,
@@ -1806,6 +1880,7 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
18061880
BidiStreamingCallable.class,
18071881
ClientStreamingCallable.class,
18081882
Generated.class,
1883+
InputStream.class,
18091884
InterruptedException.class,
18101885
IOException.class,
18111886
MoreExecutors.class,
@@ -1814,6 +1889,7 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
18141889
Operation.class,
18151890
OperationFuture.class,
18161891
OperationCallable.class,
1892+
ResumableUploadCallable.class,
18171893
ServerStreamingCallable.class,
18181894
Status.class,
18191895
Strings.class,

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

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -237,6 +237,10 @@ private List<MethodDefinition> createTestMethods(
237237
javaMethods.add(createUnsupportedTestMethod(method));
238238
continue;
239239
}
240+
if (method.isResumableUpload()) {
241+
javaMethods.add(createResumableUploadTestMethod(method));
242+
continue;
243+
}
240244
Service matchingService = service;
241245
if (method.isMixin()) {
242246
int dotIndex = method.mixedInApiName().lastIndexOf(".");
@@ -786,6 +790,29 @@ protected MethodDefinition createUnsupportedTestMethod(Method method) {
786790
.build();
787791
}
788792

793+
protected MethodDefinition createResumableUploadTestMethod(Method method) {
794+
String javaMethodName = JavaStyle.toLowerCamelCase(method.name());
795+
String testMethodName = String.format("%sTest", javaMethodName);
796+
797+
List<Statement> methodBody =
798+
Collections.singletonList(
799+
CommentStatement.withComment(
800+
LineComment.withComment(
801+
"The "
802+
+ javaMethodName
803+
+ "() method requires a live HTTP/REST server supporting the resumable"
804+
+ " upload protocol and is tested in integration tests.")));
805+
806+
return MethodDefinition.builder()
807+
.setAnnotations(Arrays.asList(TEST_ANNOTATION))
808+
.setScope(ScopeNode.PUBLIC)
809+
.setReturnType(TypeNode.VOID)
810+
.setName(testMethodName)
811+
.setThrowsExceptions(Arrays.asList(TypeNode.withExceptionClazz(Exception.class)))
812+
.setBody(methodBody)
813+
.build();
814+
}
815+
789816
protected List<Statement> createRpcExceptionTestStatements(
790817
Method method,
791818
List<MethodArgument> methodSignature,

0 commit comments

Comments
 (0)