diff --git a/src/main/java/com/google/api/generator/gapic/protoparser/MethodSignatureParser.java b/src/main/java/com/google/api/generator/gapic/protoparser/MethodSignatureParser.java index b811c6bb9b..742533e107 100644 --- a/src/main/java/com/google/api/generator/gapic/protoparser/MethodSignatureParser.java +++ b/src/main/java/com/google/api/generator/gapic/protoparser/MethodSignatureParser.java @@ -55,7 +55,8 @@ public static List> parseMethodSignatures( return signatures; } - Map patternsToResourceNames = createPatternResourceNameMap(resourceNames); + Map patternsToResourceNames = + ResourceParserHelpers.createPatternResourceNameMap(resourceNames); Message inputMessage = messageTypes.get(methodInputType.reference().simpleName()); // Example from Expand in echo.proto: 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 b69a43cdee..c965c8e864 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 @@ -113,12 +113,15 @@ public static GapicContext parse(CodeGeneratorRequest request) { ? ServiceYamlParser.parse(serviceYamlConfigPathOpt.get()) : Optional.empty(); + // Collect the resource references seen in messages. + Set outputResourceReferencesSeen = new HashSet<>(); // Keep message and resource name parsing separate for cleaner logic. // While this takes an extra pass through the protobufs, the extra time is relatively trivial // and is worth the larger reduced maintenance cost. - Map messages = parseMessages(request); + Map messages = parseMessages(request, outputResourceReferencesSeen); Map resourceNames = parseResourceNames(request); messages = updateResourceNamesInMessages(messages, resourceNames.values()); + Set outputArgResourceNames = new HashSet<>(); List services = parseServices( @@ -128,6 +131,25 @@ public static GapicContext parse(CodeGeneratorRequest request) { outputArgResourceNames, serviceYamlProtoOpt, serviceConfigOpt); + + Preconditions.checkState(!services.isEmpty(), "No services found to generate"); + + // Include all resource names present in message types for backwards-compatibility with the + // monolith. In the future, this should be removed on a client library major semver update. + outputArgResourceNames.addAll( + resourceNames.values().stream() + .filter(r -> r.hasParentMessageName()) + .collect(Collectors.toSet())); + + String servicePackage = services.get(0).pakkage(); + Map patternsToResourceNames = + ResourceParserHelpers.createPatternResourceNameMap(resourceNames); + for (ResourceReference resourceReference : outputResourceReferencesSeen) { + outputArgResourceNames.addAll( + ResourceReferenceParser.parseResourceNames( + resourceReference, servicePackage, null, resourceNames, patternsToResourceNames)); + } + return GapicContext.builder() .setServices(services) .setMessages(messages) @@ -278,33 +300,43 @@ public static List parseService( .collect(Collectors.toList()); } - public static Map parseMessages(CodeGeneratorRequest request) { + public static Map parseMessages( + CodeGeneratorRequest request, Set outputResourceReferencesSeen) { Map fileDescriptors = getFilesToGenerate(request); Map messages = new HashMap<>(); // Look for message types amongst all the protos, not just the ones to generate. This will // ensure we track commonly-used protos like Empty. for (FileDescriptor fileDescriptor : fileDescriptors.values()) { - messages.putAll(parseMessages(fileDescriptor)); + messages.putAll(parseMessages(fileDescriptor, outputResourceReferencesSeen)); } return messages; } + // TODO(miraleung): Propagate the internal method to all tests, and remove this wrapper. public static Map parseMessages(FileDescriptor fileDescriptor) { + return parseMessages(fileDescriptor, new HashSet<>()); + } + + public static Map parseMessages( + FileDescriptor fileDescriptor, Set outputResourceReferencesSeen) { // TODO(miraleung): Preserve nested type and package data in the type key. Map messages = new HashMap<>(); for (Descriptor messageDescriptor : fileDescriptor.getMessageTypes()) { - messages.putAll(parseMessages(messageDescriptor)); + messages.putAll(parseMessages(messageDescriptor, outputResourceReferencesSeen)); } return messages; } - private static Map parseMessages(Descriptor messageDescriptor) { - return parseMessages(messageDescriptor, new ArrayList()); + private static Map parseMessages( + Descriptor messageDescriptor, Set outputResourceReferencesSeen) { + return parseMessages(messageDescriptor, outputResourceReferencesSeen, new ArrayList()); } private static Map parseMessages( - Descriptor messageDescriptor, List outerNestedTypes) { + Descriptor messageDescriptor, + Set outputResourceReferencesSeen, + List outerNestedTypes) { Map messages = new HashMap<>(); String messageName = messageDescriptor.getName(); if (!messageDescriptor.getNestedTypes().isEmpty()) { @@ -313,7 +345,8 @@ private static Map parseMessages( continue; } outerNestedTypes.add(messageName); - messages.putAll(parseMessages(nestedMessage, outerNestedTypes)); + messages.putAll( + parseMessages(nestedMessage, outputResourceReferencesSeen, outerNestedTypes)); } } messages.put( @@ -321,7 +354,7 @@ private static Map parseMessages( Message.builder() .setType(TypeParser.parseType(messageDescriptor)) .setName(messageName) - .setFields(parseFields(messageDescriptor)) + .setFields(parseFields(messageDescriptor, outputResourceReferencesSeen)) .setOuterNestedTypes(outerNestedTypes) .build()); return messages; @@ -532,15 +565,21 @@ static String sanitizeDefaultHost(String rawDefaultHost) { return String.format("%s:%s", rawDefaultHost, DEFAULT_PORT); } - private static List parseFields(Descriptor messageDescriptor) { + private static List parseFields( + Descriptor messageDescriptor, Set outputResourceReferencesSeen) { 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()); + return fields.stream() + .map(f -> parseField(f, messageDescriptor, outputResourceReferencesSeen)) + .collect(Collectors.toList()); } - private static Field parseField(FieldDescriptor fieldDescriptor, Descriptor messageDescriptor) { + private static Field parseField( + FieldDescriptor fieldDescriptor, + Descriptor messageDescriptor, + Set outputResourceReferencesSeen) { FieldOptions fieldOptions = fieldDescriptor.getOptions(); MessageOptions messageOptions = messageDescriptor.getOptions(); ResourceReference resourceReference = null; @@ -560,6 +599,7 @@ private static Field parseField(FieldDescriptor fieldDescriptor, Descriptor mess isChildType ? ResourceReference.withChildType(childTypeString) : ResourceReference.withType(typeString); + outputResourceReferencesSeen.add(resourceReference); } else if (messageOptions.hasExtension(ResourceProto.resource)) { ResourceDescriptor protoResource = messageOptions.getExtension(ResourceProto.resource); diff --git a/src/main/java/com/google/api/generator/gapic/protoparser/ResourceParserHelpers.java b/src/main/java/com/google/api/generator/gapic/protoparser/ResourceParserHelpers.java new file mode 100644 index 0000000000..ff6f04777a --- /dev/null +++ b/src/main/java/com/google/api/generator/gapic/protoparser/ResourceParserHelpers.java @@ -0,0 +1,32 @@ +// 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.protoparser; + +import com.google.api.generator.gapic.model.ResourceName; +import java.util.HashMap; +import java.util.Map; + +public class ResourceParserHelpers { + public static Map createPatternResourceNameMap( + Map resourceNames) { + Map patternsToResourceNames = new HashMap<>(); + for (ResourceName resourceName : resourceNames.values()) { + for (String pattern : resourceName.patterns()) { + patternsToResourceNames.put(pattern, resourceName); + } + } + return patternsToResourceNames; + } +} diff --git a/test/integration/goldens/logging/ConfigClientTest.java b/test/integration/goldens/logging/ConfigClientTest.java index 1fbb5e36ba..ddaee28039 100644 --- a/test/integration/goldens/logging/ConfigClientTest.java +++ b/test/integration/goldens/logging/ConfigClientTest.java @@ -678,7 +678,7 @@ public void getSinkTest() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -724,7 +724,7 @@ public void getSinkTest2() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -770,7 +770,7 @@ public void createSinkTest() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -819,7 +819,7 @@ public void createSinkTest2() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -868,7 +868,7 @@ public void createSinkTest3() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -917,7 +917,7 @@ public void createSinkTest4() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -966,7 +966,7 @@ public void createSinkTest5() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -1015,7 +1015,7 @@ public void updateSinkTest() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -1064,7 +1064,7 @@ public void updateSinkTest2() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -1113,7 +1113,7 @@ public void updateSinkTest3() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true) @@ -1165,7 +1165,7 @@ public void updateSinkTest4() throws Exception { LogSink expectedResponse = LogSink.newBuilder() .setName(LogSinkName.ofProjectSinkName("[PROJECT]", "[SINK]").toString()) - .setDestination("destination-1429847026") + .setDestination(FolderLocationName.of("[FOLDER]", "[LOCATION]").toString()) .setFilter("filter-1274492040") .setDescription("description-1724546052") .setDisabled(true)