diff --git a/.github/workflows/sonar.yaml b/.github/workflows/sonar.yaml
index 07c4a188e1..349ac5011a 100644
--- a/.github/workflows/sonar.yaml
+++ b/.github/workflows/sonar.yaml
@@ -1,6 +1,6 @@
name: SonarCloud Build
env:
- SHOWCASE_VERSION: 0.25.0
+ SHOWCASE_VERSION: 0.26.0
on:
push:
branches:
diff --git a/.gitignore b/.gitignore
index 585e94f980..07604728ff 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,4 +15,4 @@ target/
# Vscode Settings
.vscode/settings.json
-*.iml
+*.iml
\ No newline at end of file
diff --git a/showcase/gapic-showcase/pom.xml b/showcase/gapic-showcase/pom.xml
index 96b4045ebd..016885ebef 100644
--- a/showcase/gapic-showcase/pom.xml
+++ b/showcase/gapic-showcase/pom.xml
@@ -18,6 +18,10 @@
0.0.1-SNAPSHOT
+
+ 0.26.0
+
+
enable-golden-tests
@@ -106,6 +110,28 @@
(IT.*\.java)|(.*Test.java)
+
+ com.googlecode.maven-download-plugin
+ download-maven-plugin
+ 1.6.8
+
+
+ download-compliance-suite
+ generate-test-resources
+
+ wget
+
+
+
+ https://raw.githubusercontent.com/googleapis/gapic-showcase/v${gapic-showcase.version}/server/services/compliance_suite.json
+
+ src/test/resources
+
+ true
+
+
+
+
diff --git a/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITHttpAnnotation.java b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITHttpAnnotation.java
new file mode 100644
index 0000000000..79431fbc68
--- /dev/null
+++ b/showcase/gapic-showcase/src/test/java/com/google/showcase/v1beta1/it/ITHttpAnnotation.java
@@ -0,0 +1,141 @@
+/*
+ * Copyright 2023 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.showcase.v1beta1.it;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import com.google.api.client.http.javanet.NetHttpTransport;
+import com.google.api.gax.core.NoCredentialsProvider;
+import com.google.common.collect.ImmutableMap;
+import com.google.protobuf.util.JsonFormat;
+import com.google.showcase.v1beta1.ComplianceClient;
+import com.google.showcase.v1beta1.ComplianceData;
+import com.google.showcase.v1beta1.ComplianceGroup;
+import com.google.showcase.v1beta1.ComplianceSettings;
+import com.google.showcase.v1beta1.ComplianceSuite;
+import com.google.showcase.v1beta1.RepeatRequest;
+import com.google.showcase.v1beta1.RepeatResponse;
+import java.io.IOException;
+import java.io.InputStreamReader;
+import java.security.GeneralSecurityException;
+import java.util.List;
+import java.util.Map;
+import java.util.Optional;
+import java.util.function.Function;
+import java.util.stream.Collectors;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.junit.runners.Parameterized;
+
+// This test runs from the parameters in the compliance_suite.json file
+// The file is downloaded from the gapic-showcase repo. Each compliance
+// group is a set of HttpJson behaviors we want to test for. Each group
+// tests the product of the rpc list and requests list.
+@RunWith(Parameterized.class)
+public class ITHttpAnnotation {
+
+ @Parameterized.Parameters(name = "Compliance Group Name: {0}")
+ public static String[] data() {
+ return new String[] {
+ "Fully working conversions, resources",
+ "Binding selection testing",
+ "Cases that apply to non-path requests",
+ "Fully working conversions, no resources"
+ };
+ }
+
+ @Parameterized.Parameter(0)
+ public String groupName;
+
+ private ComplianceSuite complianceSuite;
+ private ComplianceClient complianceClient;
+ private Map> validComplianceRpcMap;
+
+ @Before
+ public void createClient() throws IOException, GeneralSecurityException {
+ ComplianceSuite.Builder builder = ComplianceSuite.newBuilder();
+ JsonFormat.parser()
+ .merge(
+ new InputStreamReader(
+ ITHttpAnnotation.class
+ .getClassLoader()
+ .getResourceAsStream("compliance_suite.json")),
+ builder);
+ complianceSuite = builder.build();
+
+ ComplianceSettings httpjsonComplianceSettings =
+ ComplianceSettings.newHttpJsonBuilder()
+ .setCredentialsProvider(NoCredentialsProvider.create())
+ .setTransportChannelProvider(
+ ComplianceSettings.defaultHttpJsonTransportProviderBuilder()
+ .setHttpTransport(
+ new NetHttpTransport.Builder().doNotValidateCertificate().build())
+ .setEndpoint("http://localhost:7469")
+ .build())
+ .build();
+ complianceClient = ComplianceClient.create(httpjsonComplianceSettings);
+
+ // Mapping of Compliance Suite file RPC Names to ComplianceClient methods
+ validComplianceRpcMap =
+ ImmutableMap.of(
+ "Compliance.RepeatDataBody",
+ complianceClient::repeatDataBody,
+ "Compliance.RepeatDataBodyInfo",
+ complianceClient::repeatDataBodyInfo,
+ "Compliance.RepeatDataQuery",
+ complianceClient::repeatDataQuery,
+ "Compliance.RepeatDataSimplePath",
+ complianceClient::repeatDataSimplePath,
+ "Compliance.RepeatDataBodyPut",
+ complianceClient::repeatDataBodyPut,
+ "Compliance.RepeatDataBodyPatch",
+ complianceClient::repeatDataBodyPatch,
+ "Compliance.RepeatDataPathResource",
+ complianceClient::repeatDataPathResource);
+ }
+
+ @After
+ public void destroyClient() {
+ complianceClient.close();
+ }
+
+ // Verify that the input's info is the same as the response's info
+ // This ensures that the entire group's behavior over HttpJson
+ // works as intended
+ @Test
+ public void testComplianceGroup() {
+ Optional complianceGroupOptional =
+ complianceSuite.getGroupList().stream()
+ .filter(x -> x.getName().equals(groupName))
+ .findFirst();
+ assertThat(complianceGroupOptional.isPresent()).isTrue();
+ ComplianceGroup complianceGroup = complianceGroupOptional.get();
+ List validRpcList =
+ complianceGroup.getRpcsList().stream()
+ .filter(validComplianceRpcMap::containsKey)
+ .collect(Collectors.toList());
+ for (String rpcName : validRpcList) {
+ Function rpc = validComplianceRpcMap.get(rpcName);
+ for (RepeatRequest repeatRequest : complianceGroup.getRequestsList()) {
+ ComplianceData expectedData = repeatRequest.getInfo();
+ RepeatResponse response = rpc.apply(repeatRequest);
+ assertThat(response.getRequest().getInfo()).isEqualTo(expectedData);
+ }
+ }
+ }
+}
diff --git a/showcase/gapic-showcase/src/test/resources/META-INF/native-image/resource-config.json b/showcase/gapic-showcase/src/test/resources/META-INF/native-image/resource-config.json
new file mode 100644
index 0000000000..80d7bc0ea2
--- /dev/null
+++ b/showcase/gapic-showcase/src/test/resources/META-INF/native-image/resource-config.json
@@ -0,0 +1,3 @@
+{
+ "resources":[{"pattern": ".*.json"}]
+}
\ No newline at end of file
diff --git a/showcase/gapic-showcase/src/test/resources/compliance_suite.json b/showcase/gapic-showcase/src/test/resources/compliance_suite.json
new file mode 100644
index 0000000000..a458819b35
--- /dev/null
+++ b/showcase/gapic-showcase/src/test/resources/compliance_suite.json
@@ -0,0 +1,240 @@
+{
+ "group": [
+ {
+ "name": "Fully working conversions, no resources",
+ "rpcs": ["Compliance.RepeatDataBody", "Compliance.RepeatDataBodyPut", "Compliance.RepeatDataBodyPatch", "Compliance.RepeatDataQuery", "Compliance.RepeatDataSimplePath", "Compliance.RepeatDataBodyInfo"],
+ "requests": [
+ {
+ "name": "Basic data types",
+ "serverVerify": true,
+ "info": {
+ "fString": "Hello",
+ "fInt32": -1,
+ "fSint32" : -2,
+ "fSfixed32": -3,
+ "fUint32": 5,
+ "fFixed32": 7,
+ "fInt64": -11,
+ "fSint64": -13,
+ "fSfixed64": -17,
+ "fUint64": 19,
+ "fFixed64":23,
+ "fDouble": -29e4,
+ "fFloat": -31,
+ "fBool": true,
+ "fKingdom": "ANIMALIA",
+
+ "pString": "Goodbye",
+ "pInt32": -37,
+ "pDouble": -41.43,
+ "pBool": true,
+ "pKingdom": "PLANTAE",
+
+ "fChild": {
+ "fString": "second/bool/salutation"
+ }
+ },
+ "fInt32": -10,
+ "fInt64": -110,
+ "fDouble": -54e4,
+
+ "pInt32": -47,
+ "pInt64": -477,
+ "pDouble": -61.73
+ },
+ {
+ "name": "Basic types, no optional fields",
+ "serverVerify": true,
+ "info": {
+ "fString": "Hello",
+ "fInt32": -1,
+ "fSint32" : -2,
+ "fSfixed32": -3,
+ "fUint32": 5,
+ "fFixed32": 7,
+ "fInt64": -11,
+ "fSint64": -13,
+ "fSfixed64": -17,
+ "fUint64": 19,
+ "fFixed64":23,
+ "fDouble": -29e4,
+ "fFloat": -31,
+ "fBool": true,
+ "fKingdom": "ANIMALIA",
+
+ "fChild": {
+ "fString": "second/bool/salutation"
+ }
+ }
+ },
+
+ {
+ "name": "Zero values for non-string fields",
+ "serverVerify": true,
+ "info": {
+ "fString": "Hello",
+ "fInt32": 0,
+ "fSint32" : 0,
+ "fSfixed32": 0,
+ "fUint32": 0,
+ "fFixed32": 0,
+ "fInt64": 0,
+ "fSint64": 0,
+ "fSfixed64": 0,
+ "fUint64": 0,
+ "fFixed64": 0,
+ "fDouble": 0,
+ "fFloat": 0,
+ "fBool": false,
+ "fKingdom": "LIFE_KINGDOM_UNSPECIFIED",
+
+ "pString": "Goodbye",
+ "pInt32": 0,
+ "pDouble": 0,
+ "pBool": false,
+ "pKingdom": "LIFE_KINGDOM_UNSPECIFIED"
+ }
+ },
+ {
+ "name": "Extreme values",
+ "serverVerify": true,
+ "info": {
+ "fString": "non-ASCII+non-printable string ☺ → ← \"\\\/\b\f\r\t\u1234 works, not newlines yet",
+ "fInt32": 2147483647,
+ "fSint32" : 2147483647,
+ "fSfixed32": 2147483647,
+ "fUint32": 4294967295,
+ "fFixed32": 4294967295,
+ "fInt64": "9223372036854775807",
+ "fSint64": "9223372036854775807",
+ "fSfixed64": "9223372036854775807",
+ "fUint64": "18446744073709551615",
+ "fFixed64": "18446744073709551615",
+ "fDouble": 1.797693134862315708145274237317043567981e+308,
+ "fFloat": 3.40282346638528859811704183484516925440e+38,
+ "fBool": false,
+
+ "pString": "Goodbye",
+ "pInt32": 2147483647,
+ "pDouble": 1.797693134862315708145274237317043567981e+308,
+ "pBool": false
+ }
+ },
+ {
+ "name": "Strings with spaces",
+ "serverVerify": true,
+ "info": {
+ "fString": "Hello there"
+ }
+ },
+ {
+ "name": "Strings with quotes",
+ "serverVerify": true,
+ "info": {
+ "fString": "Hello \"You\""
+ }
+ },
+ {
+ "name": "Strings with percents",
+ "serverVerify": true,
+ "info": {
+ "fString": "Hello 100%"
+ }
+ }
+ ]
+ },
+ {
+ "name": "Fully working conversions, resources",
+ "rpcs": ["Compliance.RepeatDataBody", "Compliance.RepeatDataBodyPut", "Compliance.RepeatDataBodyPatch", "Compliance.RepeatDataQuery"],
+ "requests": [
+ {
+ "name": "Strings with slashes and values that resemble subsequent resource templates",
+ "serverVerify": true,
+ "info": {
+ "fString": "first/hello/second/greetings",
+ "pBool": true,
+
+ "fChild": {
+ "fString": "second/zzz/bool/true"
+ }
+ }
+ }
+ ]
+ },
+ {
+ "name": "Binding selection testing",
+ "rpcs": ["Compliance.RepeatDataPathResource"],
+ "requests": [
+ {
+ "name": "Binding testing baseline no Uri verification",
+ "serverVerify": true,
+ "info": {
+ "fString": "first/hello",
+ "pBool": true,
+
+ "fChild": {
+ "fString": "second/greetings"
+ }
+ }
+ },
+ {
+ "name": "Binding testing first binding",
+ "serverVerify": true,
+ "info": {
+ "fString": "first/hello",
+ "pBool": true,
+
+ "fChild": {
+ "fString": "second/greetings"
+ }
+ },
+ "intendedBindingUri": "/v1beta1/repeat/{info.f_string=first/*}/{info.f_child.f_string=second/*}/bool/{info.f_bool}:pathresource"
+ },
+ {
+ "name": "Binding testing additional binding",
+ "serverVerify": true,
+ "info": {
+ "fString": "second/greetings",
+ "pBool": true,
+
+ "fChild": {
+ "fString": "first/hello"
+ }
+ },
+ "intendedBindingUri": "/v1beta1/repeat/{info.f_child.f_string=first/*}/{info.f_string=second/*}/bool/{info.f_bool}:childfirstpathresource"
+ }
+ ]
+ },
+ {
+ "name": "Cases that apply to non-path requests",
+ "rpcs": ["Compliance.RepeatDataBody", "Compliance.RepeatDataBodyPut", "Compliance.RepeatDataBodyPatch", "Compliance.RepeatDataQuery"],
+ "requests": [
+ {
+ "name": "Zero values for all fields",
+ "serverVerify": true,
+ "info": {
+ "fString": "",
+ "fInt32": 0,
+ "fSint32" : 0,
+ "fSfixed32": 0,
+ "fUint32": 0,
+ "fFixed32": 0,
+ "fInt64": 0,
+ "fSint64": 0,
+ "fSfixed64": 0,
+ "fUint64": 0,
+ "fFixed64":20,
+ "fDouble": 0,
+ "fFloat": 0,
+ "fBool": false,
+
+ "pString": "",
+ "pInt32": 0,
+ "pDouble": 0,
+ "pBool": false
+ }
+ }
+ ]
+ }
+ ]
+}
diff --git a/showcase/scripts/verify.sh b/showcase/scripts/verify.sh
index 41b72565c7..48026e9600 100755
--- a/showcase/scripts/verify.sh
+++ b/showcase/scripts/verify.sh
@@ -57,6 +57,6 @@ case $1 in
unzip -q -c "$BAZEL_ROOT/$GAPIC_JAR" temp-codegen.srcjar | jar x
delete_unneeded
- diff -ru "$SHOWCASE_DIR/$GAPIC_PROJECT_DIR"/src "$GAPIC_UNPACK_DIR"/src --exclude=it
+ diff -ru "$SHOWCASE_DIR/$GAPIC_PROJECT_DIR"/src "$GAPIC_UNPACK_DIR"/src --exclude=it --exclude=resources
;;
esac