From 76de79dcb32824d6c84ff719473f665bc99a4ad4 Mon Sep 17 00:00:00 2001 From: Mira Leung Date: Sun, 12 Jul 2020 12:31:53 -0700 Subject: [PATCH] feat: add LRO parsing --- .../api/generator/gapic/model/BUILD.bazel | 1 + .../gapic/model/LongrunningOperation.java | 44 +++++++++++++++++++ .../api/generator/gapic/model/Method.java | 10 +++++ .../generator/gapic/protoparser/BUILD.bazel | 9 ++++ .../generator/gapic/protoparser/Parser.java | 28 ++++++++++++ .../gapic/protoparser/ParserTest.java | 37 ++++++++++++++++ 6 files changed, 129 insertions(+) create mode 100644 src/main/java/com/google/api/generator/gapic/model/LongrunningOperation.java diff --git a/src/main/java/com/google/api/generator/gapic/model/BUILD.bazel b/src/main/java/com/google/api/generator/gapic/model/BUILD.bazel index e104bd8052..aa4d4014b4 100644 --- a/src/main/java/com/google/api/generator/gapic/model/BUILD.bazel +++ b/src/main/java/com/google/api/generator/gapic/model/BUILD.bazel @@ -15,6 +15,7 @@ java_library( "//src/main/java/com/google/api/generator/engine/ast", "@com_google_auto_value_auto_value//jar", "@com_google_auto_value_auto_value_annotations//jar", + "@com_google_code_findbugs_jsr305//jar", "@com_google_guava_guava//jar", ], ) diff --git a/src/main/java/com/google/api/generator/gapic/model/LongrunningOperation.java b/src/main/java/com/google/api/generator/gapic/model/LongrunningOperation.java new file mode 100644 index 0000000000..c2b425e2f8 --- /dev/null +++ b/src/main/java/com/google/api/generator/gapic/model/LongrunningOperation.java @@ -0,0 +1,44 @@ +// 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.model; + +import com.google.api.generator.engine.ast.TypeNode; +import com.google.auto.value.AutoValue; + +@AutoValue +public abstract class LongrunningOperation { + public abstract TypeNode responseType(); + + public abstract TypeNode metadataType(); + + public static LongrunningOperation withTypes(TypeNode responseType, TypeNode metadataType) { + return builder().setResponseType(responseType).setMetadataType(metadataType).build(); + } + + // Private. + static Builder builder() { + return new AutoValue_LongrunningOperation.Builder(); + } + + // Private. + @AutoValue.Builder + abstract static class Builder { + abstract Builder setResponseType(TypeNode responseType); + + abstract Builder setMetadataType(TypeNode metadataType); + + abstract LongrunningOperation build(); + } +} diff --git a/src/main/java/com/google/api/generator/gapic/model/Method.java b/src/main/java/com/google/api/generator/gapic/model/Method.java index 3ec8425078..785734aad3 100644 --- a/src/main/java/com/google/api/generator/gapic/model/Method.java +++ b/src/main/java/com/google/api/generator/gapic/model/Method.java @@ -16,6 +16,7 @@ import com.google.api.generator.engine.ast.TypeNode; import com.google.auto.value.AutoValue; +import javax.annotation.Nullable; @AutoValue public abstract class Method { @@ -34,6 +35,13 @@ public enum Stream { public abstract TypeNode outputType(); + @Nullable + public abstract LongrunningOperation lro(); + + public boolean hasLro() { + return lro() != null; + } + // TODO(miraleung): Parse annotations, comments. public static Builder builder() { @@ -63,6 +71,8 @@ public abstract static class Builder { public abstract Builder setStream(Stream stream); + public abstract Builder setLro(LongrunningOperation lro); + public abstract Method build(); } } diff --git a/src/main/java/com/google/api/generator/gapic/protoparser/BUILD.bazel b/src/main/java/com/google/api/generator/gapic/protoparser/BUILD.bazel index 85030f7773..b17b52cd66 100644 --- a/src/main/java/com/google/api/generator/gapic/protoparser/BUILD.bazel +++ b/src/main/java/com/google/api/generator/gapic/protoparser/BUILD.bazel @@ -5,12 +5,21 @@ filegroup( srcs = glob(["*.java"]), ) +java_proto_library( + name = "longrunning_java_proto", + visibility = ["//visibility:private"], + deps = [ + "@com_google_googleapis//google/longrunning:operations_proto", + ], +) + java_library( name = "protoparser", srcs = [ ":protoparser_files", ], deps = [ + ":longrunning_java_proto", "//src/main/java/com/google/api/generator/engine/ast", "//src/main/java/com/google/api/generator/gapic/model", "@com_google_code_findbugs_jsr305//jar", 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 c3f460bb33..3587182d84 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 @@ -17,16 +17,21 @@ import com.google.api.generator.engine.ast.TypeNode; import com.google.api.generator.engine.ast.VaporReference; import com.google.api.generator.gapic.model.Field; +import com.google.api.generator.gapic.model.LongrunningOperation; import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.Method; import com.google.api.generator.gapic.model.Service; import com.google.common.annotations.VisibleForTesting; import com.google.common.base.Preconditions; import com.google.common.collect.Maps; +import com.google.longrunning.OperationInfo; +import com.google.longrunning.OperationsProto; import com.google.protobuf.DescriptorProtos.FileDescriptorProto; +import com.google.protobuf.DescriptorProtos.MethodOptions; import com.google.protobuf.Descriptors.Descriptor; import com.google.protobuf.Descriptors.DescriptorValidationException; import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.MethodDescriptor; import com.google.protobuf.Descriptors.ServiceDescriptor; import com.google.protobuf.compiler.PluginProtos.CodeGeneratorRequest; import java.util.ArrayList; @@ -115,10 +120,33 @@ static List parseMethods( .setInputType(TypeParser.parseType(md.getInputType())) .setOutputType(TypeParser.parseType(md.getOutputType())) .setStream(Method.toStream(md.isClientStreaming(), md.isServerStreaming())) + .setLro(parseLro(md, messageTypes)) .build()) .collect(Collectors.toList()); } + @VisibleForTesting + static LongrunningOperation parseLro( + MethodDescriptor methodDescriptor, Map messageTypes) { + MethodOptions methodOptions = methodDescriptor.getOptions(); + if (!methodOptions.hasExtension(OperationsProto.operationInfo)) { + return null; + } + + OperationInfo lroInfo = + methodDescriptor.getOptions().getExtension(OperationsProto.operationInfo); + String responseTypeName = lroInfo.getResponseType(); + String metadataTypeName = lroInfo.getMetadataType(); + Message responseMessage = messageTypes.get(responseTypeName); + Message metadataMessage = messageTypes.get(metadataTypeName); + Preconditions.checkNotNull( + responseMessage, String.format("LRO response message %s not found", responseTypeName)); + Preconditions.checkNotNull( + metadataMessage, String.format("LRO metadata message %s not found", metadataTypeName)); + + return LongrunningOperation.withTypes(responseMessage.type(), metadataMessage.type()); + } + private static List parseFields(Descriptor messageDescriptor) { return messageDescriptor.getFields().stream() .map(f -> Field.builder().setName(f.getName()).setType(TypeParser.parseType(f)).build()) 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 3fccb25f12..fe25925536 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 @@ -16,6 +16,8 @@ import static com.google.common.truth.Truth.assertThat; import static junit.framework.Assert.assertEquals; +import static junit.framework.Assert.assertTrue; +import static org.junit.Assert.assertThrows; import com.google.api.generator.engine.ast.TypeNode; import com.google.api.generator.engine.ast.VaporReference; @@ -23,6 +25,7 @@ import com.google.api.generator.gapic.model.Message; import com.google.api.generator.gapic.model.Method; import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.MethodDescriptor; import com.google.protobuf.Descriptors.ServiceDescriptor; import com.google.showcase.v1beta1.EchoOuterClass; import java.util.Arrays; @@ -102,4 +105,38 @@ public void parseMethods_basic() { assertEquals(chatMethod.name(), "Chat"); assertEquals(chatMethod.stream(), Method.Stream.BIDI); } + + @Test + public void parseMethods_basicLro() { + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + List methods = Parser.parseMethods(echoService, messageTypes); + assertThat(methods.size()).isEqualTo(7); + + // Methods should appear in the same order as in the protobuf file. + Method waitMethod = methods.get(5); + assertEquals(waitMethod.name(), "Wait"); + assertTrue(waitMethod.hasLro()); + TypeNode waitResponseType = messageTypes.get("WaitResponse").type(); + TypeNode waitMetadataType = messageTypes.get("WaitMetadata").type(); + assertThat(waitMethod.lro().responseType()).isEqualTo(waitResponseType); + assertThat(waitMethod.lro().metadataType()).isEqualTo(waitMetadataType); + } + + @Test + public void parseMethods_lroMissingResponseType() { + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + MethodDescriptor waitMethodDescriptor = echoService.getMethods().get(5); + messageTypes.remove("WaitResponse"); + assertThrows( + NullPointerException.class, () -> Parser.parseLro(waitMethodDescriptor, messageTypes)); + } + + @Test + public void parseMethods_lroMissingMetadataType() { + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + MethodDescriptor waitMethodDescriptor = echoService.getMethods().get(5); + messageTypes.remove("WaitMetadata"); + assertThrows( + NullPointerException.class, () -> Parser.parseLro(waitMethodDescriptor, messageTypes)); + } }