From 3dffa8d11e05d5248fd499bfd21bdee20f37fae0 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Mon, 31 Aug 2020 13:55:43 -0700 Subject: [PATCH 1/8] feat: parse batching descriptor fields --- .../gapic/model/GapicBatchingSettings.java | 17 ++++++++++ .../BatchingSettingsConfigParser.java | 34 +++++++++++++++++++ .../composer/RetrySettingsComposerTest.java | 5 +++ .../gapic/model/GapicServiceConfigTest.java | 2 ++ .../BatchingSettingsConfigParserTest.java | 8 +++++ 5 files changed, 66 insertions(+) diff --git a/src/main/java/com/google/api/generator/gapic/model/GapicBatchingSettings.java b/src/main/java/com/google/api/generator/gapic/model/GapicBatchingSettings.java index 349cb479ab..2f7d4de00d 100644 --- a/src/main/java/com/google/api/generator/gapic/model/GapicBatchingSettings.java +++ b/src/main/java/com/google/api/generator/gapic/model/GapicBatchingSettings.java @@ -15,6 +15,8 @@ package com.google.api.generator.gapic.model; import com.google.auto.value.AutoValue; +import com.google.common.collect.ImmutableList; +import java.util.List; import javax.annotation.Nullable; @AutoValue @@ -25,6 +27,7 @@ public enum FlowControlLimitExceededBehavior { IGNORE }; + // Threshold fields. public abstract String protoPakkage(); public abstract String serviceName(); @@ -45,6 +48,14 @@ public enum FlowControlLimitExceededBehavior { public abstract FlowControlLimitExceededBehavior flowControlLimitExceededBehavior(); + // Batch descriptor fields. + public abstract String batchedFieldName(); + + public abstract ImmutableList discriminatorFieldNames(); + + @Nullable + public abstract String subresponseFieldName(); + public boolean matches(Service service, Method method) { return protoPakkage().equals(service.protoPakkage()) && serviceName().equals(service.name()) @@ -77,6 +88,12 @@ public abstract static class Builder { public abstract Builder setFlowControlLimitExceededBehavior( FlowControlLimitExceededBehavior behavior); + public abstract Builder setBatchedFieldName(String batchedFieldName); + + public abstract Builder setDiscriminatorFieldNames(List discriminatorFieldNames); + + public abstract Builder setSubresponseFieldName(String subresponseFieldName); + public abstract GapicBatchingSettings build(); } } diff --git a/src/main/java/com/google/api/generator/gapic/protoparser/BatchingSettingsConfigParser.java b/src/main/java/com/google/api/generator/gapic/protoparser/BatchingSettingsConfigParser.java index 721b79beea..79a1c9817d 100644 --- a/src/main/java/com/google/api/generator/gapic/protoparser/BatchingSettingsConfigParser.java +++ b/src/main/java/com/google/api/generator/gapic/protoparser/BatchingSettingsConfigParser.java @@ -38,6 +38,8 @@ public class BatchingSettingsConfigParser { private static String YAML_KEY_METHODS = "methods"; private static String YAML_KEY_BATCHING = "batching"; private static String YAML_KEY_THRESHOLDS = "thresholds"; + private static String YAML_KEY_DESCRIPTOR = "batch_descriptor"; + private static String YAML_KEY_BATCHING_ELEMENT_COUNT_THRESHOLD = "element_count_threshold"; private static String YAML_KEY_BATCHING_DELAY_THRESHOLD_MILLIS = "delay_threshold_millis"; private static String YAML_KEY_BATCHING_REQUEST_BYTE_THRESHOLD = "request_byte_threshold"; @@ -46,6 +48,10 @@ public class BatchingSettingsConfigParser { private static String YAML_KEY_BATCHING_FLOW_CONTROL_LIMIT_EXCEEDED_BEHAVIOR = "flow_control_limit_exceeded_behavior"; + private static String YAML_KEY_DESCRIPTOR_BATCHED_FIELD = "batched_field"; + private static String YAML_KEY_DESCRIPTOR_DISCRIMINATOR_FIELD = "discriminator_fields"; + private static String YAML_KEY_DESCRIPTOR_SUBRESPONSE_FIELD = "subresponse_field"; + public static Optional> parse( Optional gapicYamlConfigFilePathOpt) { return gapicYamlConfigFilePathOpt.isPresent() @@ -94,6 +100,13 @@ private static Optional> parseFromMap(Map batchingYamlConfig = (Map) batchingOuterYamlConfig.get(YAML_KEY_THRESHOLDS); Preconditions.checkState( @@ -147,6 +160,27 @@ private static Optional> parseFromMap(Map descriptorYamlConfig = + (Map) batchingOuterYamlConfig.get(YAML_KEY_DESCRIPTOR); + Preconditions.checkState( + descriptorYamlConfig.containsKey(YAML_KEY_DESCRIPTOR_BATCHED_FIELD) + && descriptorYamlConfig.containsKey(YAML_KEY_DESCRIPTOR_DISCRIMINATOR_FIELD), + String.format( + "Batching descriptor YAML config is missing one of %s or %s fields", + YAML_KEY_DESCRIPTOR_BATCHED_FIELD, YAML_KEY_DESCRIPTOR_DISCRIMINATOR_FIELD)); + + settingsBuilder.setBatchedFieldName( + (String) descriptorYamlConfig.get(YAML_KEY_DESCRIPTOR_BATCHED_FIELD)); + settingsBuilder.setDiscriminatorFieldNames( + (List) descriptorYamlConfig.get(YAML_KEY_DESCRIPTOR_DISCRIMINATOR_FIELD)); + + if (descriptorYamlConfig.containsKey(YAML_KEY_DESCRIPTOR_SUBRESPONSE_FIELD)) { + settingsBuilder.setSubresponseFieldName( + (String) descriptorYamlConfig.get(YAML_KEY_DESCRIPTOR_SUBRESPONSE_FIELD)); + } + settings.add(settingsBuilder.build()); } } diff --git a/src/test/java/com/google/api/generator/gapic/composer/RetrySettingsComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/RetrySettingsComposerTest.java index b9c02053a3..27eedd2d68 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/RetrySettingsComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/RetrySettingsComposerTest.java @@ -409,6 +409,9 @@ public void batchingSettings_minimalFlowControlSettings() { .setElementCountThreshold(100) .setRequestByteThreshold(1048576) .setDelayThresholdMillis(10) + .setBatchedFieldName("messages") + .setDiscriminatorFieldNames(Arrays.asList("topic")) + .setSubresponseFieldName("message_ids") .build(); Expr builderExpr = @@ -487,6 +490,8 @@ public void batchingSettings_fullFlowControlSettings() { .setFlowControlByteLimit(10485760) .setFlowControlLimitExceededBehavior( GapicBatchingSettings.FlowControlLimitExceededBehavior.THROW_EXCEPTION) + .setBatchedFieldName("entries") + .setDiscriminatorFieldNames(Arrays.asList("log_name", "resource", "labels")) .build(); Expr builderExpr = diff --git a/src/test/java/com/google/api/generator/gapic/model/GapicServiceConfigTest.java b/src/test/java/com/google/api/generator/gapic/model/GapicServiceConfigTest.java index 6b6618a2dd..7d1ba5c9d5 100644 --- a/src/test/java/com/google/api/generator/gapic/model/GapicServiceConfigTest.java +++ b/src/test/java/com/google/api/generator/gapic/model/GapicServiceConfigTest.java @@ -147,6 +147,8 @@ public void serviceConfig_withBatchingSettings() { .setElementCountThreshold(1000) .setRequestByteThreshold(2000) .setDelayThresholdMillis(3000) + .setBatchedFieldName("name") + .setDiscriminatorFieldNames(Arrays.asList("severity")) .build(); Optional> batchingSettingsOpt = Optional.of(Arrays.asList(origBatchingSetting)); diff --git a/src/test/java/com/google/api/generator/gapic/protoparser/BatchingSettingsConfigParserTest.java b/src/test/java/com/google/api/generator/gapic/protoparser/BatchingSettingsConfigParserTest.java index ed420d87a2..92ba2e5094 100644 --- a/src/test/java/com/google/api/generator/gapic/protoparser/BatchingSettingsConfigParserTest.java +++ b/src/test/java/com/google/api/generator/gapic/protoparser/BatchingSettingsConfigParserTest.java @@ -75,6 +75,10 @@ public void parseBatchingSettings_logging() { assertEquals( GapicBatchingSettings.FlowControlLimitExceededBehavior.THROW_EXCEPTION, setting.flowControlLimitExceededBehavior()); + + assertEquals("entries", setting.batchedFieldName()); + assertThat(setting.discriminatorFieldNames()).containsExactly("log_name", "resource", "labels"); + assertThat(setting.subresponseFieldName()).isNull(); } @Test @@ -102,5 +106,9 @@ public void parseBatchingSettings_pubsub() { assertEquals( GapicBatchingSettings.FlowControlLimitExceededBehavior.IGNORE, setting.flowControlLimitExceededBehavior()); + + assertEquals("messages", setting.batchedFieldName()); + assertThat(setting.discriminatorFieldNames()).containsExactly("topic"); + assertEquals("message_ids", setting.subresponseFieldName()); } } From c656ad1e79eee5d3aa631b91a738709d2495adcb Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Mon, 31 Aug 2020 14:13:01 -0700 Subject: [PATCH 2/8] feat: add Field.isMap and map-parsing test --- .../api/generator/gapic/model/Field.java | 6 +++- .../generator/gapic/protoparser/Parser.java | 1 + .../gapic/protoparser/TypeParser.java | 8 ++--- .../gapic/protoparser/ParserTest.java | 20 +++++++++++++ .../generator/gapic/testdata/testing.proto | 30 ++++++++++--------- 5 files changed, 46 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/google/api/generator/gapic/model/Field.java b/src/main/java/com/google/api/generator/gapic/model/Field.java index 0d26f33b09..8f6d10b2af 100644 --- a/src/main/java/com/google/api/generator/gapic/model/Field.java +++ b/src/main/java/com/google/api/generator/gapic/model/Field.java @@ -26,6 +26,8 @@ public abstract class Field { public abstract boolean isRepeated(); + public abstract boolean isMap(); + @Nullable public abstract ResourceReference resourceReference(); @@ -34,7 +36,7 @@ public boolean hasResourceReference() { } public static Builder builder() { - return new AutoValue_Field.Builder().setIsRepeated(false); + return new AutoValue_Field.Builder().setIsRepeated(false).setIsMap(false); } @AutoValue.Builder @@ -45,6 +47,8 @@ public abstract static class Builder { public abstract Builder setIsRepeated(boolean isRepeated); + public abstract Builder setIsMap(boolean isMap); + public abstract Builder setResourceReference(ResourceReference resourceReference); public abstract Field build(); diff --git a/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java b/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java index 9b7a70ddd5..275ec8d5cb 100644 --- a/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java +++ b/src/main/java/com/google/api/generator/gapic/protoparser/Parser.java @@ -362,6 +362,7 @@ private static Field parseField(FieldDescriptor fieldDescriptor, Descriptor mess .setName(fieldDescriptor.getName()) .setType(TypeParser.parseType(fieldDescriptor)) .setIsRepeated(fieldDescriptor.isRepeated()) + .setIsMap(fieldDescriptor.isMapField()) .setResourceReference(resourceReference) .build(); } diff --git a/src/main/java/com/google/api/generator/gapic/protoparser/TypeParser.java b/src/main/java/com/google/api/generator/gapic/protoparser/TypeParser.java index 5fd58a250f..ae8439a647 100644 --- a/src/main/java/com/google/api/generator/gapic/protoparser/TypeParser.java +++ b/src/main/java/com/google/api/generator/gapic/protoparser/TypeParser.java @@ -61,14 +61,14 @@ public class TypeParser { .build(); public static TypeNode parseType(@Nonnull FieldDescriptor field) { - if (field.isRepeated()) { - return createListType(field); - } - if (field.isMapField()) { return createMapType(field); } + if (field.isRepeated()) { + return createListType(field); + } + // Parse basic type. JavaType protoFieldType = field.getJavaType(); boolean isEnum = protoFieldType.equals(JavaType.ENUM); diff --git a/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java b/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java index 67e2b8d22f..4557598770 100644 --- a/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java +++ b/src/test/java/com/google/api/generator/gapic/protoparser/ParserTest.java @@ -20,6 +20,7 @@ import static junit.framework.Assert.assertTrue; import static org.junit.Assert.assertThrows; +import com.google.api.generator.engine.ast.ConcreteReference; import com.google.api.generator.engine.ast.Reference; import com.google.api.generator.engine.ast.TypeNode; import com.google.api.generator.engine.ast.VaporReference; @@ -325,6 +326,25 @@ public void parseMessages_fieldsHaveResourceReferences() { assertFalse(resourceReference.isChildType()); } + @Test + public void parseFields_mapType() { + FileDescriptor testingFileDescriptor = TestingOuterClass.getDescriptor(); + ServiceDescriptor testingService = testingFileDescriptor.getServices().get(0); + assertEquals(testingService.getName(), "Testing"); + + Map messageTypes = Parser.parseMessages(testingFileDescriptor); + Message message = messageTypes.get("Session"); + Field field = message.fieldMap().get("session_ids_to_descriptor"); + assertEquals( + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(Map.class) + .setGenerics( + Arrays.asList(TypeNode.INT_OBJECT.reference(), TypeNode.STRING.reference())) + .build()), + field.type()); + } + @Test public void parseResourceNames_inputTypeHasReferenceNotInMethodSignature() { FileDescriptor testingFileDescriptor = TestingOuterClass.getDescriptor(); diff --git a/src/test/java/com/google/api/generator/gapic/testdata/testing.proto b/src/test/java/com/google/api/generator/gapic/testdata/testing.proto index 571a4d07b0..5f8fdb2e5c 100644 --- a/src/test/java/com/google/api/generator/gapic/testdata/testing.proto +++ b/src/test/java/com/google/api/generator/gapic/testdata/testing.proto @@ -133,6 +133,8 @@ message Session { // Required. The version this session is using. Version version = 2; + + map session_ids_to_descriptor = 3; } // The request for the CreateSession method. @@ -181,8 +183,8 @@ message CreateSessionRequest { // The request for the GetSession method. message GetSessionRequest { // The session to be retrieved. - string name = 1 [ - (google.api.resource_reference).type = "showcase.googleapis.com/Session"]; + string name = 1 [(google.api.resource_reference).type = + "showcase.googleapis.com/Session"]; } // The request for the ListSessions method. @@ -207,15 +209,15 @@ message ListSessionsResponse { // Request for the DeleteSession method. message DeleteSessionRequest { // The session to be deleted. - string name = 1 [ - (google.api.resource_reference).type = "showcase.googleapis.com/Session"]; + string name = 1 [(google.api.resource_reference).type = + "showcase.googleapis.com/Session"]; } // Request message for reporting on a session. message ReportSessionRequest { // The session to be reported on. - string name = 1 [ - (google.api.resource_reference).type = "showcase.googleapis.com/Session"]; + string name = 1 [(google.api.resource_reference).type = + "showcase.googleapis.com/Session"]; } // Response message for reporting on a session. @@ -327,8 +329,8 @@ message Issue { // The request for the ListTests method. message ListTestsRequest { // The session. - string parent = 1 [ - (google.api.resource_reference).type = "showcase.googleapis.com/Session"]; + string parent = 1 [(google.api.resource_reference).type = + "showcase.googleapis.com/Session"]; // The maximum number of tests to return per page. int32 page_size = 2; @@ -352,8 +354,8 @@ message TestRun { // The name of the test. // The tests/* portion of the names are hard-coded, and do not change // from session to session. - string test = 1 [ - (google.api.resource_reference).type = "showcase.googleapis.com/Test"]; + string test = 1 + [(google.api.resource_reference).type = "showcase.googleapis.com/Test"]; // An issue found with the test run. If empty, this test run was successful. Issue issue = 2; @@ -362,14 +364,14 @@ message TestRun { // Request message for deleting a test. message DeleteTestRequest { // The test to be deleted. - string name = 1 [ - (google.api.resource_reference).type = "showcase.googleapis.com/Test"]; + string name = 1 + [(google.api.resource_reference).type = "showcase.googleapis.com/Test"]; } message VerifyTestRequest { // The test to have an answer registered to it. - string name = 1 [ - (google.api.resource_reference).type = "showcase.googleapis.com/Test"]; + string name = 1 + [(google.api.resource_reference).type = "showcase.googleapis.com/Test"]; // The answer from the test. bytes answer = 2; From bf28e84678b2d5fdb576cd4721c07487a432dd95 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 1 Sep 2020 13:53:11 -0700 Subject: [PATCH 3/8] feat: add initial batching descriptor field to ServiceStubSettings --- .../composer/BatchingDescriptorComposer.java | 241 ++++++++++++++++++ .../ServiceStubSettingsClassComposer.java | 11 + .../api/generator/gapic/composer/BUILD.bazel | 1 + .../BatchingDescriptorComposerTest.java | 230 +++++++++++++++++ .../ServiceStubSettingsClassComposerTest.java | 67 +++++ 5 files changed, 550 insertions(+) create mode 100644 src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java create mode 100644 src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java diff --git a/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java b/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java new file mode 100644 index 0000000000..3677a02b79 --- /dev/null +++ b/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java @@ -0,0 +1,241 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.api.generator.gapic.composer; + +import com.google.api.gax.batching.PartitionKey; +import com.google.api.gax.batching.RequestBuilder; +import com.google.api.gax.rpc.BatchedRequestIssuer; +import com.google.api.gax.rpc.BatchingDescriptor; +import com.google.api.generator.engine.ast.AnonymousClassExpr; +import com.google.api.generator.engine.ast.AssignmentExpr; +import com.google.api.generator.engine.ast.ConcreteReference; +import com.google.api.generator.engine.ast.Expr; +import com.google.api.generator.engine.ast.ExprStatement; +import com.google.api.generator.engine.ast.IfStatement; +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.Reference; +import com.google.api.generator.engine.ast.ScopeNode; +import com.google.api.generator.engine.ast.TypeNode; +import com.google.api.generator.engine.ast.Variable; +import com.google.api.generator.engine.ast.VariableExpr; +import com.google.api.generator.gapic.model.GapicBatchingSettings; +import com.google.api.generator.gapic.model.Message; +import com.google.api.generator.gapic.model.Method; +import com.google.api.generator.gapic.utils.JavaStyle; +import com.google.common.base.Preconditions; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; +import java.util.Map; +import java.util.Objects; +import java.util.stream.Collectors; + +public class BatchingDescriptorComposer { + private static final String BATCHING_DESC_PATTERN = "%s_BATCHING_DESC"; + + private static final Reference BATCHING_DESCRIPTOR_REF = + ConcreteReference.withClazz(BatchingDescriptor.class); + private static final Reference REQUEST_BUILDER_REF = + ConcreteReference.withClazz(RequestBuilder.class); + private static final Reference BATCHED_REQUEST_ISSUER_REF = + ConcreteReference.withClazz(BatchedRequestIssuer.class); + + private static final TypeNode PARTITION_KEY_TYPE = toType(PartitionKey.class); + + private static final String ADD_ALL_METHOD_PATTERN = "addAll%s"; + private static final String GET_LIST_METHOD_PATTERN = "get%sList"; + + public static Expr createBatchingDescriptorFieldDeclExpr( + Method method, GapicBatchingSettings batchingSettings, Map messageTypes) { + List javaMethods = new ArrayList<>(); + javaMethods.add(createGetBatchPartitionKeyMethod(method, batchingSettings, messageTypes)); + javaMethods.add(createGetRequestBuilderMethod(method, batchingSettings)); + + TypeNode batchingDescriptorType = + toType(BATCHING_DESCRIPTOR_REF, method.inputType(), method.outputType()); + AnonymousClassExpr batchingDescriptorClassExpr = + AnonymousClassExpr.builder() + .setType(batchingDescriptorType) + .setMethods(javaMethods) + .build(); + + String varName = + String.format(BATCHING_DESC_PATTERN, JavaStyle.toUpperSnakeCase(method.name())); + return AssignmentExpr.builder() + .setVariableExpr( + VariableExpr.builder() + .setVariable( + Variable.builder().setType(batchingDescriptorType).setName(varName).build()) + .setIsDecl(true) + .setScope(ScopeNode.PRIVATE) + .setIsStatic(true) + .setIsFinal(true) + .build()) + .setValueExpr(batchingDescriptorClassExpr) + .build(); + } + + private static MethodDefinition createGetBatchPartitionKeyMethod( + Method method, GapicBatchingSettings batchingSettings, Map messageTypes) { + String methodInputTypeName = method.inputType().reference().name(); + Message inputMessage = messageTypes.get(methodInputTypeName); + Preconditions.checkNotNull( + inputMessage, + String.format( + "Message %s not found for RPC method %s", methodInputTypeName, method.name())); + + VariableExpr requestVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(method.inputType()).setName("request").build()); + + List partitionKeyArgExprs = new ArrayList<>(); + for (String discriminatorFieldName : batchingSettings.discriminatorFieldNames()) { + Preconditions.checkNotNull( + inputMessage.fieldMap().get(discriminatorFieldName), + String.format( + "Batching discriminator field %s not found in message %s", + discriminatorFieldName, inputMessage.name())); + String getterMethodName = + String.format("get%s", JavaStyle.toUpperCamelCase(discriminatorFieldName)); + partitionKeyArgExprs.add( + MethodInvocationExpr.builder() + .setExprReferenceExpr(requestVarExpr) + .setMethodName(getterMethodName) + .build()); + } + Expr returnExpr = + NewObjectExpr.builder() + .setType(PARTITION_KEY_TYPE) + .setArguments(partitionKeyArgExprs) + .build(); + + return MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(PARTITION_KEY_TYPE) + .setName("getBatchPartitionKey") + .setArguments(requestVarExpr.toBuilder().setIsDecl(true).build()) + .setReturnExpr(returnExpr) + .build(); + } + + private static MethodDefinition createGetRequestBuilderMethod( + Method method, GapicBatchingSettings batchingSettings) { + TypeNode builderType = toType(REQUEST_BUILDER_REF, method.inputType()); + VariableExpr builderVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(builderType).setName("builder").build()); + VariableExpr requestVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(method.inputType()).setName("request").build()); + + Expr toBuilderExpr = + AssignmentExpr.builder() + .setVariableExpr(builderVarExpr) + .setValueExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(requestVarExpr) + .setMethodName("toBuilder") + .setReturnType(builderType) + .build()) + .build(); + + String upperBatchedFieldName = JavaStyle.toUpperCamelCase(batchingSettings.batchedFieldName()); + String getFooListMethodName = String.format(GET_LIST_METHOD_PATTERN, upperBatchedFieldName); + Expr getFooListExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(requestVarExpr) + .setMethodName(getFooListMethodName) + .build(); + + String addAllMethodName = String.format(ADD_ALL_METHOD_PATTERN, upperBatchedFieldName); + Expr addAllExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(builderVarExpr) + .setMethodName(addAllMethodName) + .setArguments(getFooListExpr) + .build(); + + MethodDefinition appendRequestMethod = + MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.VOID) + .setName("appendRequest") + .setArguments(requestVarExpr.toBuilder().setIsDecl(true).build()) + .setBody( + Arrays.asList( + IfStatement.builder() + .setConditionExpr( + MethodInvocationExpr.builder() + .setStaticReferenceType(toType(Objects.class)) + .setMethodName("isNull") + .setArguments(builderVarExpr) + .setReturnType(TypeNode.BOOLEAN) + .build()) + .setBody(Arrays.asList(ExprStatement.withExpr(toBuilderExpr))) + .setElseBody(Arrays.asList(ExprStatement.withExpr(addAllExpr))) + .build())) + .build(); + + MethodDefinition buildMethod = + MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(method.inputType()) + .setName("build") + .setReturnExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(builderVarExpr) + .setMethodName("build") + .setReturnType(method.inputType()) + .build()) + .build(); + + AnonymousClassExpr requestBuilderAnonClassExpr = + AnonymousClassExpr.builder() + .setType(builderType) + .setStatements( + Arrays.asList( + ExprStatement.withExpr( + builderVarExpr + .toBuilder() + .setIsDecl(true) + .setScope(ScopeNode.PRIVATE) + .build()))) + .setMethods(Arrays.asList(appendRequestMethod, buildMethod)) + .build(); + + return MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(builderType) + .setName("getRequestBuilder") + .setReturnExpr(requestBuilderAnonClassExpr) + .build(); + } + + private static TypeNode toType(Class clazz) { + return TypeNode.withReference(ConcreteReference.withClazz(clazz)); + } + + private static TypeNode toType(Reference reference, TypeNode... types) { + return TypeNode.withReference( + reference.copyAndSetGenerics( + Arrays.asList(types).stream().map(t -> t.reference()).collect(Collectors.toList()))); + } +} 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 737d9054b6..4b8960b16f 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 @@ -283,6 +283,17 @@ private static List createClassStatements( memberVarExprs.addAll( createPagingStaticAssignExprs(service, serviceConfig, messageTypes, types)); + + for (Method method : service.methods()) { + Optional batchingSettingOpt = + serviceConfig.getBatchingSetting(service, method); + if (batchingSettingOpt.isPresent()) { + memberVarExprs.add( + BatchingDescriptorComposer.createBatchingDescriptorFieldDeclExpr( + method, batchingSettingOpt.get(), messageTypes)); + } + } + return memberVarExprs.stream().map(e -> ExprStatement.withExpr(e)).collect(Collectors.toList()); } diff --git a/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel b/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel index a18b524847..740ba1b5f2 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel +++ b/src/test/java/com/google/api/generator/gapic/composer/BUILD.bazel @@ -1,6 +1,7 @@ package(default_visibility = ["//visibility:public"]) TESTS = [ + "BatchingDescriptorComposerTest", "ComposerTest", "GrpcServiceCallableFactoryClassComposerTest", "GrpcServiceStubClassComposerTest", diff --git a/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java new file mode 100644 index 0000000000..3b5dd833ac --- /dev/null +++ b/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java @@ -0,0 +1,230 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.api.generator.gapic.composer; + +import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; + +import com.google.api.generator.engine.ast.Expr; +import com.google.api.generator.engine.writer.JavaWriterVisitor; +import com.google.api.generator.gapic.model.GapicBatchingSettings; +import com.google.api.generator.gapic.model.GapicServiceConfig; +import com.google.api.generator.gapic.model.Message; +import com.google.api.generator.gapic.model.Method; +import com.google.api.generator.gapic.model.ResourceName; +import com.google.api.generator.gapic.model.Service; +import com.google.api.generator.gapic.protoparser.BatchingSettingsConfigParser; +import com.google.api.generator.gapic.protoparser.Parser; +import com.google.api.generator.gapic.protoparser.ServiceConfigParser; +import com.google.logging.v2.LogEntryProto; +import com.google.logging.v2.LoggingConfigProto; +import com.google.logging.v2.LoggingMetricsProto; +import com.google.logging.v2.LoggingProto; +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.ServiceDescriptor; +import com.google.pubsub.v1.PubsubProto; +import google.cloud.CommonResources; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import org.junit.Before; +import org.junit.Test; + +public class BatchingDescriptorComposerTest { + private static final String TESTFILES_DIRECTORY = + "src/test/java/com/google/api/generator/gapic/testdata/"; + + private JavaWriterVisitor writerVisitor; + + @Before + public void setUp() { + writerVisitor = new JavaWriterVisitor(); + } + + @Test + public void batchingDescriptor_hasSubresponseField() { + FileDescriptor serviceFileDescriptor = PubsubProto.getDescriptor(); + FileDescriptor commonResourcesFileDescriptor = CommonResources.getDescriptor(); + ServiceDescriptor serviceDescriptor = serviceFileDescriptor.getServices().get(0); + assertEquals("Publisher", serviceDescriptor.getName()); + + Map resourceNames = new HashMap<>(); + resourceNames.putAll(Parser.parseResourceNames(serviceFileDescriptor)); + resourceNames.putAll(Parser.parseResourceNames(commonResourcesFileDescriptor)); + + Map messageTypes = Parser.parseMessages(serviceFileDescriptor); + + Set outputResourceNames = new HashSet<>(); + List services = + Parser.parseService( + serviceFileDescriptor, messageTypes, resourceNames, outputResourceNames); + + String filename = "pubsub_gapic.yaml"; + Path path = Paths.get(TESTFILES_DIRECTORY, filename); + Optional> batchingSettingsOpt = + BatchingSettingsConfigParser.parse(Optional.of(path.toString())); + assertTrue(batchingSettingsOpt.isPresent()); + + String jsonFilename = "pubsub_grpc_service_config.json"; + Path jsonPath = Paths.get(TESTFILES_DIRECTORY, jsonFilename); + Optional configOpt = + ServiceConfigParser.parse(jsonPath.toString(), batchingSettingsOpt); + assertTrue(configOpt.isPresent()); + GapicServiceConfig config = configOpt.get(); + + Service service = services.get(0); + assertEquals("Publisher", service.name()); + Method method = findMethod(service, "Publish"); + + GapicBatchingSettings batchingSetting = batchingSettingsOpt.get().get(0); + assertEquals("Publish", batchingSetting.methodName()); + Expr batchingDescriptorExpr = + BatchingDescriptorComposer.createBatchingDescriptorFieldDeclExpr( + method, batchingSetting, messageTypes); + + batchingDescriptorExpr.accept(writerVisitor); + String expected = + createLines( + "private static final BatchingDescriptor" + + " PUBLISH_BATCHING_DESC = new BatchingDescriptor() {\n", + "@Override\n", + "public PartitionKey getBatchPartitionKey(PublishRequest request) {\n", + "return new PartitionKey(request.getTopic());\n", + "}\n", + "@Override\n", + "public RequestBuilder getRequestBuilder() {\n", + "return new RequestBuilder() {\n", + "private RequestBuilder builder;\n", + "@Override\n", + "public void appendRequest(PublishRequest request) {\n", + "if (Objects.isNull(builder)) {\n", + "builder = request.toBuilder();\n", + "} else {\n", + "builder.addAllMessages(request.getMessagesList());\n", + "}\n", + "}\n", + "@Override\n", + "public PublishRequest build() {\n", + "return builder.build();\n", + "}\n", + "};\n", + "}\n", + "}"); + assertEquals(expected, writerVisitor.write()); + } + + @Test + public void batchingDescriptor_noSubresponseField() { + FileDescriptor serviceFileDescriptor = LoggingProto.getDescriptor(); + ServiceDescriptor serviceDescriptor = serviceFileDescriptor.getServices().get(0); + assertEquals(serviceDescriptor.getName(), "LoggingServiceV2"); + + List protoFiles = + Arrays.asList( + serviceFileDescriptor, + LogEntryProto.getDescriptor(), + LoggingConfigProto.getDescriptor(), + LoggingMetricsProto.getDescriptor()); + + Map resourceNames = new HashMap<>(); + Map messageTypes = new HashMap<>(); + for (FileDescriptor fileDescriptor : protoFiles) { + resourceNames.putAll(Parser.parseResourceNames(fileDescriptor)); + messageTypes.putAll(Parser.parseMessages(fileDescriptor)); + } + + Set outputResourceNames = new HashSet<>(); + List services = + Parser.parseService( + serviceFileDescriptor, messageTypes, resourceNames, outputResourceNames); + + String filename = "logging_gapic.yaml"; + Path path = Paths.get(TESTFILES_DIRECTORY, filename); + Optional> batchingSettingsOpt = + BatchingSettingsConfigParser.parse(Optional.of(path.toString())); + assertTrue(batchingSettingsOpt.isPresent()); + + String jsonFilename = "logging_grpc_service_config.json"; + Path jsonPath = Paths.get(TESTFILES_DIRECTORY, jsonFilename); + Optional configOpt = + ServiceConfigParser.parse(jsonPath.toString(), batchingSettingsOpt); + assertTrue(configOpt.isPresent()); + GapicServiceConfig config = configOpt.get(); + + Service service = services.get(0); + assertEquals("LoggingServiceV2", service.name()); + Method method = findMethod(service, "WriteLogEntries"); + + GapicBatchingSettings batchingSetting = batchingSettingsOpt.get().get(0); + assertEquals("WriteLogEntries", batchingSetting.methodName()); + Expr batchingDescriptorExpr = + BatchingDescriptorComposer.createBatchingDescriptorFieldDeclExpr( + method, batchingSetting, messageTypes); + + batchingDescriptorExpr.accept(writerVisitor); + String expected = + createLines( + "private static final BatchingDescriptor WRITE_LOG_ENTRIES_BATCHING_DESC = new" + + " BatchingDescriptor() {\n", + "@Override\n", + "public PartitionKey getBatchPartitionKey(WriteLogEntriesRequest request) {\n", + "return new PartitionKey(request.getLogName(), request.getResource()," + + " request.getLabels());\n", + "}\n", + "@Override\n", + "public RequestBuilder getRequestBuilder() {\n", + "return new RequestBuilder() {\n", + "private RequestBuilder builder;\n", + "@Override\n", + "public void appendRequest(WriteLogEntriesRequest request) {\n", + "if (Objects.isNull(builder)) {\n", + "builder = request.toBuilder();\n", + "} else {\n", + "builder.addAllEntries(request.getEntriesList());\n", + "}\n", + "}\n", + "@Override\n", + "public WriteLogEntriesRequest build() {\n", + "return builder.build();\n", + "}\n", + "};\n", + "}\n", + "}"); + + assertEquals(expected, writerVisitor.write()); + } + + private static Method findMethod(Service service, String methodName) { + for (Method m : service.methods()) { + if (m.name().equals(methodName)) { + return m; + } + } + return null; + } + + private static String createLines(String... lines) { + // Cast to get rid of warnings. + return String.format(new String(new char[lines.length]).replace("\0", "%s"), (Object[]) lines); + } +} 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 4ef13a2350..72ff6e2e6e 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 @@ -684,6 +684,8 @@ private static List parseServices( + "import com.google.api.gax.batching.BatchingSettings;\n" + "import com.google.api.gax.batching.FlowControlSettings;\n" + "import com.google.api.gax.batching.FlowController;\n" + + "import com.google.api.gax.batching.PartitionKey;\n" + + "import com.google.api.gax.batching.RequestBuilder;\n" + "import com.google.api.gax.core.GaxProperties;\n" + "import com.google.api.gax.core.GoogleCredentialsProvider;\n" + "import com.google.api.gax.core.InstantiatingExecutorProvider;\n" @@ -960,6 +962,40 @@ private static List parseServices( + " futureResponse);\n" + " }\n" + " };\n" + + " private static final BatchingDescriptor\n" + + " WRITE_LOG_ENTRIES_BATCHING_DESC =\n" + + " new BatchingDescriptor()" + + " {\n" + + " @Override\n" + + " public PartitionKey getBatchPartitionKey(WriteLogEntriesRequest request)" + + " {\n" + + " return new PartitionKey(\n" + + " request.getLogName(), request.getResource()," + + " request.getLabels());\n" + + " }\n" + + "\n" + + " @Override\n" + + " public RequestBuilder getRequestBuilder() {\n" + + " return new RequestBuilder() {\n" + + " private RequestBuilder builder;\n" + + "\n" + + " @Override\n" + + " public void appendRequest(WriteLogEntriesRequest request) {\n" + + " if (Objects.isNull(builder)) {\n" + + " builder = request.toBuilder();\n" + + " } else {\n" + + " builder.addAllEntries(request.getEntriesList());\n" + + " }\n" + + " }\n" + + "\n" + + " @Override\n" + + " public WriteLogEntriesRequest build() {\n" + + " return builder.build();\n" + + " }\n" + + " };\n" + + " }\n" + + " };\n" + "\n" + " public UnaryCallSettings deleteLogSettings() {\n" + " return deleteLogSettings;\n" @@ -1294,6 +1330,8 @@ private static List parseServices( + "import com.google.api.gax.batching.BatchingSettings;\n" + "import com.google.api.gax.batching.FlowControlSettings;\n" + "import com.google.api.gax.batching.FlowController;\n" + + "import com.google.api.gax.batching.PartitionKey;\n" + + "import com.google.api.gax.batching.RequestBuilder;\n" + "import com.google.api.gax.core.GaxProperties;\n" + "import com.google.api.gax.core.GoogleCredentialsProvider;\n" + "import com.google.api.gax.core.InstantiatingExecutorProvider;\n" @@ -1569,6 +1607,35 @@ private static List parseServices( + " futureResponse);\n" + " }\n" + " };\n" + + " private static final BatchingDescriptor" + + " PUBLISH_BATCHING_DESC =\n" + + " new BatchingDescriptor() {\n" + + " @Override\n" + + " public PartitionKey getBatchPartitionKey(PublishRequest request) {\n" + + " return new PartitionKey(request.getTopic());\n" + + " }\n" + + "\n" + + " @Override\n" + + " public RequestBuilder getRequestBuilder() {\n" + + " return new RequestBuilder() {\n" + + " private RequestBuilder builder;\n" + + "\n" + + " @Override\n" + + " public void appendRequest(PublishRequest request) {\n" + + " if (Objects.isNull(builder)) {\n" + + " builder = request.toBuilder();\n" + + " } else {\n" + + " builder.addAllMessages(request.getMessagesList());\n" + + " }\n" + + " }\n" + + "\n" + + " @Override\n" + + " public PublishRequest build() {\n" + + " return builder.build();\n" + + " }\n" + + " };\n" + + " }\n" + + " };\n" + "\n" + " public UnaryCallSettings createTopicSettings() {\n" + " return createTopicSettings;\n" From 902fd41d01ded42702cee04c82ba8fff51e341d5 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 1 Sep 2020 15:55:10 -0700 Subject: [PATCH 4/8] feat: support '? extends Foo' wildcard bounded references --- .../engine/ast/ConcreteReference.java | 43 ++++++++++++++++-- .../api/generator/engine/ast/Reference.java | 7 ++- .../api/generator/engine/ast/TypeNode.java | 20 +++++++-- .../generator/engine/ast/VaporReference.java | 11 +++++ .../engine/writer/ImportWriterVisitor.java | 13 ++++-- .../engine/ast/ConcreteReferenceTest.java | 8 ++++ .../generator/engine/ast/TypeNodeTest.java | 29 ++++++++++++ .../writer/ImportWriterVisitorTest.java | 44 +++++++++++++++++++ .../engine/writer/JavaWriterVisitorTest.java | 37 ++++++++++++++++ 9 files changed, 202 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/google/api/generator/engine/ast/ConcreteReference.java b/src/main/java/com/google/api/generator/engine/ast/ConcreteReference.java index 144820c7ab..c95823fc13 100644 --- a/src/main/java/com/google/api/generator/engine/ast/ConcreteReference.java +++ b/src/main/java/com/google/api/generator/engine/ast/ConcreteReference.java @@ -17,18 +17,29 @@ import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; import java.util.List; +import java.util.Objects; +import javax.annotation.Nullable; @AutoValue public abstract class ConcreteReference implements Reference { + private static final String EXTENDS = "extends"; + private static final String COMMA = ", "; private static final String DOT = "."; + private static final String SPACE = " "; private static final String LEFT_ANGLE = "<"; private static final String RIGHT_ANGLE = ">"; private static final String QUESTION_MARK = "?"; + private static final Class WILDCARD_CLAZZ = ReferenceWildcard.class; + // Private. abstract Class clazz(); + @Nullable + @Override + public abstract Reference wildcardUpperBound(); + @Override public abstract ImmutableList generics(); @@ -38,8 +49,15 @@ public abstract class ConcreteReference implements Reference { @Override public String name() { StringBuilder sb = new StringBuilder(); - if (this.equals(TypeNode.WILDCARD_REFERENCE)) { + if (isWildcard()) { sb.append(QUESTION_MARK); + if (wildcardUpperBound() != null) { + // Handle the upper bound. + sb.append(SPACE); + sb.append(EXTENDS); + sb.append(SPACE); + sb.append(wildcardUpperBound().name()); + } } else { if (hasEnclosingClass() && !isStaticImport()) { sb.append(clazz().getEnclosingClass().getSimpleName()); @@ -117,6 +135,11 @@ public boolean isAssignableFrom(Reference other) { return clazz().isAssignableFrom(((ConcreteReference) other).clazz()); } + @Override + public boolean isWildcard() { + return clazz().equals(WILDCARD_CLAZZ); + } + @Override public boolean equals(Object o) { if (!(o instanceof ConcreteReference)) { @@ -124,12 +147,16 @@ public boolean equals(Object o) { } ConcreteReference ref = (ConcreteReference) o; - return clazz().equals(ref.clazz()) && generics().equals(ref.generics()); + return clazz().equals(ref.clazz()) + && generics().equals(ref.generics()) + && Objects.equals(wildcardUpperBound(), ref.wildcardUpperBound()); } @Override public int hashCode() { - return 17 * clazz().hashCode() + 31 * generics().hashCode(); + int wildcardUpperBoundHash = + wildcardUpperBound() == null ? 0 : 11 * wildcardUpperBound().hashCode(); + return 17 * clazz().hashCode() + 31 * generics().hashCode() + wildcardUpperBoundHash; } @Override @@ -141,6 +168,14 @@ public static ConcreteReference withClazz(Class clazz) { return builder().setClazz(clazz).build(); } + public static ConcreteReference wildcard() { + return withClazz(ReferenceWildcard.class); + } + + public static ConcreteReference wildcardWithUpperBound(Reference upperBoundReference) { + return builder().setClazz(WILDCARD_CLAZZ).setWildcardUpperBound(upperBoundReference).build(); + } + public static Builder builder() { return new AutoValue_ConcreteReference.Builder() .setGenerics(ImmutableList.of()) @@ -154,6 +189,8 @@ public static Builder builder() { public abstract static class Builder { public abstract Builder setClazz(Class clazz); + public abstract Builder setWildcardUpperBound(Reference reference); + public abstract Builder setGenerics(List clazzes); public abstract Builder setIsStaticImport(boolean isStaticImport); diff --git a/src/main/java/com/google/api/generator/engine/ast/Reference.java b/src/main/java/com/google/api/generator/engine/ast/Reference.java index f0e92ef3eb..6fd856c78f 100644 --- a/src/main/java/com/google/api/generator/engine/ast/Reference.java +++ b/src/main/java/com/google/api/generator/engine/ast/Reference.java @@ -30,6 +30,11 @@ public interface Reference { @Nullable String enclosingClassName(); + @Nullable + Reference wildcardUpperBound(); + + Reference copyAndSetGenerics(List generics); + // Valid only for nested classes. boolean isStaticImport(); @@ -42,5 +47,5 @@ public interface Reference { boolean isAssignableFrom(Reference other); - Reference copyAndSetGenerics(List generics); + boolean isWildcard(); } diff --git a/src/main/java/com/google/api/generator/engine/ast/TypeNode.java b/src/main/java/com/google/api/generator/engine/ast/TypeNode.java index d1bb85c552..65e5f16130 100644 --- a/src/main/java/com/google/api/generator/engine/ast/TypeNode.java +++ b/src/main/java/com/google/api/generator/engine/ast/TypeNode.java @@ -25,8 +25,7 @@ @AutoValue public abstract class TypeNode implements AstNode { static final Reference EXCEPTION_REFERENCE = ConcreteReference.withClazz(Exception.class); - public static final Reference WILDCARD_REFERENCE = - ConcreteReference.withClazz(ReferenceWildcard.class); + public static final Reference WILDCARD_REFERENCE = ConcreteReference.wildcard(); public enum TypeKind { BYTE, @@ -101,7 +100,22 @@ public abstract static class Builder { public abstract Builder setReference(Reference reference); - public abstract TypeNode build(); + // Private. + abstract Reference reference(); + + abstract TypeNode autoBuild(); + + public TypeNode build() { + if (reference() != null) { + // Disallow top-level wildcard references. + Preconditions.checkState( + !reference().isWildcard(), + String.format( + "The top-level referenece in a type cannot be a wildcard, found %s", + reference().name())); + } + return autoBuild(); + } } // TODO(miraleung): More type creation helpers to come... diff --git a/src/main/java/com/google/api/generator/engine/ast/VaporReference.java b/src/main/java/com/google/api/generator/engine/ast/VaporReference.java index 736c41daff..4ba17aa356 100644 --- a/src/main/java/com/google/api/generator/engine/ast/VaporReference.java +++ b/src/main/java/com/google/api/generator/engine/ast/VaporReference.java @@ -41,6 +41,12 @@ public abstract class VaporReference implements Reference { @Override public abstract String enclosingClassName(); + @Nullable + @Override + public Reference wildcardUpperBound() { + return null; + } + @Override public String fullName() { // TODO(unsupported): Nested classes with depth greater than 1. @@ -75,6 +81,11 @@ public boolean isAssignableFrom(Reference other) { return false; } + @Override + public boolean isWildcard() { + return false; + } + abstract String plainName(); @Override diff --git a/src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java b/src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java index f8b16a57b3..dfdadbb307 100644 --- a/src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java +++ b/src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java @@ -50,6 +50,7 @@ import com.google.api.generator.engine.ast.WhileStatement; import com.google.common.base.Preconditions; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; @@ -340,9 +341,15 @@ private void variableExpressions(List expressions) { private void references(List refs) { for (Reference ref : refs) { // Don't need to import this. - if ((!ref.isStaticImport() - && (ref.isFromPackage(PKG_JAVA_LANG) || ref.isFromPackage(currentPackage))) - || ref.equals(TypeNode.WILDCARD_REFERENCE)) { + if (!ref.isStaticImport() + && (ref.isFromPackage(PKG_JAVA_LANG) || ref.isFromPackage(currentPackage))) { + continue; + } + + if (ref.isWildcard()) { + if (ref.wildcardUpperBound() != null) { + references(Arrays.asList(ref.wildcardUpperBound())); + } continue; } diff --git a/src/test/java/com/google/api/generator/engine/ast/ConcreteReferenceTest.java b/src/test/java/com/google/api/generator/engine/ast/ConcreteReferenceTest.java index 717f229822..b284b1bb3a 100644 --- a/src/test/java/com/google/api/generator/engine/ast/ConcreteReferenceTest.java +++ b/src/test/java/com/google/api/generator/engine/ast/ConcreteReferenceTest.java @@ -140,4 +140,12 @@ public void isSupertype_nestedGenerics() { assertTrue(typeOne.isSupertypeOrEquals(typeTwo)); assertFalse(typeTwo.isSupertypeOrEquals(typeOne)); } + + @Test + public void wildcards() { + assertEquals("?", ConcreteReference.wildcard().name()); + assertEquals( + "? extends String", + ConcreteReference.wildcardWithUpperBound(TypeNode.STRING.reference()).name()); + } } diff --git a/src/test/java/com/google/api/generator/engine/ast/TypeNodeTest.java b/src/test/java/com/google/api/generator/engine/ast/TypeNodeTest.java index 3cd985a119..3a446f15d9 100644 --- a/src/test/java/com/google/api/generator/engine/ast/TypeNodeTest.java +++ b/src/test/java/com/google/api/generator/engine/ast/TypeNodeTest.java @@ -16,6 +16,7 @@ import static junit.framework.Assert.assertFalse; import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertThrows; import com.google.api.generator.engine.ast.TypeNode.TypeKind; import java.util.Arrays; @@ -88,4 +89,32 @@ public void equals_basic() { assertFalse(TypeNode.CHAR.equals(TypeNode.NULL)); assertFalse(INTEGER_ARRAY.equals(INT_ARRAY)); } + + @Test + public void type_wildcardGenerics() { + // No exception thrown equates to success. + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics(Arrays.asList(ConcreteReference.wildcard())) + .build()); + } + + @Test + public void type_wildcardUpperBoundGenerics() { + // No exception thrown equates to success. + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics( + Arrays.asList( + ConcreteReference.wildcardWithUpperBound(TypeNode.STRING.reference()))) + .build()); + } + + @Test + public void invalidType_topLevelWildcard() { + assertThrows( + IllegalStateException.class, () -> TypeNode.withReference(ConcreteReference.wildcard())); + } } diff --git a/src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java index 815588b8b4..c7e023bc26 100644 --- a/src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/ImportWriterVisitorTest.java @@ -473,6 +473,50 @@ public void writeVariableExprImports_staticReference() { "import com.google.api.generator.engine.ast.TypeNode;\n\n")); } + @Test + public void writeVariableExprImports_wildcardType() { + TypeNode wildcardListType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics(Arrays.asList(TypeNode.WILDCARD_REFERENCE)) + .build()); + + // Constructs `List x`. + Variable variable = Variable.builder().setName("x").setType(wildcardListType).build(); + VariableExpr variableExpr = + VariableExpr.builder().setIsDecl(true).setVariable(variable).build(); + + variableExpr.accept(writerVisitor); + assertEquals(writerVisitor.write(), "import java.util.List;\n\n"); + } + + @Test + public void writeVariableExprImport_wildcardTypeWithUpperBound() { + TypeNode wildcardListType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics( + Arrays.asList( + ConcreteReference.wildcardWithUpperBound( + ConcreteReference.withClazz(Expr.class)))) + .build()); + + // Constructs `List x`. + Variable variable = Variable.builder().setName("x").setType(wildcardListType).build(); + VariableExpr variableExpr = + VariableExpr.builder().setIsDecl(true).setVariable(variable).build(); + + variableExpr.accept(writerVisitor); + assertEquals( + writerVisitor.write(), + String.format( + createLines(2), + "import com.google.api.generator.engine.ast.Expr;\n", + "import java.util.List;\n\n")); + } + @Test public void writeVariableExprImports_reference() { Variable variable = diff --git a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java index 9b38d81392..a3e2705bea 100644 --- a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java @@ -204,6 +204,43 @@ public void writeVariableExpr_basic() { assertEquals(writerVisitor.write(), "x"); } + @Test + public void writeVariableExpr_wildcardType() { + TypeNode wildcardListType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics(Arrays.asList(TypeNode.WILDCARD_REFERENCE)) + .build()); + + Variable variable = Variable.builder().setName("x").setType(wildcardListType).build(); + VariableExpr variableExpr = + VariableExpr.builder().setIsDecl(true).setVariable(variable).build(); + + variableExpr.accept(writerVisitor); + assertEquals(writerVisitor.write(), "List x"); + } + + @Test + public void writeVariableExpr_wildcardTypeWithUpperBound() { + TypeNode wildcardListType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(List.class) + .setGenerics( + Arrays.asList( + ConcreteReference.wildcardWithUpperBound( + ConcreteReference.withClazz(Expr.class)))) + .build()); + + Variable variable = Variable.builder().setName("x").setType(wildcardListType).build(); + VariableExpr variableExpr = + VariableExpr.builder().setIsDecl(true).setVariable(variable).build(); + + variableExpr.accept(writerVisitor); + assertEquals(writerVisitor.write(), "List x"); + } + @Test public void writeVariableExpr_staticReference() { VariableExpr variableExpr = From 8efb572b7dd3ef40f1b1e57b9198699e734643a8 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 1 Sep 2020 15:56:22 -0700 Subject: [PATCH 5/8] fix: speed up precommit checks with --disk_cache --- .githooks/pre-commit | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/.githooks/pre-commit b/.githooks/pre-commit index 06a7a3bf7b..d8091b4316 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -40,10 +40,10 @@ function echo_success { function header_check_preparation { echo_status "Setting up license check environment" export GOPATH=$(go env GOPATH) - if [ $? -ne 0 ]; - then - echo_status "Please install Go first, instructions can be found here: https://golang.org/doc/install." - else + if [ $? -ne 0 ]; + then + echo_status "Please install Go first, instructions can be found here: https://golang.org/doc/install." + else export ENV_PATH=$(echo $PATH) if [[ $ENV_PATH != *$GOPATH* ]]; then @@ -51,7 +51,7 @@ function header_check_preparation { export PATH=$GOPATH/bin:$PATH fi which addlicense - if [ $? -ne 0 ]; + if [ $? -ne 0 ]; then echo_status "addlicense tool is not yet installed, downloading it now." go get -u github.com/google/addlicense @@ -59,6 +59,13 @@ function header_check_preparation { fi } +# Disk cache. +BAZEL_CACHE_DIR=/tmp/bazel_cache_gapic_generator_java +if [ ! -d $BAZEL_CACHE_DIR ] +then + mkdir $BAZEL_CACHE_DIR +fi + # Constants. # Check only the staged files. NUM_TOTAL_FILES_CHANGED=$(git diff --cached --name-only | wc -l) @@ -80,7 +87,7 @@ fi if [ $NUM_JAVA_FILES_CHANGED -gt 0 ] then echo_status "Running Java linter..." - bazel build //:google_java_format_verification + bazel build --disk_cache="$BAZEL_CACHE_DIR" //:google_java_format_verification FORMAT_STATUS=$? if [ $FORMAT_STATUS != 0 ] then @@ -93,7 +100,7 @@ fi if [ $NUM_JAVA_FILES_CHANGED -gt 0 ] || [ $NUM_BAZEL_FILES_CHANGED -gt 0 ] then echo_status "Checking the build..." - bazel build //... + bazel build --disk_cache="$BAZEL_CACHE_DIR" //... BUILD_STATUS=$? if [ $BUILD_STATUS != 0 ] then @@ -105,7 +112,7 @@ fi if [ $NUM_JAVA_FILES_CHANGED -gt 0 ] then echo_status "Checking tests..." - bazel test //... + bazel test --disk_cache="$BAZEL_CACHE_DIR" //... TEST_STATUS=$? if [ $TEST_STATUS != 0 ] then From 66a7e1c5643ba359158ed205e8849ca3e1271808 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 1 Sep 2020 15:57:08 -0700 Subject: [PATCH 6/8] feat: add splitException, count{Elements,Bytes} batching descriptor methods to ServiceStubSettings --- .../composer/BatchingDescriptorComposer.java | 100 ++++++++++++++++++ .../BatchingDescriptorComposerTest.java | 32 ++++++ .../ServiceStubSettingsClassComposerTest.java | 44 ++++++++ 3 files changed, 176 insertions(+) diff --git a/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java b/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java index 3677a02b79..bcdb809df3 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java @@ -23,6 +23,7 @@ import com.google.api.generator.engine.ast.ConcreteReference; import com.google.api.generator.engine.ast.Expr; import com.google.api.generator.engine.ast.ExprStatement; +import com.google.api.generator.engine.ast.ForStatement; import com.google.api.generator.engine.ast.IfStatement; import com.google.api.generator.engine.ast.MethodDefinition; import com.google.api.generator.engine.ast.MethodInvocationExpr; @@ -39,6 +40,7 @@ import com.google.common.base.Preconditions; import java.util.ArrayList; import java.util.Arrays; +import java.util.Collection; import java.util.List; import java.util.Map; import java.util.Objects; @@ -58,6 +60,7 @@ public class BatchingDescriptorComposer { private static final String ADD_ALL_METHOD_PATTERN = "addAll%s"; private static final String GET_LIST_METHOD_PATTERN = "get%sList"; + private static final String GET_COUNT_METHOD_PATTERN = "get%sCount"; public static Expr createBatchingDescriptorFieldDeclExpr( Method method, GapicBatchingSettings batchingSettings, Map messageTypes) { @@ -65,6 +68,10 @@ public static Expr createBatchingDescriptorFieldDeclExpr( javaMethods.add(createGetBatchPartitionKeyMethod(method, batchingSettings, messageTypes)); javaMethods.add(createGetRequestBuilderMethod(method, batchingSettings)); + javaMethods.add(createSplitExceptionMethod(method)); + javaMethods.add(createCountElementsMethod(method, batchingSettings)); + javaMethods.add(createCountByteSMethod(method)); + TypeNode batchingDescriptorType = toType(BATCHING_DESCRIPTOR_REF, method.inputType(), method.outputType()); AnonymousClassExpr batchingDescriptorClassExpr = @@ -229,6 +236,99 @@ private static MethodDefinition createGetRequestBuilderMethod( .build(); } + private static MethodDefinition createSplitExceptionMethod(Method method) { + VariableExpr throwableVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(toType(Throwable.class)).setName("throwable").build()); + + TypeNode batchedRequestIssuerType = toType(BATCHED_REQUEST_ISSUER_REF, method.outputType()); + TypeNode batchVarType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(Collection.class) + .setGenerics( + Arrays.asList( + ConcreteReference.wildcardWithUpperBound( + batchedRequestIssuerType.reference()))) + .build()); + VariableExpr batchVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(batchVarType).setName("batch").build()); + VariableExpr responderVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(batchedRequestIssuerType).setName("responder").build()); + + ForStatement forStatement = + ForStatement.builder() + .setLocalVariableExpr(responderVarExpr.toBuilder().setIsDecl(true).build()) + .setCollectionExpr(batchVarExpr) + .setBody( + Arrays.asList( + ExprStatement.withExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(responderVarExpr) + .setMethodName("setException") + .setArguments(throwableVarExpr) + .build()))) + .build(); + + return MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.VOID) + .setName("splitException") + .setArguments( + Arrays.asList(throwableVarExpr, batchVarExpr).stream() + .map(v -> v.toBuilder().setIsDecl(true).build()) + .collect(Collectors.toList())) + .setBody(Arrays.asList(forStatement)) + .build(); + } + + private static MethodDefinition createCountElementsMethod( + Method method, GapicBatchingSettings batchingSettings) { + String getFooCountMethodName = + String.format( + GET_COUNT_METHOD_PATTERN, + JavaStyle.toUpperCamelCase(batchingSettings.batchedFieldName())); + VariableExpr requestVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(method.inputType()).setName("request").build()); + + return MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.LONG) + .setName("countElements") + .setArguments(requestVarExpr.toBuilder().setIsDecl(true).build()) + .setReturnExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(requestVarExpr) + .setMethodName(getFooCountMethodName) + .setReturnType(TypeNode.LONG) + .build()) + .build(); + } + + private static MethodDefinition createCountByteSMethod(Method method) { + VariableExpr requestVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(method.inputType()).setName("request").build()); + return MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.LONG) + .setName("countBytes") + .setArguments(requestVarExpr.toBuilder().setIsDecl(true).build()) + .setReturnExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(requestVarExpr) + .setMethodName("getSerializedSize") + .setReturnType(TypeNode.LONG) + .build()) + .build(); + } + private static TypeNode toType(Class clazz) { return TypeNode.withReference(ConcreteReference.withClazz(clazz)); } diff --git a/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java index 3b5dd833ac..e412e0a832 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java @@ -128,6 +128,22 @@ public void batchingDescriptor_hasSubresponseField() { "}\n", "};\n", "}\n", + "@Override\n", + "public void splitException(", + "Throwable throwable, ", + "Collection> batch) {\n", + "for (BatchedRequestIssuer responder : batch) {\n", + "responder.setException(throwable);\n", + "}\n", + "}\n", + "@Override\n", + "public long countElements(PublishRequest request) {\n", + "return request.getMessagesCount();\n", + "}\n", + "@Override\n", + "public long countBytes(PublishRequest request) {\n", + "return request.getSerializedSize();\n", + "}\n", "}"); assertEquals(expected, writerVisitor.write()); } @@ -209,6 +225,22 @@ public void batchingDescriptor_noSubresponseField() { "}\n", "};\n", "}\n", + "@Override\n", + "public void splitException(", + "Throwable throwable, ", + "Collection> batch) {\n", + "for (BatchedRequestIssuer responder : batch) {\n", + "responder.setException(throwable);\n", + "}\n", + "}\n", + "@Override\n", + "public long countElements(WriteLogEntriesRequest request) {\n", + "return request.getEntriesCount();\n", + "}\n", + "@Override\n", + "public long countBytes(WriteLogEntriesRequest request) {\n", + "return request.getSerializedSize();\n", + "}\n", "}"); assertEquals(expected, writerVisitor.write()); 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 72ff6e2e6e..10456faed3 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 @@ -695,6 +695,7 @@ private static List parseServices( + "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.BatchedRequestIssuer;\n" + "import com.google.api.gax.rpc.BatchingCallSettings;\n" + "import com.google.api.gax.rpc.BatchingDescriptor;\n" + "import com.google.api.gax.rpc.ClientContext;\n" @@ -723,6 +724,7 @@ private static List parseServices( + "import com.google.logging.v2.WriteLogEntriesResponse;\n" + "import com.google.protobuf.Empty;\n" + "import java.io.IOException;\n" + + "import java.util.Collection;\n" + "import java.util.List;\n" + "import java.util.Objects;\n" + "import javax.annotation.Generated;\n" @@ -995,6 +997,27 @@ private static List parseServices( + " }\n" + " };\n" + " }\n" + + "\n" + + " @Override\n" + + " public void splitException(\n" + + " Throwable throwable,\n" + + " Collection>" + + " batch) {\n" + + " for (BatchedRequestIssuer responder : batch)" + + " {\n" + + " responder.setException(throwable);\n" + + " }\n" + + " }\n" + + "\n" + + " @Override\n" + + " public long countElements(WriteLogEntriesRequest request) {\n" + + " return request.getEntriesCount();\n" + + " }\n" + + "\n" + + " @Override\n" + + " public long countBytes(WriteLogEntriesRequest request) {\n" + + " return request.getSerializedSize();\n" + + " }\n" + " };\n" + "\n" + " public UnaryCallSettings deleteLogSettings() {\n" @@ -1341,6 +1364,7 @@ private static List parseServices( + "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.BatchedRequestIssuer;\n" + "import com.google.api.gax.rpc.BatchingCallSettings;\n" + "import com.google.api.gax.rpc.BatchingDescriptor;\n" + "import com.google.api.gax.rpc.ClientContext;\n" @@ -1373,6 +1397,7 @@ private static List parseServices( + "import com.google.pubsub.v1.Topic;\n" + "import com.google.pubsub.v1.UpdateTopicRequest;\n" + "import java.io.IOException;\n" + + "import java.util.Collection;\n" + "import java.util.List;\n" + "import java.util.Objects;\n" + "import javax.annotation.Generated;\n" @@ -1635,6 +1660,25 @@ private static List parseServices( + " }\n" + " };\n" + " }\n" + + "\n" + + " @Override\n" + + " public void splitException(\n" + + " Throwable throwable,\n" + + " Collection> batch) {\n" + + " for (BatchedRequestIssuer responder : batch) {\n" + + " responder.setException(throwable);\n" + + " }\n" + + " }\n" + + "\n" + + " @Override\n" + + " public long countElements(PublishRequest request) {\n" + + " return request.getMessagesCount();\n" + + " }\n" + + "\n" + + " @Override\n" + + " public long countBytes(PublishRequest request) {\n" + + " return request.getSerializedSize();\n" + + " }\n" + " };\n" + "\n" + " public UnaryCallSettings createTopicSettings() {\n" From 1a5c8f81a8b0df01ed9d199106a92e2ac6f05a3d Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 1 Sep 2020 16:59:41 -0700 Subject: [PATCH 7/8] feat: add GeneralForStatement --- .../generator/engine/ast/AstNodeVisitor.java | 2 + .../engine/ast/GeneralForStatement.java | 103 ++++++++++++++++++ .../engine/writer/ImportWriterVisitor.java | 9 ++ .../engine/writer/JavaWriterVisitor.java | 31 ++++++ .../api/generator/engine/ast/BUILD.bazel | 1 + .../engine/ast/GeneralForStatementTest.java | 88 +++++++++++++++ .../engine/writer/JavaWriterVisitorTest.java | 21 ++++ 7 files changed, 255 insertions(+) create mode 100644 src/main/java/com/google/api/generator/engine/ast/GeneralForStatement.java create mode 100644 src/test/java/com/google/api/generator/engine/ast/GeneralForStatementTest.java diff --git a/src/main/java/com/google/api/generator/engine/ast/AstNodeVisitor.java b/src/main/java/com/google/api/generator/engine/ast/AstNodeVisitor.java index 0be36c21a7..52276d949a 100644 --- a/src/main/java/com/google/api/generator/engine/ast/AstNodeVisitor.java +++ b/src/main/java/com/google/api/generator/engine/ast/AstNodeVisitor.java @@ -67,6 +67,8 @@ public interface AstNodeVisitor { public void visit(ForStatement forStatement); + public void visit(GeneralForStatement generalForStatement); + public void visit(WhileStatement whileStatement); public void visit(TryCatchStatement tryCatchStatement); diff --git a/src/main/java/com/google/api/generator/engine/ast/GeneralForStatement.java b/src/main/java/com/google/api/generator/engine/ast/GeneralForStatement.java new file mode 100644 index 0000000000..fba06d97d1 --- /dev/null +++ b/src/main/java/com/google/api/generator/engine/ast/GeneralForStatement.java @@ -0,0 +1,103 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.api.generator.engine.ast; + +import com.google.auto.value.AutoValue; +import com.google.common.base.Preconditions; +import com.google.common.collect.ImmutableList; +import java.util.Collections; +import java.util.List; + +@AutoValue +public abstract class GeneralForStatement implements Statement { + public abstract Expr initializationExpr(); + + // TODO(summerji): Integrate OperationExpr here. Start by uncommenting the following section to + // replace the localVariableExpr and maxSizeExpr getters after it. + /* + // Uses the same terminology as https://docs.oracle.com/javase/tutorial/java/nutsandbolts/for.html. + public abstract Expr terminationExpr(); + public abstract Expr incrementExpr(); + */ + + public abstract VariableExpr localVariableExpr(); + + public abstract Expr maxSizeExpr(); + + public abstract ImmutableList body(); + + @Override + public void accept(AstNodeVisitor visitor) { + visitor.visit(this); + } + + // Convenience wrapper. + public static GeneralForStatement incrementWith( + VariableExpr variableExpr, Expr maxSizeExpr, List body) { + // TODO(summerji): Do some integration here, in JavaWriterVisitor, in ImportWriterVisitor, and + // add more tests. + return builder() + .setLocalVariableExpr(variableExpr.toBuilder().setIsDecl(false).build()) + .setMaxSizeExpr(maxSizeExpr) + .setBody(body) + .build(); + } + + public static Builder builder() { + return new AutoValue_GeneralForStatement.Builder().setBody(Collections.emptyList()); + } + + @AutoValue.Builder + public abstract static class Builder { + public abstract Builder setInitializationExpr(Expr initializationExpr); + + public abstract Builder setBody(List body); + + // Private. + abstract Builder setLocalVariableExpr(VariableExpr variableExpr); + + abstract Builder setMaxSizeExpr(Expr maxSizeExpr); + + abstract VariableExpr localVariableExpr(); + + abstract GeneralForStatement autoBuild(); + + // Type-checking will be done in the sub-expressions. + public GeneralForStatement build() { + VariableExpr varExpr = localVariableExpr(); + Preconditions.checkState( + varExpr.scope().equals(ScopeNode.LOCAL), + String.format( + "Variable %s in a general for-loop cannot have a non-local scope", + varExpr.variable().identifier().name())); + Preconditions.checkState( + !varExpr.isStatic() && !varExpr.isFinal(), + String.format( + "Variable %s in a general for-loop cannot be static or final", + varExpr.variable().identifier().name())); + setInitializationExpr( + AssignmentExpr.builder() + .setVariableExpr(varExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr( + ValueExpr.withValue( + PrimitiveValue.builder().setValue("0").setType(TypeNode.INT).build())) + .build()); + // TODO(summerji): Remove the following two lines. + // This temporary workaround will be removed soon, so it doesn't need a test. + setLocalVariableExpr(varExpr.toBuilder().setIsDecl(false).build()); + return autoBuild(); + } + } +} diff --git a/src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java b/src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java index dfdadbb307..47a61bc3af 100644 --- a/src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java +++ b/src/main/java/com/google/api/generator/engine/writer/ImportWriterVisitor.java @@ -27,6 +27,7 @@ import com.google.api.generator.engine.ast.Expr; import com.google.api.generator.engine.ast.ExprStatement; import com.google.api.generator.engine.ast.ForStatement; +import com.google.api.generator.engine.ast.GeneralForStatement; import com.google.api.generator.engine.ast.IdentifierNode; import com.google.api.generator.engine.ast.IfStatement; import com.google.api.generator.engine.ast.InstanceofExpr; @@ -241,6 +242,14 @@ public void visit(ForStatement forStatement) { statements(forStatement.body()); } + @Override + public void visit(GeneralForStatement generalForStatement) { + generalForStatement.initializationExpr().accept(this); + generalForStatement.localVariableExpr().accept(this); + generalForStatement.maxSizeExpr().accept(this); + statements(generalForStatement.body()); + } + @Override public void visit(WhileStatement whileStatement) { whileStatement.conditionExpr().accept(this); diff --git a/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java b/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java index b246e7234f..f9ee6141e6 100644 --- a/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java +++ b/src/main/java/com/google/api/generator/engine/writer/JavaWriterVisitor.java @@ -27,6 +27,7 @@ import com.google.api.generator.engine.ast.Expr; import com.google.api.generator.engine.ast.ExprStatement; import com.google.api.generator.engine.ast.ForStatement; +import com.google.api.generator.engine.ast.GeneralForStatement; import com.google.api.generator.engine.ast.IdentifierNode; import com.google.api.generator.engine.ast.IfStatement; import com.google.api.generator.engine.ast.InstanceofExpr; @@ -461,6 +462,36 @@ public void visit(ForStatement forStatement) { newline(); } + @Override + public void visit(GeneralForStatement generalForStatement) { + buffer.append(FOR); + space(); + leftParen(); + generalForStatement.initializationExpr().accept(this); + semicolon(); + space(); + + generalForStatement.localVariableExpr().accept(this); + space(); + buffer.append(LEFT_ANGLE); + space(); + generalForStatement.maxSizeExpr().accept(this); + semicolon(); + space(); + + generalForStatement.localVariableExpr().accept(this); + // TODO(summerji): Remove the following temporary workaround. + buffer.append("++"); + rightParen(); + space(); + leftBrace(); + newline(); + + statements(generalForStatement.body()); + rightBrace(); + newline(); + } + @Override public void visit(WhileStatement whileStatement) { buffer.append(WHILE); diff --git a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel index bfeff4498a..c2cf286570 100644 --- a/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel +++ b/src/test/java/com/google/api/generator/engine/ast/BUILD.bazel @@ -9,6 +9,7 @@ TESTS = [ "EnumRefExprTest", "ExprStatementTest", "ForStatementTest", + "GeneralForStatementTest", "IdentifierNodeTest", "IfStatementTest", "InstanceofExprTest", diff --git a/src/test/java/com/google/api/generator/engine/ast/GeneralForStatementTest.java b/src/test/java/com/google/api/generator/engine/ast/GeneralForStatementTest.java new file mode 100644 index 0000000000..4d544d91aa --- /dev/null +++ b/src/test/java/com/google/api/generator/engine/ast/GeneralForStatementTest.java @@ -0,0 +1,88 @@ +// Copyright 2020 Google LLC +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +package com.google.api.generator.engine.ast; + +import static org.junit.Assert.assertThrows; + +import java.util.Arrays; +import java.util.Collections; +import org.junit.Test; + +public class GeneralForStatementTest { + + @Test + public void validGeneralForStatement_basicIsDecl() { + Variable variable = Variable.builder().setName("i").setType(TypeNode.INT).build(); + VariableExpr variableExpr = + VariableExpr.builder().setVariable(variable).setIsDecl(true).build(); + + MethodInvocationExpr maxSizeExpr = + MethodInvocationExpr.builder().setMethodName("maxSize").build(); + + GeneralForStatement.incrementWith( + variableExpr, maxSizeExpr, Arrays.asList(createBodyStatement())); + } + + @Test + public void validGeneralForStatement_basicIsNotDecl() { + Variable variable = Variable.builder().setName("i").setType(TypeNode.INT).build(); + VariableExpr variableExpr = + VariableExpr.builder().setVariable(variable).setIsDecl(false).build(); + + MethodInvocationExpr maxSizeExpr = + MethodInvocationExpr.builder().setMethodName("maxSize").build(); + + GeneralForStatement.incrementWith( + variableExpr, maxSizeExpr, Arrays.asList(createBodyStatement())); + } + + @Test + public void validGeneralForStatement_emptyBody() { + Variable variable = Variable.builder().setName("i").setType(TypeNode.INT).build(); + VariableExpr variableExpr = + VariableExpr.builder().setVariable(variable).setIsDecl(false).build(); + + MethodInvocationExpr maxSizeExpr = + MethodInvocationExpr.builder().setMethodName("maxSize").build(); + + GeneralForStatement.incrementWith(variableExpr, maxSizeExpr, Collections.emptyList()); + } + + @Test + public void invalidForStatement() { + Variable variable = Variable.builder().setName("str").setType(TypeNode.STRING).build(); + VariableExpr variableExpr = + VariableExpr.builder().setVariable(variable).setScope(ScopeNode.PRIVATE).build(); + MethodInvocationExpr maxSizeExpr = + MethodInvocationExpr.builder().setMethodName("maxSize").build(); + + assertThrows( + IllegalStateException.class, + () -> + GeneralForStatement.incrementWith(variableExpr, maxSizeExpr, Collections.emptyList())); + } + + private static Statement createBodyStatement() { + Variable variable = Variable.builder().setName("x").setType(TypeNode.INT).build(); + VariableExpr variableExpr = + VariableExpr.builder().setVariable(variable).setIsDecl(true).build(); + + Variable anotherVariable = Variable.builder().setName("y").setType(TypeNode.INT).build(); + Expr valueExpr = VariableExpr.builder().setVariable(anotherVariable).build(); + + return ExprStatement.withExpr( + AssignmentExpr.builder().setVariableExpr(variableExpr).setValueExpr(valueExpr).build()); + } +} diff --git a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java index a3e2705bea..ec6ac557f5 100644 --- a/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java +++ b/src/test/java/com/google/api/generator/engine/writer/JavaWriterVisitorTest.java @@ -30,6 +30,7 @@ import com.google.api.generator.engine.ast.Expr; import com.google.api.generator.engine.ast.ExprStatement; import com.google.api.generator.engine.ast.ForStatement; +import com.google.api.generator.engine.ast.GeneralForStatement; import com.google.api.generator.engine.ast.IdentifierNode; import com.google.api.generator.engine.ast.IfStatement; import com.google.api.generator.engine.ast.InstanceofExpr; @@ -1298,6 +1299,26 @@ public void writeForStatement() { "for (String str : getSomeStrings()) {\n", "int x = 3;\n", "int x = 3;\n", "}\n")); } + @Test + public void writeGeneralForStatement_basic() { + AssignmentExpr assignExpr = createAssignmentExpr("x", "3", TypeNode.INT); + Statement assignExprStatement = ExprStatement.withExpr(assignExpr); + List body = Arrays.asList(assignExprStatement, assignExprStatement); + + VariableExpr localVarExpr = createVariableDeclExpr("i", TypeNode.INT); + Expr maxSizeExpr = MethodInvocationExpr.builder().setMethodName("maxSize").build(); + + GeneralForStatement forStatement = + GeneralForStatement.incrementWith(localVarExpr, maxSizeExpr, body); + + forStatement.accept(writerVisitor); + assertEquals( + writerVisitor.write(), + String.format( + "%s%s%s%s", + "for (int i = 0; i < maxSize(); i++) {\n", "int x = 3;\n", "int x = 3;\n", "}\n")); + } + @Test public void writeTryCatchStatement_simple() { Reference exceptionReference = ConcreteReference.withClazz(IllegalArgumentException.class); From 23b443e2521228ba237f2b583d76e88ba81586c9 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Tue, 1 Sep 2020 18:00:11 -0700 Subject: [PATCH 8/8] feat: add splitRseponse to batching desc. in ServiceStubSettings --- .../composer/BatchingDescriptorComposer.java | 202 +++++++++++++++++- .../BatchingDescriptorComposerTest.java | 24 +++ .../ServiceStubSettingsClassComposerTest.java | 33 +++ 3 files changed, 258 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java b/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java index bcdb809df3..f5b601a947 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposer.java @@ -24,15 +24,20 @@ import com.google.api.generator.engine.ast.Expr; import com.google.api.generator.engine.ast.ExprStatement; import com.google.api.generator.engine.ast.ForStatement; +import com.google.api.generator.engine.ast.GeneralForStatement; import com.google.api.generator.engine.ast.IfStatement; 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.PrimitiveValue; import com.google.api.generator.engine.ast.Reference; import com.google.api.generator.engine.ast.ScopeNode; +import com.google.api.generator.engine.ast.Statement; import com.google.api.generator.engine.ast.TypeNode; +import com.google.api.generator.engine.ast.ValueExpr; 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.GapicBatchingSettings; import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.Method; @@ -59,6 +64,7 @@ public class BatchingDescriptorComposer { private static final TypeNode PARTITION_KEY_TYPE = toType(PartitionKey.class); private static final String ADD_ALL_METHOD_PATTERN = "addAll%s"; + private static final String BATCH_FOO_INDEX_PATTERN = "batch%sIndex"; private static final String GET_LIST_METHOD_PATTERN = "get%sList"; private static final String GET_COUNT_METHOD_PATTERN = "get%sCount"; @@ -67,7 +73,7 @@ public static Expr createBatchingDescriptorFieldDeclExpr( List javaMethods = new ArrayList<>(); javaMethods.add(createGetBatchPartitionKeyMethod(method, batchingSettings, messageTypes)); javaMethods.add(createGetRequestBuilderMethod(method, batchingSettings)); - + javaMethods.add(createSplitResponseMethod(method, batchingSettings, messageTypes)); javaMethods.add(createSplitExceptionMethod(method)); javaMethods.add(createCountElementsMethod(method, batchingSettings)); javaMethods.add(createCountByteSMethod(method)); @@ -236,6 +242,200 @@ private static MethodDefinition createGetRequestBuilderMethod( .build(); } + private static MethodDefinition createSplitResponseMethod( + Method method, GapicBatchingSettings batchingSettings, Map messageTypes) { + VariableExpr batchResponseVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(method.outputType()).setName("batchResponse").build()); + + TypeNode batchedRequestIssuerType = toType(BATCHED_REQUEST_ISSUER_REF, method.outputType()); + TypeNode batchVarType = + TypeNode.withReference( + ConcreteReference.builder() + .setClazz(Collection.class) + .setGenerics( + Arrays.asList( + ConcreteReference.wildcardWithUpperBound( + batchedRequestIssuerType.reference()))) + .build()); + VariableExpr batchVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(batchVarType).setName("batch").build()); + + VariableExpr responderVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(batchedRequestIssuerType).setName("responder").build()); + + String upperCamelBatchedFieldName = + JavaStyle.toUpperCamelCase(batchingSettings.batchedFieldName()); + VariableExpr batchMessageIndexVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(TypeNode.INT).setName("batchMessageIndex").build()); + + VariableExpr subresponseElementsVarExpr = null; + boolean hasSubresponseField = batchingSettings.subresponseFieldName() != null; + + List outerForBody = new ArrayList<>(); + if (hasSubresponseField) { + Message outputMessage = messageTypes.get(method.outputType().reference().name()); + Preconditions.checkNotNull( + outputMessage, String.format("Output message not found for RPC %s", method.name())); + + Field subresponseElementField = + outputMessage.fieldMap().get(batchingSettings.subresponseFieldName()); + Preconditions.checkNotNull( + subresponseElementField, + String.format( + "Subresponse field %s not found in message %s", + batchingSettings.subresponseFieldName(), outputMessage.name())); + TypeNode subresponseElementType = subresponseElementField.type(); + subresponseElementsVarExpr = + VariableExpr.withVariable( + Variable.builder() + .setType(subresponseElementType) + .setName("subresponseElements") + .build()); + + VariableExpr subresponseCountVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(TypeNode.LONG).setName("subresponseCount").build()); + + outerForBody.add( + ExprStatement.withExpr( + AssignmentExpr.builder() + .setVariableExpr(subresponseElementsVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr( + NewObjectExpr.builder() + .setType( + TypeNode.withReference(ConcreteReference.withClazz(ArrayList.class))) + .setIsGeneric(true) + .build()) + .build())); + + String getFooCountMethodName = "getMessageCount"; + outerForBody.add( + ExprStatement.withExpr( + AssignmentExpr.builder() + .setVariableExpr(subresponseCountVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(responderVarExpr) + .setMethodName(getFooCountMethodName) + .setReturnType(subresponseCountVarExpr.type()) + .build()) + .build())); + + List innerSubresponseForExprs = new ArrayList<>(); + String getSubresponseFieldMethodName = + String.format( + "get%s", JavaStyle.toUpperCamelCase(batchingSettings.subresponseFieldName())); + Expr addMethodArgExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(batchResponseVarExpr) + .setMethodName(getSubresponseFieldMethodName) + .setArguments(batchMessageIndexVarExpr) + .build(); + innerSubresponseForExprs.add( + MethodInvocationExpr.builder() + .setExprReferenceExpr(subresponseElementsVarExpr) + .setMethodName("add") + .setArguments(addMethodArgExpr) + .build()); + // TODO(miraleung): Increment batchMessageIndexVarExpr. + + VariableExpr forIndexVarExpr = + VariableExpr.withVariable(Variable.builder().setType(TypeNode.INT).setName("i").build()); + GeneralForStatement innerSubresponseForStatement = + GeneralForStatement.incrementWith( + forIndexVarExpr, + subresponseCountVarExpr, + innerSubresponseForExprs.stream() + .map(e -> ExprStatement.withExpr(e)) + .collect(Collectors.toList())); + + outerForBody.add(innerSubresponseForStatement); + } + + TypeNode responseType = method.outputType(); + Expr responseBuilderExpr = + MethodInvocationExpr.builder() + .setStaticReferenceType(responseType) + .setMethodName("newBuilder") + .build(); + if (hasSubresponseField) { + Preconditions.checkNotNull( + subresponseElementsVarExpr, + String.format( + "subresponseElements variable should not be null for method %s", method.name())); + + responseBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(responseBuilderExpr) + .setMethodName( + String.format( + "addAll%s", + JavaStyle.toUpperCamelCase(batchingSettings.subresponseFieldName()))) + .setArguments(subresponseElementsVarExpr) + .build(); + } + responseBuilderExpr = + MethodInvocationExpr.builder() + .setExprReferenceExpr(responseBuilderExpr) + .setMethodName("build") + .setReturnType(responseType) + .build(); + + VariableExpr responseVarExpr = + VariableExpr.withVariable( + Variable.builder().setType(responseType).setName("response").build()); + outerForBody.add( + ExprStatement.withExpr( + AssignmentExpr.builder() + .setVariableExpr(responseVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr(responseBuilderExpr) + .build())); + + outerForBody.add( + ExprStatement.withExpr( + MethodInvocationExpr.builder() + .setExprReferenceExpr(responderVarExpr) + .setMethodName("setResponse") + .setArguments(responseVarExpr) + .build())); + + ForStatement outerForStatement = + ForStatement.builder() + .setLocalVariableExpr(responderVarExpr.toBuilder().setIsDecl(true).build()) + .setCollectionExpr(batchVarExpr) + .setBody(outerForBody) + .build(); + + List bodyStatements = new ArrayList<>(); + if (hasSubresponseField) { + bodyStatements.add( + ExprStatement.withExpr( + AssignmentExpr.builder() + .setVariableExpr(batchMessageIndexVarExpr.toBuilder().setIsDecl(true).build()) + .setValueExpr( + ValueExpr.withValue( + PrimitiveValue.builder().setType(TypeNode.INT).setValue("0").build())) + .build())); + } + bodyStatements.add(outerForStatement); + + return MethodDefinition.builder() + .setIsOverride(true) + .setScope(ScopeNode.PUBLIC) + .setReturnType(TypeNode.VOID) + .setName("splitResponse") + .setArguments( + Arrays.asList(batchResponseVarExpr, batchVarExpr).stream() + .map(v -> v.toBuilder().setIsDecl(true).build()) + .collect(Collectors.toList())) + .setBody(bodyStatements) + .build(); + } + private static MethodDefinition createSplitExceptionMethod(Method method) { VariableExpr throwableVarExpr = VariableExpr.withVariable( diff --git a/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java b/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java index e412e0a832..844f8b3cbd 100644 --- a/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java +++ b/src/test/java/com/google/api/generator/gapic/composer/BatchingDescriptorComposerTest.java @@ -129,6 +129,22 @@ public void batchingDescriptor_hasSubresponseField() { "};\n", "}\n", "@Override\n", + "public void splitResponse(", + "PublishResponse batchResponse, ", + "Collection> batch) {\n", + "int batchMessageIndex = 0;\n", + "for (BatchedRequestIssuer responder : batch) {\n", + "List subresponseElements = new ArrayList<>();\n", + "long subresponseCount = responder.getMessageCount();\n", + "for (int i = 0; i < subresponseCount; i++) {\n", + "subresponseElements.add(batchResponse.getMessageIds(batchMessageIndex));\n", + "}\n", + "PublishResponse response = ", + "PublishResponse.newBuilder().addAllMessageIds(subresponseElements).build();\n", + "responder.setResponse(response);\n", + "}\n", + "}\n", + "@Override\n", "public void splitException(", "Throwable throwable, ", "Collection> batch) {\n", @@ -226,6 +242,14 @@ public void batchingDescriptor_noSubresponseField() { "};\n", "}\n", "@Override\n", + "public void splitResponse(WriteLogEntriesResponse batchResponse, ", + "Collection> batch) {\n", + "for (BatchedRequestIssuer responder : batch) {\n", + "WriteLogEntriesResponse response = WriteLogEntriesResponse.newBuilder().build();\n", + "responder.setResponse(response);\n", + "}\n", + "}\n", + "@Override\n", "public void splitException(", "Throwable throwable, ", "Collection> batch) {\n", 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 10456faed3..52b025ead1 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 @@ -999,6 +999,19 @@ private static List parseServices( + " }\n" + "\n" + " @Override\n" + + " public void splitResponse(\n" + + " WriteLogEntriesResponse batchResponse,\n" + + " Collection>" + + " batch) {\n" + + " for (BatchedRequestIssuer responder : batch)" + + " {\n" + + " WriteLogEntriesResponse response =" + + " WriteLogEntriesResponse.newBuilder().build();\n" + + " responder.setResponse(response);\n" + + " }\n" + + " }\n" + + "\n" + + " @Override\n" + " public void splitException(\n" + " Throwable throwable,\n" + " Collection>" @@ -1397,6 +1410,7 @@ private static List parseServices( + "import com.google.pubsub.v1.Topic;\n" + "import com.google.pubsub.v1.UpdateTopicRequest;\n" + "import java.io.IOException;\n" + + "import java.util.ArrayList;\n" + "import java.util.Collection;\n" + "import java.util.List;\n" + "import java.util.Objects;\n" @@ -1662,6 +1676,25 @@ private static List parseServices( + " }\n" + "\n" + " @Override\n" + + " public void splitResponse(\n" + + " PublishResponse batchResponse,\n" + + " Collection> batch) {\n" + + " int batchMessageIndex = 0;\n" + + " for (BatchedRequestIssuer responder : batch) {\n" + + " List subresponseElements = new ArrayList<>();\n" + + " long subresponseCount = responder.getMessageCount();\n" + + " for (int i = 0; i < subresponseCount; i++) {\n" + + " " + + " subresponseElements.add(batchResponse.getMessageIds(batchMessageIndex));\n" + + " }\n" + + " PublishResponse response =\n" + + " " + + " PublishResponse.newBuilder().addAllMessageIds(subresponseElements).build();\n" + + " responder.setResponse(response);\n" + + " }\n" + + " }\n" + + "\n" + + " @Override\n" + " public void splitException(\n" + " Throwable throwable,\n" + " Collection> batch) {\n"