From d023f2456179b1aeff718ed2770b99c45277774b Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 13:18:20 -0700 Subject: [PATCH 01/10] fix: support LRO package.name annotations --- .../api/generator/gapic/protoparser/Parser.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) 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 f667402210..ec7d6a83d5 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 @@ -64,6 +64,7 @@ public class Parser { private static final String COMMA = ","; private static final String COLON = ":"; + private static final String DOT = "."; private static final String DEFAULT_PORT = "443"; // Allow other parsers to access this. @@ -320,12 +321,23 @@ static LongrunningOperation parseLro( OperationInfo lroInfo = methodDescriptor.getOptions().getExtension(OperationsProto.operationInfo); + String responseTypeName = lroInfo.getResponseType(); + String responseTypePackage = ""; + if (responseTypeName.contains(DOT)) { + responseTypeName = responseTypeName.substring(responseTypeName.lastIndexOf(DOT) + 1); + } + String metadataTypeName = lroInfo.getMetadataType(); + if (metadataTypeName.contains(DOT)) { + metadataTypeName = metadataTypeName.substring(metadataTypeName.lastIndexOf(DOT) + 1); + } + Message responseMessage = messageTypes.get(responseTypeName); Message metadataMessage = messageTypes.get(metadataTypeName); Preconditions.checkNotNull( responseMessage, String.format("LRO response message %s not found", responseTypeName)); + // TODO(miraleung): Check that the packages are equal if those strings are not empty. Preconditions.checkNotNull( metadataMessage, String.format("LRO metadata message %s not found", metadataTypeName)); From baa5aa6882402307f76d00e05542e67ff8fb87e5 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 13:23:47 -0700 Subject: [PATCH 02/10] feat: add microgenerator rules for cloud/asset --- .githooks/pre-commit | 4 ++-- repositories.bzl | 4 ++-- test/integration/BUILD.bazel | 26 ++++++++++++++++++++++++++ 3 files changed, 30 insertions(+), 4 deletions(-) create mode 100644 test/integration/BUILD.bazel diff --git a/.githooks/pre-commit b/.githooks/pre-commit index a0ef4987d4..1a1c841589 100755 --- a/.githooks/pre-commit +++ b/.githooks/pre-commit @@ -100,7 +100,7 @@ fi if [ $NUM_JAVA_FILES_CHANGED -gt 0 ] || [ $NUM_BAZEL_FILES_CHANGED -gt 0 ] then echo_status "Checking the build..." - bazel --batch build --disk_cache="$BAZEL_CACHE_DIR" //... + bazel --batch build --disk_cache="$BAZEL_CACHE_DIR" //src/... BUILD_STATUS=$? if [ $BUILD_STATUS != 0 ] then @@ -112,7 +112,7 @@ fi if [ $NUM_JAVA_FILES_CHANGED -gt 0 ] then echo_status "Checking tests..." - bazel --batch test --disk_cache="$BAZEL_CACHE_DIR" //... + bazel --batch test --disk_cache="$BAZEL_CACHE_DIR" //src/test/... TEST_STATUS=$? if [ $TEST_STATUS != 0 ] then diff --git a/repositories.bzl b/repositories.bzl index 9da79a5faf..9b1a8cd45b 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -59,9 +59,9 @@ def gapic_generator_java_repositories(): _maybe( http_archive, name = "com_google_googleapis", - strip_prefix = "googleapis-dbdd8ddeb6d9556952aa8784d9e48f2566c9911a", + strip_prefix = "googleapis-bda7ce951def5ae6e5c4258d0e569188dd4ae02b", urls = [ - "https://github.com/googleapis/googleapis/archive/dbdd8ddeb6d9556952aa8784d9e48f2566c9911a.zip", + "https://github.com/googleapis/googleapis/archive/bda7ce951def5ae6e5c4258d0e569188dd4ae02b.zip", ], ) diff --git a/test/integration/BUILD.bazel b/test/integration/BUILD.bazel new file mode 100644 index 0000000000..60a4ca6aa9 --- /dev/null +++ b/test/integration/BUILD.bazel @@ -0,0 +1,26 @@ +package(default_visibility = ["//visibility:public"]) + +#################################################### +# API Library Rules +#################################################### +# These will eventually go away once more APIs in googleapis have been migrated to the microgenerator. + +load( + "@com_google_googleapis_imports//:imports.bzl", + java_gapic_library = "java_gapic_library2", +) + +java_gapic_library( + name = "asset_java_gapic", + srcs = ["@com_google_googleapis//google/cloud/asset/v1:asset_proto_with_info"], + grpc_service_config = "@com_google_googleapis//google/cloud/asset/v1:cloudasset_grpc_service_config.json", + package = "google.cloud.asset.v1", + test_deps = [ + "@com_google_googleapis//google/cloud/asset/v1:asset_java_grpc", + "@com_google_googleapis//google/iam/v1:iam_java_grpc", + ], + deps = [ + "@com_google_googleapis//google/cloud/asset/v1:asset_java_proto", + "@com_google_googleapis//google/iam/v1:iam_java_proto", + ], +) From 462812b3ecf2c088efb0ab0d2e6e201ae7960376 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 14:16:47 -0700 Subject: [PATCH 03/10] fix: update CircleCI --- .circleci/config.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 5a5afd7c9d..f64b58040c 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -32,12 +32,12 @@ jobs: name: Build targets for gapic-generator-java command: | cd /tmp/gapic-generator-java - bazel --batch build //... + bazel --batch build //src/... - run: name: Run unit tests for gapic-generator-java command: | cd /tmp/gapic-generator-java - bazel --batch test //... --noshow_progress + bazel --batch test //src/test/... --noshow_progress find . -type f -regex ".*/bazel-testlogs/.*xml" -exec cp {} ${TEST_REPORTS_DIR} \; - store_test_results: path: bazel/reports/gapic-generator-java From 63f9156fe8121ab19c61fb769a7d7b483e1187cd Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 14:35:33 -0700 Subject: [PATCH 04/10] fix: sort message fields in asc. index order --- .../composer/ServiceStubSettingsClassComposer.java | 3 ++- .../api/generator/gapic/protoparser/Parser.java | 8 +++++--- .../generator/gapic/protoparser/ParserTest.java | 14 +++++++------- 3 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceStubSettingsClassComposer.java index fe8456bd00..1bc36da476 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 @@ -355,13 +355,14 @@ private static List createPagingStaticAssignExprs( Preconditions.checkState( field != null, String.format("Null field found for message %s", pagedResponseMessage.name())); - if (field.isRepeated()) { + if (field.isRepeated() && !field.isMap()) { // Field is currently a List-type. Preconditions.checkState( !field.type().reference().generics().isEmpty(), String.format("No generics found for field reference %s", field.type().reference())); repeatedResponseType = TypeNode.withReference(field.type().reference().generics().get(0)); repeatedFieldName = field.name(); + break; } } Preconditions.checkNotNull( 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 ec7d6a83d5..43e7e7c435 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 @@ -364,9 +364,11 @@ static String sanitizeDefaultHost(String rawDefaultHost) { } private static List parseFields(Descriptor messageDescriptor) { - return messageDescriptor.getFields().stream() - .map(f -> parseField(f, messageDescriptor)) - .collect(Collectors.toList()); + List fields = new ArrayList<>(messageDescriptor.getFields()); + // Sort by ascending field index order. This is important for paged responses, where the first + // repeated type is taken. + fields.sort((f1, f2) -> f1.getIndex() - f2.getIndex()); + return fields.stream().map(f -> parseField(f, messageDescriptor)).collect(Collectors.toList()); } private static Field parseField(FieldDescriptor fieldDescriptor, Descriptor messageDescriptor) { 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 e05c4203d6..b310cd661e 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 @@ -250,20 +250,20 @@ public void parseMethodSignatures_basic() { ImmutableList.of(), argument); - // Signature contents: ["name"], String variant. - methodArgs = methodSignatures.get(3); - assertEquals(1, methodArgs.size()); - argument = methodArgs.get(0); - assertMethodArgumentEquals("name", TypeNode.STRING, ImmutableList.of(), argument); - // Signature contents: ["name"], resource helper variant. - methodArgs = methodSignatures.get(4); + methodArgs = methodSignatures.get(3); assertEquals(1, methodArgs.size()); argument = methodArgs.get(0); TypeNode foobarNameType = TypeNode.withReference( VaporReference.builder().setName("FoobarName").setPakkage(ECHO_PACKAGE).build()); assertMethodArgumentEquals("name", foobarNameType, ImmutableList.of(), argument); + + // Signature contents: ["name"], String variant. + methodArgs = methodSignatures.get(4); + assertEquals(1, methodArgs.size()); + argument = methodArgs.get(0); + assertMethodArgumentEquals("name", TypeNode.STRING, ImmutableList.of(), argument); } @Test From b8aa742f98f6540bf4f753c8d0b162b83038427e Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 14:36:35 -0700 Subject: [PATCH 05/10] feat: add cloud/redis microgenerator rules --- test/integration/BUILD.bazel | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/test/integration/BUILD.bazel b/test/integration/BUILD.bazel index 60a4ca6aa9..02e0520fde 100644 --- a/test/integration/BUILD.bazel +++ b/test/integration/BUILD.bazel @@ -7,6 +7,7 @@ package(default_visibility = ["//visibility:public"]) load( "@com_google_googleapis_imports//:imports.bzl", + "proto_library_with_info", java_gapic_library = "java_gapic_library2", ) @@ -24,3 +25,28 @@ java_gapic_library( "@com_google_googleapis//google/iam/v1:iam_java_proto", ], ) + +# Redis API. +# Redefine the proto_with_info target so we can include empty_proto. +# This should be removed post-migration. +proto_library_with_info( + name = "redis_proto_with_info", + deps = [ + "@com_google_googleapis//google/cloud:common_resources_proto", + "@com_google_googleapis//google/cloud/redis/v1:redis_proto", + "@com_google_protobuf//:empty_proto", + ], +) + +java_gapic_library( + name = "redis_java_gapic", + srcs = ["redis_proto_with_info"], + grpc_service_config = "@com_google_googleapis//google/cloud/redis/v1:redis_grpc_service_config.json", + package = "google.cloud.redis.v1", + test_deps = [ + "@com_google_googleapis//google/cloud/redis/v1:redis_java_grpc", + ], + deps = [ + "@com_google_googleapis//google/cloud/redis/v1:redis_java_proto", + ], +) From a20d9f85ae30a91e9f0a24eed669d1b1fd215cce Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 14:40:29 -0700 Subject: [PATCH 06/10] fix: linter --- test/integration/BUILD.bazel | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/test/integration/BUILD.bazel b/test/integration/BUILD.bazel index 02e0520fde..8c771fdac7 100644 --- a/test/integration/BUILD.bazel +++ b/test/integration/BUILD.bazel @@ -1,3 +1,9 @@ +load( + "@com_google_googleapis_imports//:imports.bzl", + "proto_library_with_info", + java_gapic_library = "java_gapic_library2", +) + package(default_visibility = ["//visibility:public"]) #################################################### @@ -5,12 +11,6 @@ package(default_visibility = ["//visibility:public"]) #################################################### # These will eventually go away once more APIs in googleapis have been migrated to the microgenerator. -load( - "@com_google_googleapis_imports//:imports.bzl", - "proto_library_with_info", - java_gapic_library = "java_gapic_library2", -) - java_gapic_library( name = "asset_java_gapic", srcs = ["@com_google_googleapis//google/cloud/asset/v1:asset_proto_with_info"], From 3746915d854f6b8d864eb5fe7224f2ee080c77b7 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 14:53:04 -0700 Subject: [PATCH 07/10] fix: update parser test method sig order --- .../api/generator/gapic/protoparser/ParserTest.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) 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 e05c4203d6..e132056c1e 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 @@ -250,12 +250,6 @@ public void parseMethodSignatures_basic() { ImmutableList.of(), argument); - // Signature contents: ["name"], String variant. - methodArgs = methodSignatures.get(3); - assertEquals(1, methodArgs.size()); - argument = methodArgs.get(0); - assertMethodArgumentEquals("name", TypeNode.STRING, ImmutableList.of(), argument); - // Signature contents: ["name"], resource helper variant. methodArgs = methodSignatures.get(4); assertEquals(1, methodArgs.size()); @@ -264,6 +258,12 @@ public void parseMethodSignatures_basic() { TypeNode.withReference( VaporReference.builder().setName("FoobarName").setPakkage(ECHO_PACKAGE).build()); assertMethodArgumentEquals("name", foobarNameType, ImmutableList.of(), argument); + + // Signature contents: ["name"], String variant. + methodArgs = methodSignatures.get(3); + assertEquals(1, methodArgs.size()); + argument = methodArgs.get(0); + assertMethodArgumentEquals("name", TypeNode.STRING, ImmutableList.of(), argument); } @Test From 59ef6689061b088d09980908a8ce8054367caa64 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 14:59:03 -0700 Subject: [PATCH 08/10] fix: update circleci config --- .circleci/config.yml | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index f64b58040c..730e03fe18 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -5,7 +5,7 @@ jobs: gapic-generator-java-tests: working_directory: /tmp/ environment: - TEST_REPORTS_DIR: /tmp/workspace/bazel/reports/gapic-generator-java + TEST_REPORTS_DIR: ~/.cache/bazel BAZEL_VERSION: 3.5.1 PYTHON_VERSION: 3.5.2 machine: true @@ -28,6 +28,7 @@ jobs: name: Make reports directory command: | mkdir -p ${TEST_REPORTS_DIR} + echo "Test reports will be stored in ${TEST_REPORTS_DIR}" - run: name: Build targets for gapic-generator-java command: | @@ -40,9 +41,9 @@ jobs: bazel --batch test //src/test/... --noshow_progress find . -type f -regex ".*/bazel-testlogs/.*xml" -exec cp {} ${TEST_REPORTS_DIR} \; - store_test_results: - path: bazel/reports/gapic-generator-java + path: ~/.cache/bazel - store_artifacts: - path: bazel/reports/gapic-generator-java + path: ~/.cache/bazel google-java-format: docker: - image: l.gcr.io/google/bazel From 51011eedcf6e5f7d31631df56bb321b84200cf46 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 16:52:19 -0700 Subject: [PATCH 09/10] fix: linter --- test/integration/BUILD.bazel | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/integration/BUILD.bazel b/test/integration/BUILD.bazel index 60a4ca6aa9..af5762b6e8 100644 --- a/test/integration/BUILD.bazel +++ b/test/integration/BUILD.bazel @@ -1,3 +1,8 @@ +load( + "@com_google_googleapis_imports//:imports.bzl", + java_gapic_library = "java_gapic_library2", +) + package(default_visibility = ["//visibility:public"]) #################################################### @@ -5,11 +10,6 @@ package(default_visibility = ["//visibility:public"]) #################################################### # These will eventually go away once more APIs in googleapis have been migrated to the microgenerator. -load( - "@com_google_googleapis_imports//:imports.bzl", - java_gapic_library = "java_gapic_library2", -) - java_gapic_library( name = "asset_java_gapic", srcs = ["@com_google_googleapis//google/cloud/asset/v1:asset_proto_with_info"], From 9ce0b1e5a4d8ae1c8d8334da882b18cb04aa40dc Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Fri, 9 Oct 2020 17:38:59 -0700 Subject: [PATCH 10/10] fix: consoliate findRepeatedField into Message --- .../composer/ServiceClientClassComposer.java | 14 ++++------- .../ServiceClientTestClassComposer.java | 19 ++++----------- .../ServiceStubSettingsClassComposer.java | 23 +++++-------------- .../api/generator/gapic/model/Field.java | 2 ++ .../api/generator/gapic/model/Message.java | 13 +++++++++++ 5 files changed, 30 insertions(+), 41 deletions(-) diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java index a8b830092d..9c6b88bfba 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientClassComposer.java @@ -817,21 +817,15 @@ private static List createNestedPagingClasses( } // Find the repeated field. Message methodOutputMessage = messageTypes.get(method.outputType().reference().name()); - TypeNode repeatedResponseType = null; - for (Field field : methodOutputMessage.fields()) { - if (field.isRepeated() && !field.isMap()) { - Reference repeatedGenericRef = field.type().reference().generics().get(0); - repeatedResponseType = TypeNode.withReference(repeatedGenericRef); - break; - } - } - + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); Preconditions.checkNotNull( - repeatedResponseType, + repeatedPagedResultsField, String.format( "No repeated field found on message %s for method %s", methodOutputMessage.name(), method.name())); + TypeNode repeatedResponseType = repeatedPagedResultsField.type(); + nestedClasses.add( createNestedRpcPagedResponseClass(method, repeatedResponseType, messageTypes, types)); nestedClasses.add( diff --git a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java index 39c8836c6c..c472aa94f0 100644 --- a/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java +++ b/src/main/java/com/google/api/generator/gapic/composer/ServiceClientTestClassComposer.java @@ -475,7 +475,6 @@ private static MethodDefinition createRpcTestMethod( method, serviceName, classMemberVarExprs, resourceNames, messageTypes); } // Construct the expected response. - // TODO(miraleung): Paging here. TypeNode methodOutputType = method.hasLro() ? method.lro().responseType() : method.outputType(); List methodExprs = new ArrayList<>(); @@ -483,12 +482,14 @@ private static MethodDefinition createRpcTestMethod( VariableExpr responsesElementVarExpr = null; if (method.isPaged()) { Message methodOutputMessage = messageTypes.get(method.outputType().reference().name()); - repeatedResponseType = findRepeatedPagedType(methodOutputMessage); + Field repeatedPagedResultsField = methodOutputMessage.findAndUnwrapFirstRepeatedField(); Preconditions.checkNotNull( - repeatedResponseType, + repeatedPagedResultsField, String.format( - "No repeated type found for paged method %s with output message type %s", + "No repeated field found for paged method %s with output message type %s", method.name(), methodOutputMessage.name())); + + repeatedResponseType = repeatedPagedResultsField.type(); responsesElementVarExpr = VariableExpr.withVariable( Variable.builder().setType(repeatedResponseType).setName("responsesElement").build()); @@ -1772,16 +1773,6 @@ private static TypeNode getCallableType(Method protoMethod) { ConcreteReference.builder().setClazz(callableClazz).setGenerics(generics).build()); } - private static TypeNode findRepeatedPagedType(Message message) { - for (Field field : message.fields()) { - if (field.isRepeated() && !field.isMap()) { - Reference repeatedGenericRef = field.type().reference().generics().get(0); - return TypeNode.withReference(repeatedGenericRef); - } - } - return null; - } - private static String getCallableMethodName(Method protoMethod) { Preconditions.checkState( !protoMethod.stream().equals(Method.Stream.NONE), 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 1bc36da476..cd4e971b91 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 @@ -349,28 +349,17 @@ private static List createPagingStaticAssignExprs( String.format( "No method found for message type %s for method %s among %s", pagedResponseMessageKey, method.name(), messageTypes.keySet())); - TypeNode repeatedResponseType = null; - String repeatedFieldName = null; - for (Field field : pagedResponseMessage.fields()) { - Preconditions.checkState( - field != null, - String.format("Null field found for message %s", pagedResponseMessage.name())); - if (field.isRepeated() && !field.isMap()) { - // Field is currently a List-type. - Preconditions.checkState( - !field.type().reference().generics().isEmpty(), - String.format("No generics found for field reference %s", field.type().reference())); - repeatedResponseType = TypeNode.withReference(field.type().reference().generics().get(0)); - repeatedFieldName = field.name(); - break; - } - } + + Field repeatedPagedResultsField = pagedResponseMessage.findAndUnwrapFirstRepeatedField(); Preconditions.checkNotNull( - repeatedResponseType, + repeatedPagedResultsField, String.format( "No repeated type found for paged reesponse %s for method %s", method.outputType().reference().name(), method.name())); + TypeNode repeatedResponseType = repeatedPagedResultsField.type(); + String repeatedFieldName = repeatedPagedResultsField.name(); + // Create the PAGE_STR_DESC variable. TypeNode pagedListDescType = TypeNode.withReference( 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 890c7043a0..0ab721f1e9 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 @@ -48,6 +48,8 @@ public boolean hasResourceReference() { return type().equals(TypeNode.STRING) && resourceReference() != null; } + abstract Builder toBuilder(); + public static Builder builder() { return new AutoValue_Field.Builder() .setIsMessage(false) diff --git a/src/main/java/com/google/api/generator/gapic/model/Message.java b/src/main/java/com/google/api/generator/gapic/model/Message.java index c0236e4f53..b9a613d000 100644 --- a/src/main/java/com/google/api/generator/gapic/model/Message.java +++ b/src/main/java/com/google/api/generator/gapic/model/Message.java @@ -14,6 +14,7 @@ package com.google.api.generator.gapic.model; +import com.google.api.generator.engine.ast.Reference; import com.google.api.generator.engine.ast.TypeNode; import com.google.auto.value.AutoValue; import com.google.common.collect.ImmutableList; @@ -46,6 +47,18 @@ public boolean hasResource() { return resource() != null; } + /** Returns the first list repeated field in a message, unwrapped from its list type. */ + @Nullable + public Field findAndUnwrapFirstRepeatedField() { + for (Field field : fields()) { + if (field.isRepeated() && !field.isMap()) { + Reference repeatedGenericRef = field.type().reference().generics().get(0); + return field.toBuilder().setType(TypeNode.withReference(repeatedGenericRef)).build(); + } + } + return null; + } + public static Builder builder() { return new AutoValue_Message.Builder(); }