From c71f061f96d8b418c8c1549cf65eb9fc45ee6396 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 25 Aug 2020 13:06:47 -0700 Subject: [PATCH 1/6] feat: add factory var decl in ServiceStubSettings codegen --- .../ServiceStubSettingsClassComposer.java | 223 +++++++++++++++++- .../ServiceStubSettingsClassComposerTest.java | 26 ++ 2 files changed, 242 insertions(+), 7 deletions(-) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java index 9762def49a..79ea6a0117 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java @@ -52,6 +52,7 @@ import com.google.api.gax.rpc.UnaryCallSettings; import com.google.api.gax.rpc.UnaryCallable; import com.google.api.generator.engine.ast.AnnotationNode; +import com.google.api.generator.engine.ast.AnonymousClassExpr; import com.google.api.generator.engine.ast.AssignmentExpr; import com.google.api.generator.engine.ast.ClassDefinition; import com.google.api.generator.engine.ast.ConcreteReference; @@ -74,6 +75,7 @@ import com.google.api.generator.engine.ast.VaporReference; import com.google.api.generator.engine.ast.Variable; import com.google.api.generator.engine.ast.VariableExpr; +import com.google.api.generator.gapic.model.Field; import com.google.api.generator.gapic.model.GapicClass; import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.Method; @@ -102,9 +104,11 @@ // TODO(miraleung): Refactor ClassComposer's interface. public class ServiceStubSettingsClassComposer { private static final String CLASS_NAME_PATTERN = "%sStubSettings"; + private static final String GRPC_SERVICE_STUB_PATTERN = "Grpc%sStub"; + private static final String PAGE_STR_DESC_PATTERN = "%s_PAGE_STR_DESC"; + private static final String PAGED_RESPONSE_FACTORY_PATTERN = "%s_PAGE_STR_FACT"; private static final String PAGED_RESPONSE_TYPE_NAME_PATTERN = "%sPagedResponse"; private static final String NESTED_BUILDER_CLASS_NAME = "Builder"; - private static final String GRPC_SERVICE_STUB_PATTERN = "Grpc%sStub"; private static final String STUB_PATTERN = "%sStub"; private static final String LEFT_BRACE = "{"; @@ -139,7 +143,9 @@ public GapicClass generate( .setScope(ScopeNode.PUBLIC) .setName(className) .setExtendsType(createExtendsType(service, types)) - .setStatements(createClassStatements(service, methodSettingsMemberVarExprs, types)) + .setStatements( + createClassStatements( + service, serviceConfig, methodSettingsMemberVarExprs, messageTypes, types)) .setMethods(createClassMethods(service, methodSettingsMemberVarExprs, types)) .setNestedClasses(Arrays.asList(createNestedBuilderClass(service, types))) .build(); @@ -193,9 +199,11 @@ private static Map createClassMemberVarExprs( private static List createClassStatements( Service service, + ServiceConfig serviceConfig, Map methodSettingsMemberVarExprs, + Map messageTypes, Map types) { - List memberVars = new ArrayList<>(); + List memberVarExprs = new ArrayList<>(); // Assign DEFAULT_SERVICE_SCOPES. VariableExpr defaultServiceScopesDeclVarExpr = @@ -228,14 +236,14 @@ private static List createClassStatements( .setReturnType(DEFAULT_SERVICE_SCOPES_VAR_EXPR.type()) .build(); - memberVars.add( + memberVarExprs.add( AssignmentExpr.builder() .setVariableExpr(defaultServiceScopesDeclVarExpr) .setValueExpr(listBuilderExpr) .build()); // Declare settings members. - memberVars.addAll( + memberVarExprs.addAll( methodSettingsMemberVarExprs.values().stream() .map( v -> @@ -246,8 +254,209 @@ private static List createClassStatements( .build()) .collect(Collectors.toList())); - // TODO(miraleung): Fill this out. - return memberVars.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); + memberVarExprs.addAll( + createPagingStaticAssignExprs(service, serviceConfig, messageTypes, types)); + return memberVarExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); + } + + private static List createPagingStaticAssignExprs( + Service service, + ServiceConfig serviceConfig, + Map messageTypes, + Map types) { + // TODO(miraleung): Add a test case for several such statements. + List exprs = new ArrayList<>(); + for (Method method : service.methods()) { + if (!method.isPaged()) { + continue; + } + // Find the repeated type. + Message pagedResponseMessage = + messageTypes.get(JavaStyle.toUpperCamelCase(method.outputType().reference().name())); + TypeNode repeatedResponseType = null; + for (Field field : pagedResponseMessage.fields()) { + if (field.isRepeated()) { + // Field is currently a List-type. + Preconditions.checkState( + !field.type().reference().generics().isEmpty(), + String.format("No generics found for field reference %s", field.type().reference())); + repeatedResponseType = TypeNode.withReference(field.type().reference().generics().get(0)); + } + } + Preconditions.checkNotNull( + repeatedResponseType, + String.format( + "No repeated type found for paged reesponse %s for method %s", + method.outputType().reference().name(), method.name())); + + // Create the PAGE_STR_DESC variable. + TypeNode pagedListDescriptorType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(PagedListDescriptor.class) + .setGenerics( + Arrays.asList(method.inputType(), method.outputType(), repeatedResponseType) + .stream() + .map(t -> t.reference()) + .collect(Collectors.toList())) + .build()); + String pageStrDescVarName = + String.format(PAGE_STR_DESC_PATTERN, JavaStyle.toUpperSnakeCase(method.name())); + VariableExpr pageStrDescVarExpr = + VariableExpr.withVariable( + Variable.builder() + .setType(pagedListDescriptorType) + .setName(pageStrDescVarName) + .build()); + + Expr pagedListResponseFactoryAssignExpr = + createPagedListResponseFactoryAssignExpr( + pageStrDescVarExpr, method, repeatedResponseType, types); + + exprs.add(pagedListResponseFactoryAssignExpr); + } + + return exprs; + } + + private static Expr createPagedListResponseFactoryAssignExpr( + VariableExpr pageStrDescVarExpr, + Method method, + TypeNode repeatedResponseType, + Map types) { + Preconditions.checkState( + method.isPaged(), String.format("Method %s is not paged", method.name())); + + // Create the PagedListResponseFactory. + TypeNode pagedResponseType = types.get(getPagedResponseTypeName(method.name())); + TypeNode apiFutureType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ApiFuture.class) + .setGenerics(Arrays.asList(pagedResponseType.reference())) + .build()); + + VariableExpr callableVarExpr = + VariableExpr.withVariable( + Variable.builder() + .setType( + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(UnaryCallable.class) + .setGenerics( + Arrays.asList( + method.inputType().reference(), + method.outputType().reference())) + .build())) + .setName("callable") + .build()); + VariableExpr requestVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(method.inputType()).setName("request").build()); + VariableExpr contextVarExpr = + VariableExpr.withVariable( + Variable.builder() + .setType(STATIC_TYPES.get("ApiCallContext")) + .setName("context") + .build()); + VariableExpr futureResponseVarExpr = + VariableExpr.withVariable( + Variable.builder() + .setType( + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ApiFuture.class) + .setGenerics(Arrays.asList(method.outputType().reference())) + .build())) + .setName("futureResponse") + .build()); + + TypeNode pageContextType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(PageContext.class) + .setGenerics( + Arrays.asList(method.inputType(), method.outputType(), repeatedResponseType) + .stream() + .map(t -> t.reference()) + .collect(Collectors.toList())) + .build()); + VariableExpr pageContextVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(pageContextType).setName("pageContext").build()); + AssignmentExpr pageContextAssignExpr = + AssignmentExpr.builder() + .setVariableExpr(pageContextVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr( + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("PageContext")) + .setMethodName("create") + .setArguments( + callableVarExpr, pageStrDescVarExpr, requestVarExpr, contextVarExpr) + .setReturnType(pageContextVarExpr.type()) + .build()) + .build(); + + Expr returnExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(types.get(getPagedResponseTypeName(method.name()))) + .setMethodName("createAsync") + .setArguments(pageContextVarExpr, futureResponseVarExpr) + .setReturnType(apiFutureType) + .build(); + + MethodDefinition getFuturePagedResponseMethod = + MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(apiFutureType) + .setName("getFuturePagedResponse") + .setArguments( + Arrays.asList( + callableVarExpr, requestVarExpr, contextVarExpr, futureResponseVarExpr) + .stream() + .map(v -> v.toBuilder().setIsDecl(true).build()) + .collect(Collectors.toList())) + .setBody(Arrays.asList(ExprStatement.withExpr(pageContextAssignExpr))) + .setReturnExpr(returnExpr) + .build(); + + // Create the variable. + TypeNode pagedResponseFactoryType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(PagedListResponseFactory.class) + .setGenerics( + Arrays.asList( + method.inputType(), + method.outputType(), + types.get(getPagedResponseTypeName(method.name()))) + .stream() + .map(t -> t.reference()) + .collect(Collectors.toList())) + .build()); + String varName = + String.format(PAGED_RESPONSE_FACTORY_PATTERN, JavaStyle.toUpperSnakeCase(method.name())); + VariableExpr pagedListResponseFactoryVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(pagedResponseFactoryType).setName(varName).build()); + AnonymousClassExpr factoryAnonClassExpr = + AnonymousClassExpr.builder() + .setType(pagedResponseFactoryType) + .setMethods(Arrays.asList(getFuturePagedResponseMethod)) + .build(); + + return AssignmentExpr.builder() + .setVariableExpr( + pagedListResponseFactoryVarExpr + .toBuilder() + .setIsDecl(true) + .setScope(ScopeNode.PRIVATE) + .setIsStatic(true) + .setIsFinal(true) + .build()) + .setValueExpr(factoryAnonClassExpr) + .build(); } private static List createClassMethods( diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java index 6759638b23..7c4738074f 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java @@ -82,6 +82,7 @@ public void generateServiceClasses() { + "\n" + "import static com.google.showcase.v1beta1.EchoClient.PagedExpandPagedResponse;\n" + "\n" + + "import com.google.api.core.ApiFuture;\n" + "import com.google.api.core.BetaApi;\n" + "import com.google.api.gax.core.GaxProperties;\n" + "import com.google.api.gax.core.GoogleCredentialsProvider;\n" @@ -89,15 +90,20 @@ public void generateServiceClasses() { + "import com.google.api.gax.grpc.GaxGrpcProperties;\n" + "import com.google.api.gax.grpc.GrpcTransportChannel;\n" + "import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;\n" + + "import com.google.api.gax.rpc.ApiCallContext;\n" + "import com.google.api.gax.rpc.ApiClientHeaderProvider;\n" + "import com.google.api.gax.rpc.ClientContext;\n" + "import com.google.api.gax.rpc.OperationCallSettings;\n" + + "import com.google.api.gax.rpc.PageContext;\n" + "import com.google.api.gax.rpc.PagedCallSettings;\n" + + "import com.google.api.gax.rpc.PagedListDescriptor;\n" + + "import com.google.api.gax.rpc.PagedListResponseFactory;\n" + "import com.google.api.gax.rpc.ServerStreamingCallSettings;\n" + "import com.google.api.gax.rpc.StreamingCallSettings;\n" + "import com.google.api.gax.rpc.StubSettings;\n" + "import com.google.api.gax.rpc.TransportChannelProvider;\n" + "import com.google.api.gax.rpc.UnaryCallSettings;\n" + + "import com.google.api.gax.rpc.UnaryCallable;\n" + "import com.google.common.collect.ImmutableList;\n" + "import com.google.longrunning.Operation;\n" + "import com.google.showcase.v1beta1.BlockRequest;\n" @@ -133,6 +139,26 @@ public void generateServiceClasses() { + " private final OperationCallSettings\n" + " waitOperationSettings;\n" + " private final UnaryCallSettings blockSettings;\n" + + " private static final PagedListResponseFactory<\n" + + " PagedExpandRequest, PagedExpandResponse, PagedExpandPagedResponse>\n" + + " PAGED_EXPAND_PAGE_STR_FACT =\n" + + " new PagedListResponseFactory<\n" + + " PagedExpandRequest, PagedExpandResponse, PagedExpandPagedResponse>()" + + " {\n" + + " @Override\n" + + " public ApiFuture getFuturePagedResponse(\n" + + " UnaryCallable callable,\n" + + " PagedExpandRequest request,\n" + + " ApiCallContext context,\n" + + " ApiFuture futureResponse) {\n" + + " PageContext" + + " pageContext =\n" + + " PageContext.create(callable, PAGED_EXPAND_PAGE_STR_DESC, request," + + " context);\n" + + " return PagedExpandPagedResponse.createAsync(pageContext," + + " futureResponse);\n" + + " }\n" + + " };\n" + "\n" + " public UnaryCallSettings echoSettings() {\n" + " return echoSettings;\n" From 4919bba7b3f59327f4338383a2265ac84f977e25 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 25 Aug 2020 15:17:07 -0700 Subject: [PATCH 2/6] fix: prevent duplicate MethodDefinition annotations --- .../engine/ast/MethodDefinition.java | 22 +++++++++++++------ .../engine/ast/MethodDefinitionTest.java | 22 ++++++++++++++++++- 2 files changed, 36 insertions(+), 8 deletions(-) diff --git a/src/main/java/com/google/api/generator/engine/ast/MethodDefinition.java b/src/main/java/com/google/api/generator/engine/ast/MethodDefinition.java index c37ab6579f..285fc898dd 100644 --- a/src/main/java/com/google/api/generator/engine/ast/MethodDefinition.java +++ b/src/main/java/com/google/api/generator/engine/ast/MethodDefinition.java @@ -19,6 +19,7 @@ import com.google.common.collect.ImmutableList; import java.util.Arrays; import java.util.Collections; +import java.util.LinkedHashSet; import java.util.List; import java.util.stream.Collectors; import javax.annotation.Nullable; @@ -122,10 +123,7 @@ public abstract static class Builder { public abstract Builder setHeaderCommentStatements( List headeCommentStatements); - public Builder setAnnotations(List annotations) { - annotationsBuilder().addAll(annotations); - return this; - } + public abstract Builder setAnnotations(List annotations); public abstract Builder setIsStatic(boolean isStatic); @@ -162,10 +160,10 @@ public Builder setArguments(VariableExpr... arguments) { // Private accessors. - abstract ImmutableList.Builder annotationsBuilder(); - abstract String name(); + abstract ImmutableList annotations(); + abstract TypeNode returnType(); abstract boolean isOverride(); @@ -231,9 +229,19 @@ public MethodDefinition build() { } // If this method overrides another, ensure that the Override annotaiton is the last one. + ImmutableList processedAnnotations = annotations(); if (isOverride()) { - annotationsBuilder().add(AnnotationNode.OVERRIDE); + processedAnnotations = + annotations() + .builder() + .addAll(annotations()) + .add(AnnotationNode.OVERRIDE) + .build(); } + // Remove duplicates while maintaining insertion order. + setAnnotations( + new LinkedHashSet(processedAnnotations) + .stream().collect(Collectors.toList())); MethodDefinition method = autoBuild(); diff --git a/src/test/java/com/google/api/generator/engine/ast/MethodDefinitionTest.java b/src/test/java/com/google/api/generator/engine/ast/MethodDefinitionTest.java index ddc92a1eee..4fe7a5c05b 100644 --- a/src/test/java/com/google/api/generator/engine/ast/MethodDefinitionTest.java +++ b/src/test/java/com/google/api/generator/engine/ast/MethodDefinitionTest.java @@ -14,6 +14,7 @@ package com.google.api.generator.engine.ast; +import static com.google.common.truth.Truth.assertThat; import static org.junit.Assert.assertThrows; import java.util.ArrayList; @@ -35,6 +36,24 @@ public void validMethodDefinition_basicWithComments() { // No exception thrown, we're good. } + @Test + public void validMethodDefinition_repeatedAnnotations() { + MethodDefinition method = + MethodDefinition.builder() + .setName("close") + .setAnnotations( + Arrays.asList( + AnnotationNode.withSuppressWarnings("all"), + AnnotationNode.DEPRECATED, + AnnotationNode.DEPRECATED)) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.VOID) + .setBody(Arrays.asList(ExprStatement.withExpr(createAssignmentExpr()))) + .build(); + assertThat(method.annotations()) + .containsExactly(AnnotationNode.withSuppressWarnings("all"), AnnotationNode.DEPRECATED); + } + @Test public void validMethodDefinition_basicWithReturnType() { MethodDefinition.builder() @@ -681,7 +700,8 @@ private static List createCommentStatements() { JavaDocComment.builder() .addComment("Constructs an instance of GrpcMyProtoStub, using the given settings.") .addComment( - "This is protected so that it is easy to make a subclass, but otherwise, the static factory methods should be preferred.") + "This is protected so that it is easy to make a subclass, but otherwise, the" + + " static factory methods should be preferred.") .build(); return Arrays.asList(CommentStatement.withComment(javaDocComment)); } From b77848e68a3815e274b4ba90a7a7f34636166cba Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 25 Aug 2020 15:22:47 -0700 Subject: [PATCH 3/6] feat: add descriptor fields to ServiceStubSettings codegen --- .../ServiceStubSettingsClassComposer.java | 209 +++++++++++++++++- .../ServiceStubSettingsClassComposerTest.java | 43 ++++ 2 files changed, 240 insertions(+), 12 deletions(-) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java index 79ea6a0117..6a319ca649 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java @@ -62,12 +62,14 @@ import com.google.api.generator.engine.ast.MethodDefinition; import com.google.api.generator.engine.ast.MethodInvocationExpr; import com.google.api.generator.engine.ast.NewObjectExpr; +import com.google.api.generator.engine.ast.NullObjectValue; import com.google.api.generator.engine.ast.Reference; import com.google.api.generator.engine.ast.ReferenceConstructorExpr; import com.google.api.generator.engine.ast.ReturnExpr; import com.google.api.generator.engine.ast.ScopeNode; import com.google.api.generator.engine.ast.Statement; import com.google.api.generator.engine.ast.StringObjectValue; +import com.google.api.generator.engine.ast.TernaryExpr; import com.google.api.generator.engine.ast.ThisObjectValue; import com.google.api.generator.engine.ast.ThrowExpr; import com.google.api.generator.engine.ast.TypeNode; @@ -96,6 +98,7 @@ import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Objects; import java.util.function.Function; import java.util.stream.Collectors; import javax.annotation.Generated; @@ -265,7 +268,8 @@ private static List createPagingStaticAssignExprs( Map messageTypes, Map types) { // TODO(miraleung): Add a test case for several such statements. - List exprs = new ArrayList<>(); + List descExprs = new ArrayList<>(); + List factoryExprs = new ArrayList<>(); for (Method method : service.methods()) { if (!method.isPaged()) { continue; @@ -290,7 +294,7 @@ private static List createPagingStaticAssignExprs( method.outputType().reference().name(), method.name())); // Create the PAGE_STR_DESC variable. - TypeNode pagedListDescriptorType = + TypeNode pagedListDescType = TypeNode.withReference( ConcreteReference.builder() .setClazz(PagedListDescriptor.class) @@ -302,21 +306,202 @@ private static List createPagingStaticAssignExprs( .build()); String pageStrDescVarName = String.format(PAGE_STR_DESC_PATTERN, JavaStyle.toUpperSnakeCase(method.name())); - VariableExpr pageStrDescVarExpr = + VariableExpr pagedListDescVarExpr = VariableExpr.withVariable( - Variable.builder() - .setType(pagedListDescriptorType) - .setName(pageStrDescVarName) - .build()); + Variable.builder().setType(pagedListDescType).setName(pageStrDescVarName).build()); - Expr pagedListResponseFactoryAssignExpr = + descExprs.add( + createPagedListDescriptorAssignExpr( + pagedListDescVarExpr, method, repeatedResponseType, types)); + factoryExprs.add( createPagedListResponseFactoryAssignExpr( - pageStrDescVarExpr, method, repeatedResponseType, types); - - exprs.add(pagedListResponseFactoryAssignExpr); + pagedListDescVarExpr, method, repeatedResponseType, types)); } - return exprs; + descExprs.addAll(factoryExprs); + return descExprs; + } + + private static Expr createPagedListDescriptorAssignExpr( + VariableExpr pagedListDescVarExpr, + Method method, + TypeNode repeatedResponseType, + Map types) { + MethodDefinition.Builder methodStarterBuilder = + MethodDefinition.builder().setIsOverride(true).setScope(ScopeNode.PUBLIC); + List anonClassMethods = new ArrayList<>(); + + // Create emptyToken method. + anonClassMethods.add( + methodStarterBuilder + .setReturnType(TypeNode.STRING) + .setName("emptyToken") + .setReturnExpr(ValueExpr.withValue(StringObjectValue.withValue(""))) + .build()); + + // Create injectToken method. + VariableExpr payloadVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(method.inputType()).setName("payload").build()); + VariableExpr strTokenVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(TypeNode.STRING).setName("token").build()); + TypeNode returnType = method.inputType(); + Expr newBuilderExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(method.inputType()) + .setMethodName("newBuilder") + .setArguments(payloadVarExpr) + .build(); + Expr returnExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(newBuilderExpr) + .setMethodName("setPageToken") + .setArguments(strTokenVarExpr) + .build(); + returnExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(returnExpr) + .setMethodName("build") + .setReturnType(returnType) + .build(); + anonClassMethods.add( + methodStarterBuilder + .setReturnType(method.inputType()) + .setName("injectToken") + .setArguments( + Arrays.asList(payloadVarExpr, strTokenVarExpr).stream() + .map(v -> v.toBuilder().setIsDecl(true).build()) + .collect(Collectors.toList())) + .setReturnExpr(returnExpr) + .build()); + + // Create injectPageSize method. + VariableExpr pageSizeVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(TypeNode.INT).setName("pageSize").build()); + // Re-declare for clarity and easier readeability. + returnType = method.inputType(); + returnExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(newBuilderExpr) + .setMethodName("setPageSize") + .setArguments(pageSizeVarExpr) + .build(); + returnExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(returnExpr) + .setMethodName("build") + .setReturnType(returnType) + .build(); + anonClassMethods.add( + methodStarterBuilder + .setReturnType(method.inputType()) + .setName("injectPageSize") + .setArguments( + Arrays.asList(payloadVarExpr, pageSizeVarExpr).stream() + .map(v -> v.toBuilder().setIsDecl(true).build()) + .collect(Collectors.toList())) + .setReturnExpr(returnExpr) + .build()); + + // TODO(miraleung): Test the edge cases where these proto fields aren't present. + // Create extractPageSize method. + returnType = TypeNode.INT_OBJECT; + anonClassMethods.add( + methodStarterBuilder + .setReturnType(returnType) + .setName("extractPageSize") + .setArguments(payloadVarExpr.toBuilder().setIsDecl(true).build()) + .setReturnExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(payloadVarExpr) + .setMethodName("getPageSize") + .setReturnType(returnType) + .build()) + .build()); + + // Create extractNextToken method. + returnType = TypeNode.STRING; + payloadVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(method.outputType()).setName("payload").build()); + anonClassMethods.add( + methodStarterBuilder + .setReturnType(returnType) + .setName("extractNextToken") + .setArguments(payloadVarExpr.toBuilder().setIsDecl(true).build()) + .setReturnExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(payloadVarExpr) + .setMethodName("getNextPageToken") + .setReturnType(returnType) + .build()) + .build()); + + // Create extractResources method. + returnType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(Iterable.class) + .setGenerics(Arrays.asList(repeatedResponseType.reference())) + .build()); + Expr getResponsesListExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(payloadVarExpr) + .setMethodName("getResponsesList") + .setReturnType(returnType) + .build(); + Expr conditionExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType( + TypeNode.withReference(ConcreteReference.withClazz(Objects.class))) + .setMethodName("equals") + .setArguments(getResponsesListExpr, ValueExpr.withValue(NullObjectValue.create())) + .setReturnType(TypeNode.BOOLEAN) + .build(); + Expr thenExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType( + TypeNode.withReference(ConcreteReference.withClazz(ImmutableList.class))) + .setGenerics(Arrays.asList(repeatedResponseType.reference())) + .setMethodName("of") + .setReturnType(returnType) + .build(); + + returnExpr = + TernaryExpr.builder() + .setConditionExpr(conditionExpr) + .setThenExpr(thenExpr) + .setElseExpr(getResponsesListExpr) + .build(); + anonClassMethods.add( + methodStarterBuilder + .setReturnType(returnType) + .setName("extractResources") + .setArguments(payloadVarExpr.toBuilder().setIsDecl(true).build()) + .setReturnExpr(returnExpr) + .build()); + + // Create the anonymous class. + AnonymousClassExpr pagedListDescAnonClassExpr = + AnonymousClassExpr.builder() + .setType(pagedListDescVarExpr.type()) + .setMethods(anonClassMethods) + .build(); + + // Declare and assign the variable. + return AssignmentExpr.builder() + .setVariableExpr( + pagedListDescVarExpr + .toBuilder() + .setIsDecl(true) + .setScope(ScopeNode.PRIVATE) + .setIsStatic(true) + .setIsFinal(true) + .build()) + .setValueExpr(pagedListDescAnonClassExpr) + .build(); } private static Expr createPagedListResponseFactoryAssignExpr( diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java index 7c4738074f..d5fa63a595 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java @@ -118,6 +118,7 @@ public void generateServiceClasses() { + "import com.google.showcase.v1beta1.WaitResponse;\n" + "import java.io.IOException;\n" + "import java.util.List;\n" + + "import java.util.Objects;\n" + "import javax.annotation.Generated;\n" + "\n" + "@BetaApi\n" @@ -139,6 +140,48 @@ public void generateServiceClasses() { + " private final OperationCallSettings\n" + " waitOperationSettings;\n" + " private final UnaryCallSettings blockSettings;\n" + + " private static final PagedListDescriptor\n" + + " PAGED_EXPAND_PAGE_STR_DESC =\n" + + " new PagedListDescriptor() {\n" + + " @Override\n" + + " public String emptyToken() {\n" + + " return \"\";\n" + + " }\n" + + "\n" + + " @Override\n" + + " public PagedExpandRequest injectToken(PagedExpandRequest payload, String" + + " token) {\n" + + " return" + + " PagedExpandRequest.newBuilder(payload).setPageToken(token).build();\n" + + " }\n" + + "\n" + + " @Override\n" + + " public PagedExpandRequest injectPageSize(PagedExpandRequest payload, int" + + " pageSize) {\n" + + " return" + + " PagedExpandRequest.newBuilder(payload).setPageSize(pageSize).build();\n" + + " }\n" + + "\n" + + " @Override\n" + + " public Integer extractPageSize(PagedExpandRequest payload) {\n" + + " return payload.getPageSize();\n" + + " }\n" + + "\n" + + " @Override\n" + + " public String extractNextToken(PagedExpandResponse payload) {\n" + + " return payload.getNextPageToken();\n" + + " }\n" + + "\n" + + " @Override\n" + + " public Iterable extractResources(PagedExpandResponse" + + " payload) {\n" + + " return Objects.equals(payload.getResponsesList(), null)\n" + + " ? ImmutableList.of()\n" + + " : payload.getResponsesList();\n" + + " }\n" + + " };\n" + " private static final PagedListResponseFactory<\n" + " PagedExpandRequest, PagedExpandResponse, PagedExpandPagedResponse>\n" + " PAGED_EXPAND_PAGE_STR_FACT =\n" From 861f142f6770c06e47b92f6f0e68b84d8acfe7d2 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 25 Aug 2020 15:59:26 -0700 Subject: [PATCH 4/6] feat: add starter Builder to ServiceStubSettings codegen --- .../ServiceStubSettingsClassComposer.java | 104 ++++++++++++++++++ .../ServiceStubSettingsClassComposerTest.java | 13 ++- 2 files changed, 116 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java index 6a319ca649..b153047559 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java @@ -112,6 +112,11 @@ public class ServiceStubSettingsClassComposer { private static final String PAGED_RESPONSE_FACTORY_PATTERN = "%s_PAGE_STR_FACT"; private static final String PAGED_RESPONSE_TYPE_NAME_PATTERN = "%sPagedResponse"; private static final String NESTED_BUILDER_CLASS_NAME = "Builder"; + private static final String NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_NAME = + "unaryMethodSettingsBuilders"; + private static final String NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_NAME = + "RETRYABLE_CODE_DEFINITIONS"; + private static final String NESTED_RETRY_PARAM_DEFINITIONS_VAR_NAME = "RETRY_PARAM_DEFINITIONS"; private static final String STUB_PATTERN = "%sStub"; private static final String LEFT_BRACE = "{"; @@ -124,6 +129,12 @@ public class ServiceStubSettingsClassComposer { private static final Map STATIC_TYPES = createStaticTypes(); private static final VariableExpr DEFAULT_SERVICE_SCOPES_VAR_EXPR = createDefaultServiceScopesVarExpr(); + private static final VariableExpr NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_EXPR = + createNestedUnaryMethodSettingsBuildersVarExpr(); + private static final VariableExpr NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR = + createNestedRetryableCodeDefinitionsVarExpr(); + private static final VariableExpr NESTED_RETRY_PARAM_DEFINITIONS_VAR_EXPR = + createNestedRetryParamDefinitionsVarExpr(); private ServiceStubSettingsClassComposer() {} @@ -1048,15 +1059,49 @@ private static ClassDefinition createNestedBuilderClass( String className = "Builder"; + TypeNode extendsType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(StubSettings.Builder.class) + .setGenerics( + Arrays.asList( + types.get(getServiceStubTypeName(service.name())), types.get(className)) + .stream() + .map(t -> t.reference()) + .collect(Collectors.toList())) + .build()); + // TODO(miraleung): Fill this out. return ClassDefinition.builder() .setIsNested(true) .setScope(ScopeNode.PUBLIC) .setIsStatic(true) .setName(className) + .setExtendsType(extendsType) + .setStatements(createNestedClassStatements()) .build(); } + private static List createNestedClassStatements() { + List varDeclExprs = new ArrayList<>(); + Function varDeclFn = + v -> v.toBuilder().setIsDecl(true).setScope(ScopeNode.PRIVATE).setIsFinal(true).build(); + varDeclExprs.add(varDeclFn.apply(NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_EXPR)); + + Function varStaticDeclFn = + v -> + v.toBuilder() + .setIsDecl(true) + .setScope(ScopeNode.PRIVATE) + .setIsStatic(true) + .setIsFinal(true) + .build(); + varDeclExprs.add(varStaticDeclFn.apply(NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR)); + varDeclExprs.add(varStaticDeclFn.apply(NESTED_RETRY_PARAM_DEFINITIONS_VAR_EXPR)); + + return varDeclExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); + } + private static Map createStaticTypes() { List concreteClazzes = Arrays.asList( @@ -1179,6 +1224,65 @@ private static VariableExpr createDefaultServiceScopesVarExpr() { Variable.builder().setName("DEFAULT_SERVICE_SCOPES").setType(listStringType).build()); } + private static VariableExpr createNestedUnaryMethodSettingsBuildersVarExpr() { + Reference builderRef = + ConcreteReference.builder() + .setClazz(UnaryCallSettings.Builder.class) + .setGenerics(Arrays.asList(TypeNode.WILDCARD_REFERENCE, TypeNode.WILDCARD_REFERENCE)) + .build(); + TypeNode varType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ImmutableList.class) + .setGenerics(Arrays.asList(builderRef)) + .build()); + return VariableExpr.withVariable( + Variable.builder() + .setType(varType) + .setName(NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_NAME) + .build()); + } + + private static VariableExpr createNestedRetryableCodeDefinitionsVarExpr() { + TypeNode immutableSetType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ImmutableSet.class) + .setGenerics(Arrays.asList(ConcreteReference.withClazz(StatusCode.Code.class))) + .build()); + TypeNode varType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ImmutableMap.class) + .setGenerics( + Arrays.asList(TypeNode.STRING, immutableSetType).stream() + .map(t -> t.reference()) + .collect(Collectors.toList())) + .build()); + return VariableExpr.withVariable( + Variable.builder() + .setType(varType) + .setName(NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_NAME) + .build()); + } + + private static VariableExpr createNestedRetryParamDefinitionsVarExpr() { + TypeNode varType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(ImmutableMap.class) + .setGenerics( + Arrays.asList(TypeNode.STRING, STATIC_TYPES.get("RetrySettings")).stream() + .map(t -> t.reference()) + .collect(Collectors.toList())) + .build()); + return VariableExpr.withVariable( + Variable.builder() + .setType(varType) + .setName(NESTED_RETRY_PARAM_DEFINITIONS_VAR_NAME) + .build()); + } + private static String getThisClassName(String serviceName) { return String.format(CLASS_NAME_PATTERN, JavaStyle.toUpperCamelCase(serviceName)); } diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java index d5fa63a595..78222adf5d 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java @@ -90,6 +90,7 @@ public void generateServiceClasses() { + "import com.google.api.gax.grpc.GaxGrpcProperties;\n" + "import com.google.api.gax.grpc.GrpcTransportChannel;\n" + "import com.google.api.gax.grpc.InstantiatingGrpcChannelProvider;\n" + + "import com.google.api.gax.retrying.RetrySettings;\n" + "import com.google.api.gax.rpc.ApiCallContext;\n" + "import com.google.api.gax.rpc.ApiClientHeaderProvider;\n" + "import com.google.api.gax.rpc.ClientContext;\n" @@ -99,12 +100,15 @@ public void generateServiceClasses() { + "import com.google.api.gax.rpc.PagedListDescriptor;\n" + "import com.google.api.gax.rpc.PagedListResponseFactory;\n" + "import com.google.api.gax.rpc.ServerStreamingCallSettings;\n" + + "import com.google.api.gax.rpc.StatusCode;\n" + "import com.google.api.gax.rpc.StreamingCallSettings;\n" + "import com.google.api.gax.rpc.StubSettings;\n" + "import com.google.api.gax.rpc.TransportChannelProvider;\n" + "import com.google.api.gax.rpc.UnaryCallSettings;\n" + "import com.google.api.gax.rpc.UnaryCallable;\n" + "import com.google.common.collect.ImmutableList;\n" + + "import com.google.common.collect.ImmutableMap;\n" + + "import com.google.common.collect.ImmutableSet;\n" + "import com.google.longrunning.Operation;\n" + "import com.google.showcase.v1beta1.BlockRequest;\n" + "import com.google.showcase.v1beta1.BlockResponse;\n" @@ -323,6 +327,13 @@ public void generateServiceClasses() { + " blockSettings = settingsBuilder.blockSettings().build();\n" + " }\n" + "\n" - + " public static class Builder {}\n" + + " public static class Builder extends StubSettings.Builder {\n" + + " private final ImmutableList>" + + " unaryMethodSettingsBuilders;\n" + + " private static final ImmutableMap>\n" + + " RETRYABLE_CODE_DEFINITIONS;\n" + + " private static final ImmutableMap" + + " RETRY_PARAM_DEFINITIONS;\n" + + " }\n" + "}\n"; } From 5f285df576a33a41076b11e9d5bc2c59cf89fbc9 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 25 Aug 2020 16:18:45 -0700 Subject: [PATCH 5/6] feat: add settings.builder decls to ServiceStubSettings codegen --- .../ServiceStubSettingsClassComposer.java | 62 ++++++++++++++----- .../ServiceStubSettingsClassComposerTest.java | 19 ++++++ 2 files changed, 67 insertions(+), 14 deletions(-) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java index b153047559..311f6fa1dd 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java @@ -147,7 +147,7 @@ public GapicClass generate( String pakkage = String.format("%s.stub", service.pakkage()); Map types = createDynamicTypes(service, pakkage); Map methodSettingsMemberVarExprs = - createClassMemberVarExprs(service, types); + createMethodSettingsClassMemberVarExprs(service, types, /* isNestedClass= */ false); String className = getThisClassName(service.name()); ClassDefinition classDef = @@ -184,22 +184,22 @@ private static TypeNode createExtendsType(Service service, Map .copyAndSetGenerics(Arrays.asList(thisClassType.reference()))); } - private static Map createClassMemberVarExprs( - Service service, Map types) { + private static Map createMethodSettingsClassMemberVarExprs( + Service service, Map types, boolean isNestedClass) { // Maintain insertion order. Map varExprs = new LinkedHashMap<>(); // Creates class variables Settings, e.g. echoSettings. // TODO(miraleung): Handle batching here. for (Method method : service.methods()) { - TypeNode settingsType = getCallSettingsType(method, types); + TypeNode settingsType = getCallSettingsType(method, types, isNestedClass); String varName = JavaStyle.toLowerCamelCase(String.format("%sSettings", method.name())); varExprs.put( varName, VariableExpr.withVariable( Variable.builder().setType(settingsType).setName(varName).build())); if (method.hasLro()) { - settingsType = getOperationCallSettingsType(method); + settingsType = getOperationCallSettingsType(method, isNestedClass); varName = JavaStyle.toLowerCamelCase(String.format("%sOperationSettings", method.name())); varExprs.put( varName, @@ -1071,6 +1071,9 @@ private static ClassDefinition createNestedBuilderClass( .collect(Collectors.toList())) .build()); + Map nestedMethodSettingsMemberVarExprs = + createMethodSettingsClassMemberVarExprs(service, types, /* isNestedClass= */ true); + // TODO(miraleung): Fill this out. return ClassDefinition.builder() .setIsNested(true) @@ -1078,16 +1081,26 @@ private static ClassDefinition createNestedBuilderClass( .setIsStatic(true) .setName(className) .setExtendsType(extendsType) - .setStatements(createNestedClassStatements()) + .setStatements(createNestedClassStatements(nestedMethodSettingsMemberVarExprs)) .build(); } - private static List createNestedClassStatements() { + private static List createNestedClassStatements( + Map nestedMethodSettingsMemberVarExprs) { List varDeclExprs = new ArrayList<>(); + + // Declare unaryMethodSettingsBuilders. Function varDeclFn = v -> v.toBuilder().setIsDecl(true).setScope(ScopeNode.PRIVATE).setIsFinal(true).build(); varDeclExprs.add(varDeclFn.apply(NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_EXPR)); + // Declare all the settings fields. + varDeclExprs.addAll( + nestedMethodSettingsMemberVarExprs.values().stream() + .map(v -> varDeclFn.apply(v)) + .collect(Collectors.toList())); + + // Declare the RETRYABLE_CODE_DEFNITIONS field. Function varStaticDeclFn = v -> v.toBuilder() @@ -1097,6 +1110,8 @@ private static List createNestedClassStatements() { .setIsFinal(true) .build(); varDeclExprs.add(varStaticDeclFn.apply(NESTED_RETRYABLE_CODE_DEFINITIONS_VAR_EXPR)); + + // Declare the RETRY_PARAM_DEFINITIONS field. varDeclExprs.add(varStaticDeclFn.apply(NESTED_RETRY_PARAM_DEFINITIONS_VAR_EXPR)); return varDeclExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); @@ -1299,22 +1314,35 @@ private static String getGrpcServiceStubTypeName(String serviceName) { return String.format(GRPC_SERVICE_STUB_PATTERN, JavaStyle.toUpperCamelCase(serviceName)); } - private static TypeNode getCallSettingsType(Method method, Map types) { + private static TypeNode getCallSettingsType( + Method method, Map types, final boolean isSettingsBuilder) { + Function typeMakerFn = + clz -> TypeNode.withReference(ConcreteReference.withClazz(clz)); // Default: No streaming. TypeNode callSettingsType = method.isPaged() - ? STATIC_TYPES.get("PagedCallSettings") - : STATIC_TYPES.get("UnaryCallSettings"); + ? typeMakerFn.apply( + isSettingsBuilder ? PagedCallSettings.Builder.class : PagedCallSettings.class) + : typeMakerFn.apply( + isSettingsBuilder ? UnaryCallSettings.Builder.class : UnaryCallSettings.class); // Streaming takes precendence over paging, as per the monolith's existing behavior. switch (method.stream()) { case SERVER: - callSettingsType = STATIC_TYPES.get("ServerStreamingCallSettings"); + callSettingsType = + typeMakerFn.apply( + isSettingsBuilder + ? ServerStreamingCallSettings.Builder.class + : ServerStreamingCallSettings.class); break; case CLIENT: // Fall through. case BIDI: - callSettingsType = STATIC_TYPES.get("StreamingCallSettings"); + callSettingsType = + typeMakerFn.apply( + isSettingsBuilder + ? StreamingCallSettings.Builder.class + : StreamingCallSettings.class); break; case NONE: // Fall through. @@ -1331,7 +1359,7 @@ private static TypeNode getCallSettingsType(Method method, Map return TypeNode.withReference(callSettingsType.reference().copyAndSetGenerics(generics)); } - private static TypeNode getOperationCallSettingsType(Method method) { + private static TypeNode getOperationCallSettingsType(Method method, boolean isSettingsBuilder) { Preconditions.checkState( method.hasLro(), String.format("Cannot get OperationCallSettings for non-LRO method %s", method.name())); @@ -1340,6 +1368,12 @@ private static TypeNode getOperationCallSettingsType(Method method) { generics.add(method.lro().responseType().reference()); generics.add(method.lro().metadataType().reference()); return TypeNode.withReference( - STATIC_TYPES.get("OperationCallSettings").reference().copyAndSetGenerics(generics)); + ConcreteReference.builder() + .setClazz( + isSettingsBuilder + ? OperationCallSettings.Builder.class + : OperationCallSettings.class) + .setGenerics(generics) + .build()); } } diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java index 78222adf5d..67133297f0 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java @@ -330,6 +330,25 @@ public void generateServiceClasses() { + " public static class Builder extends StubSettings.Builder {\n" + " private final ImmutableList>" + " unaryMethodSettingsBuilders;\n" + + " private final UnaryCallSettings.Builder" + + " echoSettings;\n" + + " private final ServerStreamingCallSettings.Builder" + + " expandSettings;\n" + + " private final StreamingCallSettings.Builder" + + " collectSettings;\n" + + " private final StreamingCallSettings.Builder" + + " chatSettings;\n" + + " private final StreamingCallSettings.Builder" + + " chatAgainSettings;\n" + + " private final PagedCallSettings.Builder<\n" + + " PagedExpandRequest, PagedExpandResponse, PagedExpandPagedResponse>\n" + + " pagedExpandSettings;\n" + + " private final UnaryCallSettings.Builder waitSettings;\n" + + " private final OperationCallSettings.Builder\n" + + " waitOperationSettings;\n" + + " private final UnaryCallSettings.Builder" + + " blockSettings;\n" + " private static final ImmutableMap>\n" + " RETRYABLE_CODE_DEFINITIONS;\n" + " private static final ImmutableMap" From dbc57359b23b9a22429d002f5e6bef64ac908f94 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 25 Aug 2020 17:59:33 -0700 Subject: [PATCH 6/6] feat: add first nested ctors to ServiceStubSettings codegen --- .../ServiceStubSettingsClassComposer.java | 179 +++++++++++++++++- .../ServiceStubSettingsClassComposerTest.java | 22 +++ 2 files changed, 199 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java index 311f6fa1dd..ce11685ae1 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java @@ -54,6 +54,7 @@ import com.google.api.generator.engine.ast.AnnotationNode; import com.google.api.generator.engine.ast.AnonymousClassExpr; import com.google.api.generator.engine.ast.AssignmentExpr; +import com.google.api.generator.engine.ast.CastExpr; import com.google.api.generator.engine.ast.ClassDefinition; import com.google.api.generator.engine.ast.ConcreteReference; import com.google.api.generator.engine.ast.Expr; @@ -118,6 +119,7 @@ public class ServiceStubSettingsClassComposer { "RETRYABLE_CODE_DEFINITIONS"; private static final String NESTED_RETRY_PARAM_DEFINITIONS_VAR_NAME = "RETRY_PARAM_DEFINITIONS"; private static final String STUB_PATTERN = "%sStub"; + private static final String SETTINGS_LITERAL = "Settings"; private static final String LEFT_BRACE = "{"; private static final String RIGHT_BRACE = "}"; @@ -285,11 +287,25 @@ private static List createPagingStaticAssignExprs( if (!method.isPaged()) { continue; } + // Find the repeated type. - Message pagedResponseMessage = - messageTypes.get(JavaStyle.toUpperCamelCase(method.outputType().reference().name())); + String pagedResponseMessageKey = + JavaStyle.toUpperCamelCase(method.outputType().reference().name()); + if (method.hasLro()) { + pagedResponseMessageKey = + JavaStyle.toUpperCamelCase(method.lro().responseType().reference().name()); + } + Message pagedResponseMessage = messageTypes.get(pagedResponseMessageKey); + Preconditions.checkNotNull( + pagedResponseMessage, + String.format( + "No method found for message type %s for method %s among %s", + pagedResponseMessageKey, method.name(), messageTypes.keySet())); TypeNode repeatedResponseType = null; for (Field field : pagedResponseMessage.fields()) { + Preconditions.checkState( + field != null, + String.format("Null field found for message %s", pagedResponseMessage.name())); if (field.isRepeated()) { // Field is currently a List-type. Preconditions.checkState( @@ -1082,6 +1098,7 @@ private static ClassDefinition createNestedBuilderClass( .setName(className) .setExtendsType(extendsType) .setStatements(createNestedClassStatements(nestedMethodSettingsMemberVarExprs)) + .setMethods(createNestedClassMethods(nestedMethodSettingsMemberVarExprs, types)) .build(); } @@ -1117,6 +1134,164 @@ private static List createNestedClassStatements( return varDeclExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); } + private static List createNestedClassMethods( + Map nestedMethodSettingsMemberVarExprs, Map types) { + List nestedClassMethods = new ArrayList<>(); + nestedClassMethods.addAll( + createNestedClassConstructorMethods(nestedMethodSettingsMemberVarExprs, types)); + + // TODO(miraleung): initDefaults(). + return nestedClassMethods; + } + + private static List createNestedClassConstructorMethods( + Map nestedMethodSettingsMemberVarExprs, Map types) { + TypeNode builderType = types.get(NESTED_BUILDER_CLASS_NAME); + + List ctorMethods = new ArrayList<>(); + + // First argument-less contructor. + ctorMethods.add( + MethodDefinition.constructorBuilder() + .setScope(ScopeNode.PROTECTED) + .setReturnType(builderType) + .setBody( + Arrays.asList( + ExprStatement.withExpr( + ReferenceConstructorExpr.thisBuilder() + .setType(builderType) + .setArguments( + CastExpr.builder() + .setType(STATIC_TYPES.get("ClientContext")) + .setExpr(ValueExpr.withValue(NullObjectValue.create())) + .build()) + .build()))) + .build()); + + // Second ctor that takes a clientContext argument. + VariableExpr clientContextVarExpr = + VariableExpr.withVariable( + Variable.builder() + .setType(STATIC_TYPES.get("ClientContext")) + .setName("clientContext") + .build()); + Reference pagedSettingsBuilderRef = + ConcreteReference.withClazz(PagedCallSettings.Builder.class); + Reference unaryCallSettingsBuilderRef = + ConcreteReference.withClazz(UnaryCallSettings.Builder.class); + Function isUnaryCallSettingsBuilderFn = + t -> + t.reference() + .copyAndSetGenerics(ImmutableList.of()) + .equals(unaryCallSettingsBuilderRef); + Function isPagedCallSettingsBuilderFn = + t -> t.reference().copyAndSetGenerics(ImmutableList.of()).equals(pagedSettingsBuilderRef); + Function builderToCallSettingsFn = + t -> + TypeNode.withReference( + VaporReference.builder() + .setName(t.reference().enclosingClassName()) + .setPakkage(t.reference().pakkage()) + .build()); + List ctorBodyExprs = new ArrayList<>(); + ctorBodyExprs.add( + ReferenceConstructorExpr.superBuilder() + .setType(builderType) + .setArguments(clientContextVarExpr) + .build()); + ctorBodyExprs.addAll( + nestedMethodSettingsMemberVarExprs.entrySet().stream() + .map( + e -> { + // Name is fooBarSettings. + VariableExpr varExpr = e.getValue(); + TypeNode varType = varExpr.type(); + if (!isPagedCallSettingsBuilderFn.apply(varType)) { + boolean isUnaryCallSettings = isUnaryCallSettingsBuilderFn.apply(varType); + return AssignmentExpr.builder() + .setVariableExpr(varExpr) + .setValueExpr( + MethodInvocationExpr.builder() + .setStaticReferenceType( + builderToCallSettingsFn.apply(varExpr.type())) + .setMethodName( + isUnaryCallSettings + ? "newUnaryCallSettingsBuilder" + : "newBuilder") + .setReturnType(varExpr.type()) + .build()) + .build(); + } + String varName = e.getKey(); + Preconditions.checkState( + varName.endsWith(SETTINGS_LITERAL), + String.format("%s expected to end with \"Settings\"", varName)); + varName = varName.substring(0, varName.length() - SETTINGS_LITERAL.length()); + varName = + String.format( + PAGED_RESPONSE_FACTORY_PATTERN, JavaStyle.toUpperSnakeCase(varName)); + VariableExpr argVar = + VariableExpr.withVariable( + Variable.builder() + .setType(STATIC_TYPES.get("PagedListResponseFactory")) + .setName(varName) + .build()); + return AssignmentExpr.builder() + .setVariableExpr(varExpr) + .setValueExpr( + MethodInvocationExpr.builder() + .setStaticReferenceType(builderToCallSettingsFn.apply(varExpr.type())) + .setMethodName("newBuilder") + .setArguments(argVar) + .setReturnType(varExpr.type()) + .build()) + .build(); + }) + .collect(Collectors.toList())); + + ctorBodyExprs.add( + AssignmentExpr.builder() + .setVariableExpr(NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_EXPR) + .setValueExpr( + MethodInvocationExpr.builder() + .setStaticReferenceType(STATIC_TYPES.get("ImmutableList")) + .setGenerics( + NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_EXPR + .type() + .reference() + .generics()) + .setMethodName("of") + .setArguments( + nestedMethodSettingsMemberVarExprs.values().stream() + .filter( + v -> + isUnaryCallSettingsBuilderFn.apply(v.type()) + || isPagedCallSettingsBuilderFn.apply(v.type())) + .collect(Collectors.toList())) + .setReturnType(NESTED_UNARY_METHOD_SETTINGS_BUILDERS_VAR_EXPR.type()) + .build()) + .build()); + + ctorBodyExprs.add( + MethodInvocationExpr.builder() + .setMethodName("initDefaults") + .setArguments(ValueExpr.withValue(ThisObjectValue.withType(builderType))) + .build()); + + ctorMethods.add( + MethodDefinition.constructorBuilder() + .setScope(ScopeNode.PROTECTED) + .setReturnType(builderType) + .setArguments(clientContextVarExpr.toBuilder().setIsDecl(true).build()) + .setBody( + ctorBodyExprs.stream() + .map(e -> ExprStatement.withExpr(e)) + .collect(Collectors.toList())) + .build()); + + return ctorMethods; + } + private static Map createStaticTypes() { List concreteClazzes = Arrays.asList( diff --git a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java index 67133297f0..fbf5fd8f54 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposerTest.java @@ -353,6 +353,28 @@ public void generateServiceClasses() { + " RETRYABLE_CODE_DEFINITIONS;\n" + " private static final ImmutableMap" + " RETRY_PARAM_DEFINITIONS;\n" + + "\n" + + " protected Builder() {\n" + + " this(((ClientContext) null));\n" + + " }\n" + + "\n" + + " protected Builder(ClientContext clientContext) {\n" + + " super(clientContext);\n" + + " echoSettings = UnaryCallSettings.newUnaryCallSettingsBuilder();\n" + + " expandSettings = ServerStreamingCallSettings.newBuilder();\n" + + " collectSettings = StreamingCallSettings.newBuilder();\n" + + " chatSettings = StreamingCallSettings.newBuilder();\n" + + " chatAgainSettings = StreamingCallSettings.newBuilder();\n" + + " pagedExpandSettings =" + + " PagedCallSettings.newBuilder(PAGED_EXPAND_PAGE_STR_FACT);\n" + + " waitSettings = UnaryCallSettings.newUnaryCallSettingsBuilder();\n" + + " waitOperationSettings = OperationCallSettings.newBuilder();\n" + + " blockSettings = UnaryCallSettings.newUnaryCallSettingsBuilder();\n" + + " unaryMethodSettingsBuilders =\n" + + " ImmutableList.>of(\n" + + " echoSettings, pagedExpandSettings, waitSettings, blockSettings);\n" + + " initDefaults(this);\n" + + " }\n" + " }\n" + "}\n"; }