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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,19 @@
import java.util.Optional;
import java.util.stream.Collectors;
import org.jspecify.annotations.NullMarked;
import org.jspecify.annotations.Nullable;

@NullMarked
public class ServiceClientCommentComposer {
// Tokens.
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 =
Expand Down Expand Up @@ -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(
Expand All @@ -105,9 +116,9 @@ public static List<CommentStatement> 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 =
Expand Down Expand Up @@ -187,14 +198,17 @@ public static List<CommentStatement> 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.
Expand All @@ -204,6 +218,10 @@ public static List<CommentStatement> createRpcMethodHeaderComment(
}
}

if (method.isResumableUpload()) {
methodJavadocBuilder.addParam(PAYLOAD_PARAM_NAME, PAYLOAD_PARAM_DESCRIPTION);
}

methodJavadocBuilder.setThrows(API_EXCEPTION_TYPE_NAME, EXCEPTION_CONDITION);

if (method.isDeprecated()) {
Expand Down Expand Up @@ -348,8 +366,12 @@ public static List<CommentStatement> 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());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,13 @@
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.ResumableUploadCallSettings;
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;
Expand Down Expand Up @@ -88,6 +91,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;
Expand All @@ -109,6 +113,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);
Expand Down Expand Up @@ -650,42 +655,46 @@ private static List<MethodDefinition> createServiceMethods(
methodVariantsForClientHeader.put(method.name(), new ArrayList<>());
}
if (method.stream().equals(Stream.NONE)) {
List<MethodDefinition> 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<MethodDefinition> 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));
Expand Down Expand Up @@ -778,7 +787,8 @@ private static List<MethodDefinition> 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();

Expand Down Expand Up @@ -873,7 +883,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 =
Expand All @@ -885,9 +896,8 @@ private static MethodDefinition createMethodDefaultMethod(
}

Optional<Sample> defaultMethodSample =
Optional.of(
ServiceClientMethodSampleComposer.composeCanonicalSample(
method, typeStore.get(clientName), resourceNames, messageTypes, service));
ServiceClientMethodSampleComposer.composeCanonicalSample(
method, typeStore.get(clientName), resourceNames, messageTypes, service);
Optional<String> defaultMethodDocSample = Optional.empty();
if (defaultMethodSample.isPresent()) {
samples.add(defaultMethodSample.get());
Expand All @@ -914,14 +924,86 @@ 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(),
CastExpr.builder()
.setType(typeStore.get("ResumableUploadCallSettings"))
.setExpr(ValueExpr.createNullExpr())
.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));
Expand Down Expand Up @@ -992,7 +1074,9 @@ private static MethodDefinition createCallableMethod(
case NONE:
// Fall through.
default:
rawCallableReturnType = typeStore.get("UnaryCallable");
rawCallableReturnType =
typeStore.get(
method.isResumableUpload() ? "ResumableUploadCallable" : "UnaryCallable");
}
}

Expand Down Expand Up @@ -1038,13 +1122,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(
Expand Down Expand Up @@ -1797,6 +1880,7 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
List<Class<?>> concreteClazzes =
Arrays.asList(
AbstractPagedListResponse.class,
ApiExceptions.class,
ApiFunction.class,
ApiFuture.class,
ApiFutures.class,
Expand All @@ -1806,6 +1890,7 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
BidiStreamingCallable.class,
ClientStreamingCallable.class,
Generated.class,
InputStream.class,
InterruptedException.class,
IOException.class,
MoreExecutors.class,
Expand All @@ -1814,6 +1899,8 @@ private static TypeStore createTypes(Service service, Map<String, Message> messa
Operation.class,
OperationFuture.class,
OperationCallable.class,
ResumableUploadCallSettings.class,
ResumableUploadCallable.class,
ServerStreamingCallable.class,
Status.class,
Strings.class,
Expand Down
Loading