diff --git a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/SpringWriterTest.java b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/SpringWriterTest.java index 3b78596bc8..12df0af38f 100644 --- a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/SpringWriterTest.java +++ b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/SpringWriterTest.java @@ -22,6 +22,7 @@ import com.google.api.generator.test.framework.Assert; import com.google.api.generator.test.framework.GoldenFileWriter; import com.google.api.generator.test.protoloader.TestProtoLoader; +import com.google.showcase.v1beta1.EchoOuterClass; import java.nio.file.Path; import java.nio.file.Paths; import org.junit.Before; @@ -32,7 +33,7 @@ public class SpringWriterTest { @Before public void setUp() { - this.context = TestProtoLoader.instance().parseShowcaseEcho(); + this.context = TestUtils.parseShowcaseEcho(); } @Test diff --git a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/TestUtils.java b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/TestUtils.java new file mode 100644 index 0000000000..d2bf205bee --- /dev/null +++ b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/TestUtils.java @@ -0,0 +1,102 @@ +/* + * Copyright 2024 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 + * + * https://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.cloud.generator.spring; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +import com.google.api.Service; +import com.google.api.generator.gapic.model.GapicContext; +import com.google.api.generator.gapic.model.GapicServiceConfig; +import com.google.api.generator.gapic.model.Message; +import com.google.api.generator.gapic.model.ResourceName; +import com.google.api.generator.gapic.model.Transport; +import com.google.api.generator.gapic.protoparser.Parser; +import com.google.api.generator.gapic.protoparser.ServiceConfigParser; +import com.google.api.generator.gapic.protoparser.ServiceYamlParser; +import com.google.protobuf.Descriptors.FileDescriptor; +import com.google.protobuf.Descriptors.ServiceDescriptor; +import com.google.showcase.v1beta1.EchoOuterClass; +import java.nio.file.Path; +import java.nio.file.Paths; +import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Set; +import java.util.stream.Collectors; + +public class TestUtils { + + private static final String TEST_FILES_DIRECTORY = "src/test/resources/"; + private static final String ECHO_SERVICE_DESCRIPTION = + "This service is used showcase the four main types of rpcs - unary, server\n" + + " side streaming, client side streaming, and bidirectional streaming. This\n" + + " service also exposes methods that explicitly implement server delay, and\n" + + " paginated calls. Set the 'showcase-trailer' metadata key on any method\n" + + " to have the values echoed in the response trailers."; + + /** + * Temporary workaround that copies the implementation of + * {@link com.google.api.generator.test.protoloader.TestProtoLoader} {@code parseShowcaseEcho()} + * This is due to the resources not being available in the test-jar exported by + * gapic-generator-java We could in theory create a TestProtoLoader instance with a custom test + * resources path but the constructor is protected + * + * @return The {@link GapicContext} of the Showcase Echo service + */ + public static GapicContext parseShowcaseEcho() { + FileDescriptor echoFileDescriptor = EchoOuterClass.getDescriptor(); + ServiceDescriptor echoServiceDescriptor = echoFileDescriptor.getServices().get(0); + assertEquals(echoServiceDescriptor.getName(), "Echo"); + + String serviceYamlFilename = "echo_v1beta1.yaml"; + Path serviceYamlPath = Paths.get(TEST_FILES_DIRECTORY, serviceYamlFilename); + Optional serviceYamlOpt = + ServiceYamlParser.parse(serviceYamlPath.toString()); + assertTrue(serviceYamlOpt.isPresent()); + + Map messageTypes = Parser.parseMessages(echoFileDescriptor); + Map resourceNames = Parser.parseResourceNames(echoFileDescriptor); + Set outputResourceNames = new HashSet<>(); + List services = + Parser.parseService( + echoFileDescriptor, messageTypes, resourceNames, serviceYamlOpt, outputResourceNames); + + // Explicitly adds service description, since this is not parsed from source code location + // in test protos, as it would from a protoc CodeGeneratorRequest + List servicesWithDescription = + services.stream() + .map(s -> s.toBuilder().setDescription(ECHO_SERVICE_DESCRIPTION).build()) + .collect(Collectors.toList()); + + String jsonFilename = "showcase_grpc_service_config.json"; + Path jsonPath = Paths.get(TEST_FILES_DIRECTORY, jsonFilename); + Optional configOpt = ServiceConfigParser.parse(jsonPath.toString()); + assertTrue(configOpt.isPresent()); + GapicServiceConfig config = configOpt.get(); + + return GapicContext.builder() + .setMessages(messageTypes) + .setResourceNames(resourceNames) + .setServices(servicesWithDescription) + .setServiceConfig(config) + .setHelperResourceNames(outputResourceNames) + .setTransport(Transport.GRPC) + .build(); + } +} diff --git a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringAutoConfigClassComposerTest.java b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringAutoConfigClassComposerTest.java index da2d68ff62..042b92fed0 100644 --- a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringAutoConfigClassComposerTest.java +++ b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringAutoConfigClassComposerTest.java @@ -24,7 +24,7 @@ import com.google.api.generator.gapic.model.Transport; import com.google.api.generator.test.framework.Assert; import com.google.api.generator.test.protoloader.GrpcRestTestProtoLoader; -import com.google.api.generator.test.protoloader.TestProtoLoader; +import com.google.cloud.generator.spring.TestUtils; import org.junit.Before; import org.junit.Test; @@ -41,7 +41,7 @@ public class SpringAutoConfigClassComposerTest { @Before public void setUp() { - this.echoContext = TestProtoLoader.instance().parseShowcaseEcho(); + this.echoContext = TestUtils.parseShowcaseEcho(); this.echoProtoService = this.echoContext.services().get(0); this.echoGrpcRestContext = this.echoContext.toBuilder().setTransport(Transport.GRPC_REST).build(); this.echoGrpcRestProtoService = this.echoGrpcRestContext.services().get(0); diff --git a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringComposerTest.java b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringComposerTest.java index 8633a598df..7879a43de8 100644 --- a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringComposerTest.java +++ b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringComposerTest.java @@ -22,7 +22,7 @@ import com.google.api.generator.gapic.model.GapicPackageInfo; import com.google.api.generator.test.framework.Assert; import com.google.api.generator.test.framework.GoldenFileWriter; -import com.google.api.generator.test.protoloader.TestProtoLoader; +import com.google.cloud.generator.spring.TestUtils; import java.nio.file.Path; import java.nio.file.Paths; import java.util.List; @@ -34,7 +34,7 @@ public class SpringComposerTest { @Before public void setUp() { - this.context = TestProtoLoader.instance().parseShowcaseEcho(); + this.context = TestUtils.parseShowcaseEcho(); } @Test diff --git a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringPackageInfoComposerTest.java b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringPackageInfoComposerTest.java index b8daebf70d..c6083c3c72 100644 --- a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringPackageInfoComposerTest.java +++ b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringPackageInfoComposerTest.java @@ -21,7 +21,7 @@ import com.google.api.generator.gapic.model.GapicPackageInfo; import com.google.api.generator.test.framework.Assert; import com.google.api.generator.test.framework.GoldenFileWriter; -import com.google.api.generator.test.protoloader.TestProtoLoader; +import com.google.cloud.generator.spring.TestUtils; import java.nio.file.Path; import java.nio.file.Paths; import org.junit.Before; @@ -32,7 +32,7 @@ public class SpringPackageInfoComposerTest { @Before public void setUp() { - this.context = TestProtoLoader.instance().parseShowcaseEcho(); + this.context = TestUtils.parseShowcaseEcho(); } @Test diff --git a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringPropertiesClassComposerTest.java b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringPropertiesClassComposerTest.java index 0dea711e8f..85c6ef4a47 100644 --- a/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringPropertiesClassComposerTest.java +++ b/spring-cloud-generator/src/test/java/com/google/cloud/generator/spring/composer/SpringPropertiesClassComposerTest.java @@ -24,7 +24,7 @@ import com.google.api.generator.gapic.model.Transport; import com.google.api.generator.test.framework.Assert; import com.google.api.generator.test.protoloader.GrpcRestTestProtoLoader; -import com.google.api.generator.test.protoloader.TestProtoLoader; +import com.google.cloud.generator.spring.TestUtils; import org.junit.Before; import org.junit.Test; @@ -40,7 +40,7 @@ public class SpringPropertiesClassComposerTest { @Before public void setUp() { - this.echoContext = TestProtoLoader.instance().parseShowcaseEcho(); + this.echoContext = TestUtils.parseShowcaseEcho(); this.echoProtoService = this.echoContext.services().get(0); this.echoGrpcRestContext = this.echoContext.toBuilder().setTransport(Transport.GRPC_REST).build(); this.echoGrpcRestProtoService = this.echoGrpcRestContext.services().get(0); diff --git a/spring-cloud-generator/src/test/resources/echo_v1beta1.yaml b/spring-cloud-generator/src/test/resources/echo_v1beta1.yaml new file mode 100644 index 0000000000..a6aea48e87 --- /dev/null +++ b/spring-cloud-generator/src/test/resources/echo_v1beta1.yaml @@ -0,0 +1,106 @@ +# 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 +# +# https://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. + +type: google.api.Service +config_version: 3 +name: showcase.googleapis.com +title: Client Libraries Showcase API + +apis: + - name: google.showcase.v1beta1.Compliance + - name: google.showcase.v1beta1.Echo + - name: google.showcase.v1beta1.Identity + - name: google.showcase.v1beta1.Messaging + - name: google.showcase.v1beta1.SequenceService + - name: google.showcase.v1beta1.Testing + # Mix-in services + - name: 'google.cloud.location.Locations' + - name: 'google.iam.v1.IAMPolicy' + - name: 'google.longrunning.Operations' + +documentation: + summary: |- + Showcase represents both a model API and an integration testing surface for + client library generator consumption. + +backend: + rules: + - selector: 'google.cloud.location.Locations.*' + deadline: 60.0 + - selector: 'google.iam.v1.IAMPolicy.*' + deadline: 60.0 + - selector: 'google.longrunning.Operations.*' + deadline: 60.0 + +http: + rules: + - selector: google.cloud.location.Locations.ListLocations + get: '/v1beta1/{name=projects/*}/locations' + - selector: google.cloud.location.Locations.GetLocation + get: '/v1beta1/{name=projects/*/locations/*}' + - selector: google.iam.v1.IAMPolicy.SetIamPolicy + post: '/v1beta1/{resource=users/*}:setIamPolicy' + body: '*' + additional_bindings: + - post: '/v1beta1/{resource=rooms/*}:setIamPolicy' + body: '*' + - post: '/v1beta1/{resource=rooms/*/blurbs/*}:setIamPolicy' + body: '*' + - post: '/v1beta1/{resource=sequences/*}:setIamPolicy' + body: '*' + - selector: google.iam.v1.IAMPolicy.GetIamPolicy + get: '/v1beta1/{resource=users/*}:getIamPolicy' + additional_bindings: + - get: '/v1beta1/{resource=rooms/*}:getIamPolicy' + - get: '/v1beta1/{resource=rooms/*/blurbs/*}:getIamPolicy' + - get: '/v1beta1/{resource=sequences/*}:getIamPolicy' + - selector: google.iam.v1.IAMPolicy.TestIamPermissions + post: '/v1beta1/{resource=users/*}:testIamPermissions' + body: '*' + additional_bindings: + - post: '/v1beta1/{resource=rooms/*}:testIamPermissions' + body: '*' + - post: '/v1beta1/{resource=rooms/*/blurbs/*}:testIamPermissions' + body: '*' + - post: '/v1beta1/{resource=sequences/*}:testIamPermissions' + body: '*' + - selector: google.longrunning.Operations.ListOperations + get: '/v1beta1/operations' + additional_bindings: + - get: '/v1beta2/operations' + - get: '/v1beta3/operations' + - selector: google.longrunning.Operations.GetOperation + get: '/v1beta1/{name=operations/**}' + additional_bindings: + - get: '/v1beta2/{name=operations/**}' + - get: '/v1beta3/{name=operations/**}' + - selector: google.longrunning.Operations.DeleteOperation + delete: '/v1beta1/{name=operations/**}' + additional_bindings: + - delete: '/v1beta2/{name=operations/**}' + - delete: '/v1beta3/{name=operations/**}' + - selector: google.longrunning.Operations.CancelOperation + post: '/v1beta1/{name=operations/**}:cancel' + additional_bindings: + - post: '/v1beta2/{name=operations/**}:cancel' + - post: '/v1beta3/{name=operations/**}:cancel' +publishing: + method_settings: + - selector: google.showcase.v1beta1.Echo.Echo + auto_populated_fields: + - request_id + - second_request_id + - third_request_id + - fourth_request_id + - non_existent_field \ No newline at end of file diff --git a/spring-cloud-generator/src/test/resources/showcase_grpc_service_config.json b/spring-cloud-generator/src/test/resources/showcase_grpc_service_config.json new file mode 100644 index 0000000000..ea16b8994b --- /dev/null +++ b/spring-cloud-generator/src/test/resources/showcase_grpc_service_config.json @@ -0,0 +1,85 @@ +{ + "methodConfig": [ + { + "name": [ + {"service": "google.showcase.v1beta1.Echo"}, + {"service": "google.showcase.v1beta1.Messaging"} + ], + "timeout": "5s" + }, + { + "name": [ + { + "service": "google.showcase.v1beta1.Echo", + "method": "Echo" + }, + { + "service": "google.showcase.v1beta1.Echo", + "method": "Expand" + }, + { + "service": "google.showcase.v1beta1.Echo", + "method": "PagedExpand" + }, + { + "service": "google.showcase.v1beta1.Messaging", + "method": "GetRoom" + }, + { + "service": "google.showcase.v1beta1.Messaging", + "method": "ListRooms" + }, + { + "service": "google.showcase.v1beta1.Messaging", + "method": "GetBlurb" + }, + { + "service": "google.showcase.v1beta1.Messaging", + "method": "ListBlurbs" + }, + { + "service": "google.showcase.v1beta1.Messaging", + "method": "SearchBlurbs" + }, + { + "service": "google.showcase.v1beta1.Messaging", + "method": "Connect" + } + ], + "retryPolicy": { + "maxAttempts": 3, + "maxBackoff": "3s", + "initialBackoff": "0.1s", + "backoffMultiplier": 2, + "retryableStatusCodes": [ + "UNAVAILABLE", + "UNKNOWN" + ] + }, + "timeout": "10s" + }, + { + "name": [ + { + "service": "google.showcase.v1beta1.Identity", + "method": "GetUser" + }, + { + "service": "google.showcase.v1beta1.Identity", + "method": "ListUsers" + } + ], + "retryPolicy": { + "maxAttempts": 5, + "maxBackoff": "3s", + "initialBackoff": "0.2s", + "backoffMultiplier": 2, + "retryableStatusCodes": [ + "UNAVAILABLE", + "UNKNOWN" + ] + }, + "timeout": "5s" + } + ] +}