From 85bb35df22a37d29e500992b80d634e49f746cd0 Mon Sep 17 00:00:00 2001 From: vam-google Date: Thu, 23 Jul 2020 23:29:11 -0700 Subject: [PATCH 01/16] feat: Implement rest-gapic http transport --- build.gradle | 2 + gax-httpjson/build.gradle | 2 + .../api/gax/httpjson/FieldsExtractor.java | 34 ++++++ .../api/gax/httpjson/HttpRequestRunnable.java | 18 ++- .../ProtoMessageRequestFormatter.java | 111 ++++++++++++++++++ .../httpjson/ProtoMessageResponseParser.java | 72 ++++++++++++ .../api/gax/httpjson/ProtoRestSerializer.java | 105 +++++++++++++++++ 7 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java create mode 100644 gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java create mode 100644 gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java create mode 100644 gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java diff --git a/build.gradle b/build.gradle index 2aed70953..22cfa1e3b 100644 --- a/build.gradle +++ b/build.gradle @@ -132,6 +132,8 @@ subprojects { 'maven.io_grpc_grpc_protobuf': "io.grpc:grpc-protobuf:${libraries['version.io_grpc']}", 'maven.io_grpc_grpc_netty_shaded': "io.grpc:grpc-netty-shaded:${libraries['version.io_grpc']}", 'maven.io_grpc_grpc_alts': "io.grpc:grpc-alts:${libraries['version.io_grpc']}", + 'maven.com_google_protobuf': "com.google.protobuf:protobuf-java:${libraries['version.com_google_protobuf']}", + 'maven.com_google_protobuf_java_util': "com.google.protobuf:protobuf-java-util:${libraries['version.com_google_protobuf']}" ]) } diff --git a/gax-httpjson/build.gradle b/gax-httpjson/build.gradle index 8a05a7415..b7619c865 100644 --- a/gax-httpjson/build.gradle +++ b/gax-httpjson/build.gradle @@ -5,6 +5,8 @@ project.version = "0.75.3-SNAPSHOT" // {x-version-update:gax-httpjson:current} dependencies { compile project(':gax'), + libraries['maven.com_google_protobuf'], + libraries['maven.com_google_protobuf_java_util'], libraries['maven.com_google_code_gson_gson'], libraries['maven.com_google_guava_guava'], libraries['maven.com_google_code_findbugs_jsr305'], diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java new file mode 100644 index 000000000..8acd66469 --- /dev/null +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java @@ -0,0 +1,34 @@ +/* + * Copyright 2020 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.api.gax.httpjson; + +public interface FieldsExtractor { + ParamsT extract(RequestT request); +} diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java index 1c449dca5..5506c5fe2 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java @@ -103,7 +103,8 @@ HttpRequest createHttpRequest() throws IOException { } // Populate URL path and query parameters. - GenericUrl url = new GenericUrl(getEndpoint() + requestFormatter.getPath(getRequest())); + String endpoint = normalizeEndpoint(getEndpoint()); + GenericUrl url = new GenericUrl(endpoint + requestFormatter.getPath(getRequest())); Map> queryParams = requestFormatter.getQueryParamNames(getRequest()); for (Entry> queryParam : queryParams.entrySet()) { if (queryParam.getValue() != null) { @@ -120,6 +121,21 @@ HttpRequest createHttpRequest() throws IOException { return httpRequest; } + // This will be frequently executed, so avoiding using regexps if not necessary. + private String normalizeEndpoint(String endpoint) { + String normalized = endpoint; + // Set protocol as https by default if not set explicitly + if (!normalized.contains("://")) { + normalized = "https://" + normalized; + } + + if (normalized.charAt(normalized.length() - 1) != '/') { + normalized += '/'; + } + + return normalized; + } + @Override public void run() { try { diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java new file mode 100644 index 000000000..55c4e935e --- /dev/null +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java @@ -0,0 +1,111 @@ +/* + * Copyright 2020 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.api.gax.httpjson; + +import com.google.api.pathtemplate.PathTemplate; +import com.google.protobuf.Message; +import java.util.List; +import java.util.Map; + +public class ProtoMessageRequestFormatter + implements HttpRequestFormatter { + + private final FieldsExtractor requestBodyExtractor; + private final FieldsExtractor>> queryParamsExtractor; + private final PathTemplate pathTemplate; + private final FieldsExtractor> pathVarsExtractor; + + public ProtoMessageRequestFormatter( + FieldsExtractor requestBodyExtractor, + FieldsExtractor>> queryParamsExtractor, + PathTemplate pathTemplate, + FieldsExtractor> pathVarsExtractor) { + this.requestBodyExtractor = requestBodyExtractor; + this.queryParamsExtractor = queryParamsExtractor; + this.pathTemplate = pathTemplate; + this.pathVarsExtractor = pathVarsExtractor; + } + + public static + ProtoMessageRequestFormatter.Builder newBuilder() { + return new Builder<>(); + } + + @Override + public Map> getQueryParamNames(RequestT apiMessage) { + return queryParamsExtractor.extract(apiMessage); + } + + @Override + public String getRequestBody(RequestT apiMessage) { + return requestBodyExtractor.extract(apiMessage); + } + + @Override + public String getPath(RequestT apiMessage) { + return pathTemplate.instantiate(pathVarsExtractor.extract(apiMessage)); + } + + @Override + public PathTemplate getPathTemplate() { + return pathTemplate; + } + + public static class Builder { + private FieldsExtractor requestBodyExtractor; + private FieldsExtractor>> queryParamsExtractor; + private String path; + private FieldsExtractor> pathVarsExtractor; + + public Builder setRequestBodyExtractor( + FieldsExtractor requestBodyExtractor) { + this.requestBodyExtractor = requestBodyExtractor; + return this; + } + + public Builder setQueryParamsExtractor( + FieldsExtractor>> queryParamsExtractor) { + this.queryParamsExtractor = queryParamsExtractor; + return this; + } + + public Builder setPath( + String path, FieldsExtractor> pathVarsExtractor) { + this.path = path; + this.pathVarsExtractor = pathVarsExtractor; + return this; + } + + public ProtoMessageRequestFormatter build() { + return new ProtoMessageRequestFormatter<>( + requestBodyExtractor, queryParamsExtractor, PathTemplate.create(path), pathVarsExtractor); + } + } +} diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java new file mode 100644 index 000000000..94f15f009 --- /dev/null +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java @@ -0,0 +1,72 @@ +/* + * Copyright 2020 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.api.gax.httpjson; + +import com.google.protobuf.Message; +import java.io.InputStream; + +public class ProtoMessageResponseParser + implements HttpResponseParser { + + private final ResponseT defaultInstance; + + private ProtoMessageResponseParser(ResponseT defaultInstance) { + this.defaultInstance = defaultInstance; + } + + public static + ProtoMessageResponseParser.Builder newBuilder() { + return new ProtoMessageResponseParser.Builder<>(); + } + + @Override + public ResponseT parse(InputStream httpContent) { + return ProtoRestSerializer.create() + .fromJson(httpContent, defaultInstance.newBuilderForType()); + } + + @Override + public String serialize(ResponseT response) { + return ProtoRestSerializer.create().toJson(response); + } + + public static class Builder { + private ResponseT defaultInstance; + + public Builder setDefaultInstance(ResponseT defaultInstance) { + this.defaultInstance = defaultInstance; + return this; + } + + public ProtoMessageResponseParser build() { + return new ProtoMessageResponseParser<>(defaultInstance); + } + } +} diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java new file mode 100644 index 000000000..c051840e8 --- /dev/null +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -0,0 +1,105 @@ +/* + * Copyright 2020 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.api.gax.httpjson; + +import com.google.common.collect.ImmutableList; +import com.google.protobuf.InvalidProtocolBufferException; +import com.google.protobuf.Message; +import com.google.protobuf.util.JsonFormat; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.util.List; +import java.util.Map; + +public class ProtoRestSerializer { + private ProtoRestSerializer() {} + + public static ProtoRestSerializer create() { + return new ProtoRestSerializer<>(); + } + + public String toJson(RequestT message) { + try { + return JsonFormat.printer().print(message); + } catch (InvalidProtocolBufferException e) { + throw new RuntimeException("Failed to serialize message to JSON", e); + } + } + + @SuppressWarnings("unchecked") + public RequestT fromJson(InputStream message, RequestT.Builder builder) { + try (Reader json = new InputStreamReader(message)) { + JsonFormat.parser().ignoringUnknownFields().merge(json, builder); + return (RequestT) builder.build(); + } catch (IOException e) { + throw new RuntimeException("Failed to perse response message", e); + } + } + + public void toPathParam(Map fields, String fieldName, Object fieldValue) { + if (isDefaultValue(fieldName, fieldValue)) { + return; + } + fields.put(fieldName, String.valueOf(fieldValue)); + } + + public void toQueryParam(Map> fields, String fieldName, Object fieldValue) { + if (isDefaultValue(fieldName, fieldValue)) { + return; + } + + ImmutableList.Builder paramValueList = ImmutableList.builder(); + if (fieldValue instanceof List) { + for (Object fieldValueItem : (List) fieldValue) { + paramValueList.add(String.valueOf(fieldValueItem)); + } + } else { + paramValueList.add(String.valueOf(fieldValue)); + } + + fields.put(fieldName, paramValueList.build()); + } + + public String toBody(String fieldName, RequestT fieldValue) { + return toJson(fieldValue); + } + + private boolean isDefaultValue(String fieldName, Object fieldValue) { + if (fieldValue instanceof Number) { + return ((Number) fieldValue).longValue() == 0L; + } else if (fieldValue instanceof String) { + return ((String) fieldValue).isEmpty(); + } + + return false; + } +} From d6c282999209553a4e7933173a1dc1f2ae868532 Mon Sep 17 00:00:00 2001 From: vam-google Date: Fri, 24 Jul 2020 00:24:22 -0700 Subject: [PATCH 02/16] fix: fix bazel build --- gax-httpjson/BUILD.bazel | 2 ++ .../google/api/gax/httpjson/ProtoRestSerializer.java | 2 +- repositories.bzl | 12 ++++++++++-- 3 files changed, 13 insertions(+), 3 deletions(-) diff --git a/gax-httpjson/BUILD.bazel b/gax-httpjson/BUILD.bazel index e53beacd4..619f50f20 100644 --- a/gax-httpjson/BUILD.bazel +++ b/gax-httpjson/BUILD.bazel @@ -19,6 +19,8 @@ _COMPILE_DEPS = [ "@com_google_auto_value_auto_value//jar", "@com_google_http_client_google_http_client_jackson2//jar", "@javax_annotation_javax_annotation_api//jar", + "@com_google_protobuf//:protobuf_java", + "@com_google_protobuf_java_util//jar", "//gax:gax", ] diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java index c051840e8..ea4cea434 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -56,7 +56,7 @@ public String toJson(RequestT message) { } @SuppressWarnings("unchecked") - public RequestT fromJson(InputStream message, RequestT.Builder builder) { + public RequestT fromJson(InputStream message, Message.Builder builder) { try (Reader json = new InputStreamReader(message)) { JsonFormat.parser().ignoringUnknownFields().merge(json, builder); return (RequestT) builder.build(); diff --git a/repositories.bzl b/repositories.bzl index 4d9a429ff..9fea171eb 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -67,8 +67,16 @@ def com_google_api_gax_java_repositories(): _maybe( http_archive, name = "bazel_skylib", - strip_prefix = "bazel-skylib-0.7.0", - urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.7.0.zip"], + strip_prefix = "bazel-skylib-0.9.0", + urls = ["https://github.com/bazelbuild/bazel-skylib/archive/0.9.0.zip"], + ) + + _maybe( + jvm_maven_import_external, + name = "com_google_protobuf_java_util", + artifact = "com.google.protobuf:protobuf-java-util:%s" % PROPERTIES["version.com_google_protobuf"], + server_urls = ["https://repo.maven.apache.org/maven2/", "http://repo1.maven.org/maven2/"], + licenses = ["notice", "reciprocal"], ) _maybe( From 55b9f21958ed2394e5d33dd491347409d1dd87ae Mon Sep 17 00:00:00 2001 From: vam-google Date: Tue, 8 Sep 2020 14:49:05 -0700 Subject: [PATCH 03/16] chore: Add tests to the httpjson protobuf-specific code --- .../ProtoMessageRequestFormatter.java | 2 +- .../api/gax/httpjson/ProtoRestSerializer.java | 2 +- .../ProtoMessageRequestFormatterTest.java | 133 +++++++++++++++++ .../ProtoMessageResponseParserTest.java | 88 +++++++++++ .../gax/httpjson/ProtoRestSerializerTest.java | 141 ++++++++++++++++++ 5 files changed, 364 insertions(+), 2 deletions(-) create mode 100644 gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java create mode 100644 gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java create mode 100644 gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java index 55c4e935e..ec720dd86 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java @@ -42,7 +42,7 @@ public class ProtoMessageRequestFormatter private final PathTemplate pathTemplate; private final FieldsExtractor> pathVarsExtractor; - public ProtoMessageRequestFormatter( + private ProtoMessageRequestFormatter( FieldsExtractor requestBodyExtractor, FieldsExtractor>> queryParamsExtractor, PathTemplate pathTemplate, diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java index ea4cea434..6e7b72527 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -61,7 +61,7 @@ public RequestT fromJson(InputStream message, Message.Builder builder) { JsonFormat.parser().ignoringUnknownFields().merge(json, builder); return (RequestT) builder.build(); } catch (IOException e) { - throw new RuntimeException("Failed to perse response message", e); + throw new RuntimeException("Failed to parse response message", e); } } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java new file mode 100644 index 000000000..f2123d51d --- /dev/null +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java @@ -0,0 +1,133 @@ +/* + * Copyright 2018 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.httpjson; + +import com.google.common.truth.Truth; +import com.google.protobuf.Field; +import com.google.protobuf.Field.Cardinality; +import com.google.protobuf.Option; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; + +public class ProtoMessageRequestFormatterTest { + private Field field; + private HttpRequestFormatter formatter; + + @Before + public void setUp() { + field = + Field.newBuilder() + .setNumber(2) + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .setCardinality(Cardinality.CARDINALITY_OPTIONAL) + .build(); + + formatter = + ProtoMessageRequestFormatter.newBuilder() + .setPath( + "/api/v1/names/{name}/aggregated", + new FieldsExtractor>() { + @Override + public Map extract(Field request) { + Map fields = new HashMap<>(); + ProtoRestSerializer serializer = ProtoRestSerializer.create(); + serializer.toPathParam(fields, "name", request.getName()); + serializer.toPathParam(fields, "kindValue", request.getKindValue()); + return fields; + } + }) + .setQueryParamsExtractor( + new FieldsExtractor>>() { + @Override + public Map> extract(Field request) { + Map> fields = new HashMap<>(); + ProtoRestSerializer serializer = ProtoRestSerializer.create(); + serializer.toQueryParam(fields, "number", request.getNumber()); + serializer.toQueryParam(fields, "typeUrl", request.getTypeUrl()); + return fields; + } + }) + .setRequestBodyExtractor( + new FieldsExtractor() { + @Override + public String extract(Field request) { + ProtoRestSerializer serializer = ProtoRestSerializer.create(); + return serializer.toBody("field", request); + } + }) + .build(); + } + + @Test + public void getQueryParamNames() { + Map> queryParamNames = formatter.getQueryParamNames(field); + Map> expected = new HashMap<>(); + expected.put("number", Arrays.asList("2")); + Truth.assertThat(queryParamNames).isEqualTo(expected); + } + + @Test + public void getRequestBody() { + String bodyJson = formatter.getRequestBody(field); + String expectedBodyJson = + "{\n" + + " \"cardinality\": \"CARDINALITY_OPTIONAL\",\n" + + " \"number\": 2,\n" + + " \"name\": \"field_name1\",\n" + + " \"options\": [{\n" + + " \"name\": \"opt_name1\"\n" + + " }, {\n" + + " \"name\": \"opt_name2\"\n" + + " }]\n" + + "}"; + Truth.assertThat(bodyJson).isEqualTo(expectedBodyJson); + } + + @Test + public void getPath() { + String path = formatter.getPath(field); + Truth.assertThat(path).isEqualTo("api/v1/names/field_name1/aggregated"); + } + + @Test + public void getPathTemplate() { + String path = + formatter.getPathTemplate().instantiate(Collections.singletonMap("name", "field_name1")); + Truth.assertThat(path).isEqualTo("api/v1/names/field_name1/aggregated"); + } +} diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java new file mode 100644 index 000000000..00a3fa2a9 --- /dev/null +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -0,0 +1,88 @@ +package com.google.api.gax.httpjson; + +import static org.junit.Assert.*; + +import com.google.common.truth.Truth; +import com.google.protobuf.Field; +import com.google.protobuf.Field.Cardinality; +import com.google.protobuf.Option; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import org.junit.Before; +import org.junit.Test; + +/* + * 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. + */ +public class ProtoMessageResponseParserTest { + private ProtoMessageResponseParser parser; + private Field field; + private String fieldJson; + + @Before + public void setUp() { + parser = + ProtoMessageResponseParser.newBuilder() + .setDefaultInstance(Field.getDefaultInstance()) + .build(); + + field = + Field.newBuilder() + .setNumber(2) + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .setCardinality(Cardinality.CARDINALITY_OPTIONAL) + .build(); + + fieldJson = + "{\n" + + " \"cardinality\": \"CARDINALITY_OPTIONAL\",\n" + + " \"number\": 2,\n" + + " \"name\": \"field_name1\",\n" + + " \"options\": [{\n" + + " \"name\": \"opt_name1\"\n" + + " }, {\n" + + " \"name\": \"opt_name2\"\n" + + " }]\n" + + "}"; + } + + @Test + public void parse() { + Field actualField = + parser.parse(new ByteArrayInputStream(fieldJson.getBytes(StandardCharsets.UTF_8))); + Truth.assertThat(actualField).isEqualTo(field); + } + + @Test + public void parseInvalidJson() { + try { + parser.parse(new ByteArrayInputStream("invalid".getBytes(StandardCharsets.UTF_8))); + Truth.assertThat(true).isFalse(); + } catch (RuntimeException e) { + Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); + } + } + + @Test + public void serialize() { + String actualFieldJson = parser.serialize(field); + Truth.assertThat(actualFieldJson).isEqualTo(fieldJson); + } + + +} diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java new file mode 100644 index 000000000..0a2c3bd13 --- /dev/null +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java @@ -0,0 +1,141 @@ +/* + * Copyright 2018 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +package com.google.api.gax.httpjson; + +import com.google.common.truth.Truth; +import com.google.protobuf.Field; +import com.google.protobuf.Field.Cardinality; +import com.google.protobuf.Option; +import java.io.ByteArrayInputStream; +import java.io.IOException; +import java.nio.charset.StandardCharsets; +import java.util.Arrays; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import org.junit.Before; +import org.junit.Test; + +public class ProtoRestSerializerTest { + private ProtoRestSerializer requestSerializer; + private Field field; + private String fieldJson; + + @Before + public void setUp() { + requestSerializer = ProtoRestSerializer.create(); + field = + Field.newBuilder() + .setNumber(2) + .setName("field_name1") + .addOptions(Option.newBuilder().setName("opt_name1").build()) + .addOptions(Option.newBuilder().setName("opt_name2").build()) + .setCardinality(Cardinality.CARDINALITY_OPTIONAL) + .build(); + + fieldJson = + "{\n" + + " \"cardinality\": \"CARDINALITY_OPTIONAL\",\n" + + " \"number\": 2,\n" + + " \"name\": \"field_name1\",\n" + + " \"options\": [{\n" + + " \"name\": \"opt_name1\"\n" + + " }, {\n" + + " \"name\": \"opt_name2\"\n" + + " }]\n" + + "}"; + } + + @Test + public void toJson() { + String fieldToJson = requestSerializer.toJson(field); + Truth.assertThat(fieldToJson).isEqualTo(fieldJson); + } + + @Test + public void fromJson() { + Field fieldFromJson = + requestSerializer.fromJson( + new ByteArrayInputStream(fieldJson.getBytes(StandardCharsets.UTF_8)), + Field.newBuilder()); + + Truth.assertThat(fieldFromJson).isEqualTo(field); + } + + @Test + public void fromJsonInvalidJson() { + try { + requestSerializer.fromJson( + new ByteArrayInputStream("heh".getBytes(StandardCharsets.UTF_8)), Field.newBuilder()); + Truth.assertThat(true).isFalse(); + } catch (RuntimeException e) { + Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); + } + } + + @Test + public void toPathParam() { + Map fields = new HashMap<>(); + requestSerializer.toPathParam(fields, "optName1", 1); + requestSerializer.toPathParam(fields, "optName2", 0); + requestSerializer.toPathParam(fields, "optName3", "three"); + requestSerializer.toPathParam(fields, "optName4", ""); + + Map expectedFields = new HashMap<>(); + expectedFields.put("optName1", "1"); + expectedFields.put("optName3", "three"); + + Truth.assertThat(fields).isEqualTo(expectedFields); + } + + @Test + public void toQueryParam() { + Map> fields = new HashMap<>(); + requestSerializer.toQueryParam(fields, "optName1", 1); + requestSerializer.toQueryParam(fields, "optName2", 0); + requestSerializer.toQueryParam(fields, "optName3", "three"); + requestSerializer.toQueryParam(fields, "optName4", ""); + requestSerializer.toQueryParam(fields, "optName5", Arrays.asList("four", "five")); + + Map> expectedFields = new HashMap<>(); + expectedFields.put("optName1", Arrays.asList("1")); + expectedFields.put("optName3", Arrays.asList("three")); + expectedFields.put("optName5", Arrays.asList("four", "five")); + + Truth.assertThat(fields).isEqualTo(expectedFields); + } + + @Test + public void toBody() { + String body = requestSerializer.toBody("bodyField1", field); + Truth.assertThat(body).isEqualTo(fieldJson); + } +} From 3c4c19d4f9ae8a93ca3502a6972129bb2e716384 Mon Sep 17 00:00:00 2001 From: vam-google Date: Tue, 8 Sep 2020 14:52:37 -0700 Subject: [PATCH 04/16] chore: Add tests to the httpjson protobuf-specific code --- .../api/gax/httpjson/ProtoMessageResponseParserTest.java | 4 ---- 1 file changed, 4 deletions(-) diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java index 00a3fa2a9..961ab0761 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -1,7 +1,5 @@ package com.google.api.gax.httpjson; -import static org.junit.Assert.*; - import com.google.common.truth.Truth; import com.google.protobuf.Field; import com.google.protobuf.Field.Cardinality; @@ -83,6 +81,4 @@ public void serialize() { String actualFieldJson = parser.serialize(field); Truth.assertThat(actualFieldJson).isEqualTo(fieldJson); } - - } From e02e36863026b5a9d7990d55d313655911f6e254 Mon Sep 17 00:00:00 2001 From: vam-google Date: Tue, 8 Sep 2020 15:14:47 -0700 Subject: [PATCH 05/16] fix license header --- .../ProtoMessageResponseParserTest.java | 44 ++++++++++++------- 1 file changed, 29 insertions(+), 15 deletions(-) diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java index 961ab0761..38d707918 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -1,3 +1,32 @@ +/* + * Copyright 2018 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ package com.google.api.gax.httpjson; import com.google.common.truth.Truth; @@ -10,21 +39,6 @@ import org.junit.Before; import org.junit.Test; -/* - * 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. - */ public class ProtoMessageResponseParserTest { private ProtoMessageResponseParser parser; private Field field; From 7c08c24370ba89170afff44ac487558f3c3aaca0 Mon Sep 17 00:00:00 2001 From: vam-google Date: Fri, 11 Sep 2020 23:33:28 -0700 Subject: [PATCH 06/16] Address PR feedback --- .../api/gax/httpjson/ProtoRestSerializer.java | 4 ++-- .../gax/httpjson/HttpRequestRunnableTest.java | 18 +++++++++++++++ .../ProtoMessageRequestFormatterTest.java | 8 +++---- .../gax/httpjson/ProtoRestSerializerTest.java | 22 +++++++++---------- 4 files changed, 35 insertions(+), 17 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java index 6e7b72527..41d070815 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -65,14 +65,14 @@ public RequestT fromJson(InputStream message, Message.Builder builder) { } } - public void toPathParam(Map fields, String fieldName, Object fieldValue) { + public void putPathParam(Map fields, String fieldName, Object fieldValue) { if (isDefaultValue(fieldName, fieldValue)) { return; } fields.put(fieldName, String.valueOf(fieldValue)); } - public void toQueryParam(Map> fields, String fieldName, Object fieldValue) { + public void putQueryParam(Map> fields, String fieldName, Object fieldValue) { if (isDefaultValue(fieldName, fieldValue)) { return; } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java index e753f205d..a614f348a 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java @@ -168,6 +168,24 @@ public void testRequestUrl() throws IOException { Truth.assertThat(httpRequest.getUrl().toString()).isEqualTo(expectedUrl); } + @Test + public void testRequestUrlUnnormalized() throws IOException { + httpRequestRunnable = + HttpRequestRunnable.newBuilder() + .setHttpJsonCallOptions(fakeCallOptions) + .setEndpoint("www.googleapis.com/animals/v1/projects") + .setRequest(catMessage) + .setApiMethodDescriptor(methodDescriptor) + .setHttpTransport(new MockHttpTransport()) + .setJsonFactory(new JacksonFactory()) + .setResponseFuture(SettableApiFuture.create()) + .build(); + HttpRequest httpRequest = httpRequestRunnable.createHttpRequest(); + Truth.assertThat(httpRequest.getContent()).isInstanceOf(EmptyContent.class); + String expectedUrl = ENDPOINT + "name/feline" + "?food=bird&food=mouse&size=small"; + Truth.assertThat(httpRequest.getUrl().toString()).isEqualTo(expectedUrl); + } + // TODO(andrealin): test request body private static class CatMessage extends FakeApiMessage { diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java index f2123d51d..94a5c4e0a 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java @@ -66,8 +66,8 @@ public void setUp() { public Map extract(Field request) { Map fields = new HashMap<>(); ProtoRestSerializer serializer = ProtoRestSerializer.create(); - serializer.toPathParam(fields, "name", request.getName()); - serializer.toPathParam(fields, "kindValue", request.getKindValue()); + serializer.putPathParam(fields, "name", request.getName()); + serializer.putPathParam(fields, "kindValue", request.getKindValue()); return fields; } }) @@ -77,8 +77,8 @@ public Map extract(Field request) { public Map> extract(Field request) { Map> fields = new HashMap<>(); ProtoRestSerializer serializer = ProtoRestSerializer.create(); - serializer.toQueryParam(fields, "number", request.getNumber()); - serializer.toQueryParam(fields, "typeUrl", request.getTypeUrl()); + serializer.putQueryParam(fields, "number", request.getNumber()); + serializer.putQueryParam(fields, "typeUrl", request.getTypeUrl()); return fields; } }) diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java index 0a2c3bd13..a9943c936 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java @@ -102,12 +102,12 @@ public void fromJsonInvalidJson() { } @Test - public void toPathParam() { + public void putPathParam() { Map fields = new HashMap<>(); - requestSerializer.toPathParam(fields, "optName1", 1); - requestSerializer.toPathParam(fields, "optName2", 0); - requestSerializer.toPathParam(fields, "optName3", "three"); - requestSerializer.toPathParam(fields, "optName4", ""); + requestSerializer.putPathParam(fields, "optName1", 1); + requestSerializer.putPathParam(fields, "optName2", 0); + requestSerializer.putPathParam(fields, "optName3", "three"); + requestSerializer.putPathParam(fields, "optName4", ""); Map expectedFields = new HashMap<>(); expectedFields.put("optName1", "1"); @@ -117,13 +117,13 @@ public void toPathParam() { } @Test - public void toQueryParam() { + public void putQueryParam() { Map> fields = new HashMap<>(); - requestSerializer.toQueryParam(fields, "optName1", 1); - requestSerializer.toQueryParam(fields, "optName2", 0); - requestSerializer.toQueryParam(fields, "optName3", "three"); - requestSerializer.toQueryParam(fields, "optName4", ""); - requestSerializer.toQueryParam(fields, "optName5", Arrays.asList("four", "five")); + requestSerializer.putQueryParam(fields, "optName1", 1); + requestSerializer.putQueryParam(fields, "optName2", 0); + requestSerializer.putQueryParam(fields, "optName3", "three"); + requestSerializer.putQueryParam(fields, "optName4", ""); + requestSerializer.putQueryParam(fields, "optName5", Arrays.asList("four", "five")); Map> expectedFields = new HashMap<>(); expectedFields.put("optName1", Arrays.asList("1")); From d264bc6bd33b6d8f02f438461c1f9f4fea6e1d0b Mon Sep 17 00:00:00 2001 From: vam-google Date: Sat, 19 Sep 2020 17:46:31 -0700 Subject: [PATCH 07/16] Address PR feedback, add javadoc --- .../api/gax/httpjson/FieldsExtractor.java | 3 ++ .../api/gax/httpjson/HttpResponseParser.java | 5 +- .../ProtoMessageRequestFormatter.java | 9 ++++ .../httpjson/ProtoMessageResponseParser.java | 6 +++ .../api/gax/httpjson/ProtoRestSerializer.java | 47 ++++++++++++++++++- .../ProtoMessageRequestFormatterTest.java | 2 +- .../ProtoMessageResponseParserTest.java | 2 +- .../gax/httpjson/ProtoRestSerializerTest.java | 2 +- 8 files changed, 69 insertions(+), 7 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java index 8acd66469..a00580c65 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java @@ -29,6 +29,9 @@ */ package com.google.api.gax.httpjson; +import com.google.api.core.BetaApi; + +@BetaApi public interface FieldsExtractor { ParamsT extract(RequestT request); } diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java index c67bc4a44..4767ca2fa 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java @@ -39,13 +39,14 @@ public interface HttpResponseParser { /* Parse the http body content JSON stream into the MessageFormatT. * - * @param httpContent the body of an http response. */ + * @param httpContent the body of an http response. + */ MessageFormatT parse(InputStream httpContent); /* Serialize an object into an HTTP body, which is written out to output. * * @param response the object to serialize. - * @param output the output stream to append the serialization to. */ + */ @InternalApi String serialize(MessageFormatT response); } diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java index ec720dd86..823e23054 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java @@ -29,11 +29,14 @@ */ package com.google.api.gax.httpjson; +import com.google.api.core.BetaApi; import com.google.api.pathtemplate.PathTemplate; import com.google.protobuf.Message; import java.util.List; import java.util.Map; +/** Creates parts of a HTTP request from a protobuf message. */ +@BetaApi public class ProtoMessageRequestFormatter implements HttpRequestFormatter { @@ -58,26 +61,32 @@ ProtoMessageRequestFormatter.Builder newBuilder() { return new Builder<>(); } + /* {@inheritDoc} */ @Override public Map> getQueryParamNames(RequestT apiMessage) { return queryParamsExtractor.extract(apiMessage); } + /* {@inheritDoc} */ @Override public String getRequestBody(RequestT apiMessage) { return requestBodyExtractor.extract(apiMessage); } + /* {@inheritDoc} */ @Override public String getPath(RequestT apiMessage) { return pathTemplate.instantiate(pathVarsExtractor.extract(apiMessage)); } + /* {@inheritDoc} */ @Override public PathTemplate getPathTemplate() { return pathTemplate; } + // This has class has compound setter methods (multiple arguments in setters), that is why not + // using @AutoValue. public static class Builder { private FieldsExtractor requestBodyExtractor; private FieldsExtractor>> queryParamsExtractor; diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java index 94f15f009..39c5071e8 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java @@ -29,9 +29,12 @@ */ package com.google.api.gax.httpjson; +import com.google.api.core.BetaApi; import com.google.protobuf.Message; import java.io.InputStream; +/** The implementation of {@link HttpResponseParser} which works with protobuf messages. */ +@BetaApi public class ProtoMessageResponseParser implements HttpResponseParser { @@ -46,17 +49,20 @@ ProtoMessageResponseParser.Builder newBuilder() { return new ProtoMessageResponseParser.Builder<>(); } + /* {@inheritDoc} */ @Override public ResponseT parse(InputStream httpContent) { return ProtoRestSerializer.create() .fromJson(httpContent, defaultInstance.newBuilderForType()); } + /* {@inheritDoc} */ @Override public String serialize(ResponseT response) { return ProtoRestSerializer.create().toJson(response); } + // Convert to @AutoValue if this class gets more complicated public static class Builder { private ResponseT defaultInstance; diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java index 41d070815..be92c1f6a 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -29,6 +29,7 @@ */ package com.google.api.gax.httpjson; +import com.google.api.core.BetaApi; import com.google.common.collect.ImmutableList; import com.google.protobuf.InvalidProtocolBufferException; import com.google.protobuf.Message; @@ -40,14 +41,28 @@ import java.util.List; import java.util.Map; +/** + * This class is responsible for serializing/deserializing protobuf {@link Message} to/from a proper + * format suitable for transferring it over by in a HTTP REST request/response. The information from + * a {@code Message} is split between a request body (JSON), URL path parameters and query + * parameters. + */ +@BetaApi public class ProtoRestSerializer { private ProtoRestSerializer() {} + /** Creates a new instance of ProtoRestSerializer. */ public static ProtoRestSerializer create() { return new ProtoRestSerializer<>(); } - public String toJson(RequestT message) { + /** + * Serializes the data from {@code message} to a JSON string. The implementation relies on + * protobuf native JSON formatter. + * + * @param message a message to serialize + */ + String toJson(RequestT message) { try { return JsonFormat.printer().print(message); } catch (InvalidProtocolBufferException e) { @@ -55,8 +70,14 @@ public String toJson(RequestT message) { } } + /** + * Deserializes a {@code message} from an input stream to a protobuf message. + * + * @param message the input stream with a JSON-encoded message in it + * @param builder an empty builder for the specific {@code RequestT} message to serialize + */ @SuppressWarnings("unchecked") - public RequestT fromJson(InputStream message, Message.Builder builder) { + RequestT fromJson(InputStream message, Message.Builder builder) { try (Reader json = new InputStreamReader(message)) { JsonFormat.parser().ignoringUnknownFields().merge(json, builder); return (RequestT) builder.build(); @@ -65,6 +86,14 @@ public RequestT fromJson(InputStream message, Message.Builder builder) { } } + /** + * Puts a message field in {@code fields} map which will be used to populate URL path of a + * request. + * + * @param fields a map with serialized fields + * @param fieldName a field name + * @param fieldValue a field value + */ public void putPathParam(Map fields, String fieldName, Object fieldValue) { if (isDefaultValue(fieldName, fieldValue)) { return; @@ -72,6 +101,14 @@ public void putPathParam(Map fields, String fieldName, Object fi fields.put(fieldName, String.valueOf(fieldValue)); } + /** + * Puts a message field in {@code fields} map which will be used to populate query parameters of a + * request. + * + * @param fields a map with serialized fields + * @param fieldName a field name + * @param fieldValue a field value + */ public void putQueryParam(Map> fields, String fieldName, Object fieldValue) { if (isDefaultValue(fieldName, fieldValue)) { return; @@ -89,6 +126,12 @@ public void putQueryParam(Map> fields, String fieldName, Ob fields.put(fieldName, paramValueList.build()); } + /** + * Serializes a message to a request body in a form of JSON-encoded string. + * + * @param fieldName a name of a request message field this message belongs to + * @param fieldValue a field value to serialize + */ public String toBody(String fieldName, RequestT fieldValue) { return toJson(fieldValue); } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java index 94a5c4e0a..e8986f61d 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatterTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Google LLC + * Copyright 2020 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java index 38d707918..04620bf85 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Google LLC + * Copyright 2020 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java index a9943c936..040fa49bb 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Google LLC + * Copyright 2020 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are From 3a982ffff71014b8ef53669b56c66ba8a09774b9 Mon Sep 17 00:00:00 2001 From: vam-google Date: Tue, 22 Sep 2020 14:05:47 -0700 Subject: [PATCH 08/16] Address PR feedback --- .../java/com/google/api/gax/httpjson/ProtoRestSerializer.java | 1 + .../api/gax/httpjson/ProtoMessageResponseParserTest.java | 3 ++- .../com/google/api/gax/httpjson/ProtoRestSerializerTest.java | 3 ++- 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java index be92c1f6a..36a3c0951 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -110,6 +110,7 @@ public void putPathParam(Map fields, String fieldName, Object fi * @param fieldValue a field value */ public void putQueryParam(Map> fields, String fieldName, Object fieldValue) { + // Avoids empty query parameter if (isDefaultValue(fieldName, fieldValue)) { return; } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java index 04620bf85..d8873cb68 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -36,6 +36,7 @@ import java.io.ByteArrayInputStream; import java.io.IOException; import java.nio.charset.StandardCharsets; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -84,7 +85,7 @@ public void parse() { public void parseInvalidJson() { try { parser.parse(new ByteArrayInputStream("invalid".getBytes(StandardCharsets.UTF_8))); - Truth.assertThat(true).isFalse(); + Assert.fail(); } catch (RuntimeException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java index 040fa49bb..43fdb78de 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java @@ -41,6 +41,7 @@ import java.util.HashMap; import java.util.List; import java.util.Map; +import org.junit.Assert; import org.junit.Before; import org.junit.Test; @@ -95,7 +96,7 @@ public void fromJsonInvalidJson() { try { requestSerializer.fromJson( new ByteArrayInputStream("heh".getBytes(StandardCharsets.UTF_8)), Field.newBuilder()); - Truth.assertThat(true).isFalse(); + Assert.fail(); } catch (RuntimeException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); } From 07b20bade32f51460e7fef734e172c31c99d73cb Mon Sep 17 00:00:00 2001 From: vam-google Date: Tue, 22 Sep 2020 14:16:57 -0700 Subject: [PATCH 09/16] merge upsream/master --- .../java/com/google/api/gax/httpjson/FieldsExtractor.java | 4 ++++ repositories.bzl | 8 ++++++++ 2 files changed, 12 insertions(+) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java index a00580c65..3e47076cb 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/FieldsExtractor.java @@ -31,6 +31,10 @@ import com.google.api.core.BetaApi; +/** + * A functional interface to be implemented for each request message to extract specific fields from + * it. For advanced usage only. + */ @BetaApi public interface FieldsExtractor { ParamsT extract(RequestT request); diff --git a/repositories.bzl b/repositories.bzl index 0f909e131..2896b375c 100644 --- a/repositories.bzl +++ b/repositories.bzl @@ -72,6 +72,14 @@ def com_google_api_gax_java_repositories(): urls = ["https://github.com/bazelbuild/bazel-skylib/archive/%s.zip" % _bazel_skylib_version], ) + _maybe( + jvm_maven_import_external, + name = "com_google_protobuf_java_util", + artifact = "com.google.protobuf:protobuf-java-util:%s" % PROPERTIES["version.com_google_protobuf"], + server_urls = ["https://repo.maven.apache.org/maven2/", "http://repo1.maven.org/maven2/"], + licenses = ["notice", "reciprocal"], + ) + _maybe( jvm_maven_import_external, name = "io_grpc_grpc_netty_shaded", From 7f72f2ba367f3f41673fd9f81cd9151501838ec8 Mon Sep 17 00:00:00 2001 From: vam-google Date: Fri, 2 Oct 2020 21:34:06 -0700 Subject: [PATCH 10/16] Address PR feedback --- .../api/gax/httpjson/HttpResponseParser.java | 4 +- .../ProtoRestSerializationException.java | 46 +++++++++++++++++++ .../api/gax/httpjson/ProtoRestSerializer.java | 13 +++--- .../ProtoMessageResponseParserTest.java | 2 +- .../gax/httpjson/ProtoRestSerializerTest.java | 2 +- 5 files changed, 57 insertions(+), 10 deletions(-) create mode 100644 gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java index 4767ca2fa..b53596cb4 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java @@ -39,13 +39,13 @@ public interface HttpResponseParser { /* Parse the http body content JSON stream into the MessageFormatT. * - * @param httpContent the body of an http response. + * @param httpContent the body of an http response */ MessageFormatT parse(InputStream httpContent); /* Serialize an object into an HTTP body, which is written out to output. * - * @param response the object to serialize. + * @param response the object to serialize */ @InternalApi String serialize(MessageFormatT response); diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java new file mode 100644 index 000000000..4cc37f624 --- /dev/null +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java @@ -0,0 +1,46 @@ +/* + * Copyright 2018 Google LLC + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are + * met: + * + * * Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * * Redistributions in binary form must reproduce the above + * copyright notice, this list of conditions and the following disclaimer + * in the documentation and/or other materials provided with the + * distribution. + * * Neither the name of Google LLC nor the names of its + * contributors may be used to endorse or promote products derived from + * this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package com.google.api.gax.httpjson; + +import com.google.api.core.BetaApi; + +/** + * An exception thrown when a protobuf message cannot be serialized/deserialized for REST + * interactions. + */ +@BetaApi +public class ProtoRestSerializationException extends RuntimeException { + + private static final long serialVersionUID = -6485633460933364916L; + + public ProtoRestSerializationException(String message, Throwable cause) { + super(message, cause); + } +} diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java index 36a3c0951..5066623dd 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -42,10 +42,10 @@ import java.util.Map; /** - * This class is responsible for serializing/deserializing protobuf {@link Message} to/from a proper - * format suitable for transferring it over by in a HTTP REST request/response. The information from - * a {@code Message} is split between a request body (JSON), URL path parameters and query - * parameters. + * This class serializes/deserializes protobuf {@link Message} for REST interactions. It serializes + * requests protobuf messages into REST messages, splitting the message into the JSON request body, + * URL path parameters, and query parameters. It deserializes JSON responses into response protobuf + * message. */ @BetaApi public class ProtoRestSerializer { @@ -66,7 +66,7 @@ String toJson(RequestT message) { try { return JsonFormat.printer().print(message); } catch (InvalidProtocolBufferException e) { - throw new RuntimeException("Failed to serialize message to JSON", e); + throw new ProtoRestSerializationException("Failed to serialize message to JSON", e); } } @@ -82,7 +82,7 @@ RequestT fromJson(InputStream message, Message.Builder builder) { JsonFormat.parser().ignoringUnknownFields().merge(json, builder); return (RequestT) builder.build(); } catch (IOException e) { - throw new RuntimeException("Failed to parse response message", e); + throw new ProtoRestSerializationException("Failed to parse response message", e); } } @@ -138,6 +138,7 @@ public String toBody(String fieldName, RequestT fieldValue) { } private boolean isDefaultValue(String fieldName, Object fieldValue) { + // TODO: Revisit this approach to ensure proper default-value handling as per design. if (fieldValue instanceof Number) { return ((Number) fieldValue).longValue() == 0L; } else if (fieldValue instanceof String) { diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java index d8873cb68..0a4b6dc12 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -86,7 +86,7 @@ public void parseInvalidJson() { try { parser.parse(new ByteArrayInputStream("invalid".getBytes(StandardCharsets.UTF_8))); Assert.fail(); - } catch (RuntimeException e) { + } catch (ProtoRestSerializationException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); } } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java index 43fdb78de..dc26ea6a6 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java @@ -97,7 +97,7 @@ public void fromJsonInvalidJson() { requestSerializer.fromJson( new ByteArrayInputStream("heh".getBytes(StandardCharsets.UTF_8)), Field.newBuilder()); Assert.fail(); - } catch (RuntimeException e) { + } catch (ProtoRestSerializationException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); } } From 5f257e6f85bdaf090b5ef886c08d6cde603e750a Mon Sep 17 00:00:00 2001 From: vam-google Date: Mon, 5 Oct 2020 14:28:56 -0700 Subject: [PATCH 11/16] Update license year --- .../api/gax/httpjson/ProtoRestSerializationException.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java index 4cc37f624..ec8958d9f 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java @@ -1,5 +1,5 @@ /* - * Copyright 2018 Google LLC + * Copyright 2020 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are From 71474be72113d86cae4c2fbb17471553a22e25e1 Mon Sep 17 00:00:00 2001 From: vam-google Date: Mon, 5 Oct 2020 17:41:45 -0700 Subject: [PATCH 12/16] correct documentation typo --- .../java/com/google/api/gax/httpjson/HttpResponseParser.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java index b53596cb4..5745bbd68 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java @@ -33,13 +33,13 @@ import com.google.api.core.InternalExtensionOnly; import java.io.InputStream; -/** Interface for classes that parse parts of Http responses into the parameterized message type. */ +/** Interface for classes that parse parts of HTTP responses into the parameterized message type. */ @InternalExtensionOnly public interface HttpResponseParser { /* Parse the http body content JSON stream into the MessageFormatT. * - * @param httpContent the body of an http response + * @param httpContent the body of an HTTP response */ MessageFormatT parse(InputStream httpContent); From b231f0f8cc2bf0085dc9cf3b12d0685d56607001 Mon Sep 17 00:00:00 2001 From: vam-google Date: Mon, 5 Oct 2020 18:28:48 -0700 Subject: [PATCH 13/16] Add thrown exceptions into documentation, wrap gson exceptions into RestSerializationException --- .../api/gax/httpjson/ApiMessageHttpResponseParser.java | 10 ++++++++-- .../google/api/gax/httpjson/HttpResponseParser.java | 10 ++++++++-- .../google/api/gax/httpjson/ProtoRestSerializer.java | 8 ++++++-- ...nException.java => RestSerializationException.java} | 8 ++++++-- .../gax/httpjson/ProtoMessageResponseParserTest.java | 2 +- .../api/gax/httpjson/ProtoRestSerializerTest.java | 2 +- 6 files changed, 30 insertions(+), 10 deletions(-) rename gax-httpjson/src/main/java/com/google/api/gax/httpjson/{ProtoRestSerializationException.java => RestSerializationException.java} (89%) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java index 6931b07d9..2bfa9a9c3 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java @@ -32,6 +32,8 @@ import com.google.auto.value.AutoValue; import com.google.gson.Gson; import com.google.gson.GsonBuilder; +import com.google.gson.JsonIOException; +import com.google.gson.JsonSyntaxException; import com.google.gson.TypeAdapter; import com.google.gson.stream.JsonReader; import com.google.gson.stream.JsonWriter; @@ -91,8 +93,12 @@ public ResponseT parse(InputStream httpResponseBody) { return null; } else { Type responseType = getResponseInstance().getClass(); - return getResponseMarshaller() - .fromJson(new InputStreamReader(httpResponseBody), responseType); + try { + return getResponseMarshaller() + .fromJson(new InputStreamReader(httpResponseBody), responseType); + } catch (JsonIOException | JsonSyntaxException e) { + throw new RestSerializationException(e); + } } } diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java index 5745bbd68..0bb70568e 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java @@ -37,15 +37,21 @@ @InternalExtensionOnly public interface HttpResponseParser { - /* Parse the http body content JSON stream into the MessageFormatT. + /** + * Parse the http body content JSON stream into the MessageFormatT. * * @param httpContent the body of an HTTP response + * @throws RestSerializationException if failed to parse the {@code httpContent} to a valid {@code + * MessageFormatT} */ MessageFormatT parse(InputStream httpContent); - /* Serialize an object into an HTTP body, which is written out to output. + /** + * Serialize an object into an HTTP body, which is written out to output. * * @param response the object to serialize + * @throws RestSerializationException if failed to serialize {@code response} to a valid {@code + * String} representation */ @InternalApi String serialize(MessageFormatT response); diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java index 5066623dd..3e4c58a12 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -61,12 +61,14 @@ public static ProtoRestSerializer create() * protobuf native JSON formatter. * * @param message a message to serialize + * @throws InvalidProtocolBufferException if failed to serialize the protobuf message to JSON + * format */ String toJson(RequestT message) { try { return JsonFormat.printer().print(message); } catch (InvalidProtocolBufferException e) { - throw new ProtoRestSerializationException("Failed to serialize message to JSON", e); + throw new RestSerializationException("Failed to serialize message to JSON", e); } } @@ -75,6 +77,8 @@ String toJson(RequestT message) { * * @param message the input stream with a JSON-encoded message in it * @param builder an empty builder for the specific {@code RequestT} message to serialize + * @throws RestSerializationException if failed to deserialize a protobuf message from the JSON + * stream */ @SuppressWarnings("unchecked") RequestT fromJson(InputStream message, Message.Builder builder) { @@ -82,7 +86,7 @@ RequestT fromJson(InputStream message, Message.Builder builder) { JsonFormat.parser().ignoringUnknownFields().merge(json, builder); return (RequestT) builder.build(); } catch (IOException e) { - throw new ProtoRestSerializationException("Failed to parse response message", e); + throw new RestSerializationException("Failed to parse response message", e); } } diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/RestSerializationException.java similarity index 89% rename from gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java rename to gax-httpjson/src/main/java/com/google/api/gax/httpjson/RestSerializationException.java index ec8958d9f..7f5252595 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializationException.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/RestSerializationException.java @@ -36,11 +36,15 @@ * interactions. */ @BetaApi -public class ProtoRestSerializationException extends RuntimeException { +public class RestSerializationException extends RuntimeException { private static final long serialVersionUID = -6485633460933364916L; - public ProtoRestSerializationException(String message, Throwable cause) { + public RestSerializationException(Throwable cause) { + super(cause); + } + + public RestSerializationException(String message, Throwable cause) { super(message, cause); } } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java index 0a4b6dc12..3794f66b1 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -86,7 +86,7 @@ public void parseInvalidJson() { try { parser.parse(new ByteArrayInputStream("invalid".getBytes(StandardCharsets.UTF_8))); Assert.fail(); - } catch (ProtoRestSerializationException e) { + } catch (RestSerializationException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); } } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java index dc26ea6a6..572a00597 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java @@ -97,7 +97,7 @@ public void fromJsonInvalidJson() { requestSerializer.fromJson( new ByteArrayInputStream("heh".getBytes(StandardCharsets.UTF_8)), Field.newBuilder()); Assert.fail(); - } catch (ProtoRestSerializationException e) { + } catch (RestSerializationException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); } } From 79c41a10ba653ca6379c43f9e56800b429e14364 Mon Sep 17 00:00:00 2001 From: vam-google Date: Wed, 7 Oct 2020 16:57:59 -0700 Subject: [PATCH 14/16] Added a comment about triple nested generics --- .../google/api/gax/httpjson/ProtoMessageRequestFormatter.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java index 823e23054..9f75ba576 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageRequestFormatter.java @@ -41,6 +41,9 @@ public class ProtoMessageRequestFormatter implements HttpRequestFormatter { private final FieldsExtractor requestBodyExtractor; + // Using of triple nested generics (which is not pretty) is predetermined by the + // Map> returned value type of the getQueryParamNames interface method + // implemented by this class. private final FieldsExtractor>> queryParamsExtractor; private final PathTemplate pathTemplate; private final FieldsExtractor> pathVarsExtractor; From 0dc2d89b6f3bc92054f1e756faf178129493bc8d Mon Sep 17 00:00:00 2001 From: vam-google Date: Sat, 10 Oct 2020 16:21:24 -0700 Subject: [PATCH 15/16] Add character set (breaking change) --- .../api/gax/httpjson/ApiMessageHttpResponseParser.java | 6 ++++-- .../com/google/api/gax/httpjson/HttpRequestRunnable.java | 5 +++-- .../com/google/api/gax/httpjson/HttpResponseParser.java | 4 +++- .../api/gax/httpjson/ProtoMessageResponseParser.java | 5 +++-- .../com/google/api/gax/httpjson/ProtoRestSerializer.java | 6 ++++-- .../api/gax/httpjson/HttpJsonDirectCallableTest.java | 3 ++- .../google/api/gax/httpjson/HttpRequestRunnableTest.java | 6 ++++-- .../com/google/api/gax/httpjson/MockHttpServiceTest.java | 3 ++- .../api/gax/httpjson/ProtoMessageResponseParserTest.java | 8 ++++++-- .../google/api/gax/httpjson/ProtoRestSerializerTest.java | 5 ++++- 10 files changed, 35 insertions(+), 16 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java index 2bfa9a9c3..bcb9158de 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java @@ -40,6 +40,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; +import java.nio.charset.Charset; /** Utility class to parse {@link ApiMessage}s from HTTP responses. */ @AutoValue @@ -88,14 +89,15 @@ ApiMessageHttpResponseParser.Builder newBuilder() { } @Override - public ResponseT parse(InputStream httpResponseBody) { + public ResponseT parse(InputStream httpResponseBody, Charset httpResponseBodyCharset) { if (getResponseInstance() == null) { return null; } else { Type responseType = getResponseInstance().getClass(); try { return getResponseMarshaller() - .fromJson(new InputStreamReader(httpResponseBody), responseType); + .fromJson( + new InputStreamReader(httpResponseBody, httpResponseBodyCharset), responseType); } catch (JsonIOException | JsonSyntaxException e) { throw new RestSerializationException(e); } diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java index 5506c5fe2..8155622c7 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java @@ -141,7 +141,6 @@ public void run() { try { HttpRequest httpRequest = createHttpRequest(); HttpResponse httpResponse = httpRequest.execute(); - if (!httpResponse.isSuccessStatusCode()) { ApiExceptionFactory.createException( null, @@ -150,7 +149,9 @@ public void run() { } if (getApiMethodDescriptor().getResponseParser() != null) { ResponseT response = - getApiMethodDescriptor().getResponseParser().parse(httpResponse.getContent()); + getApiMethodDescriptor() + .getResponseParser() + .parse(httpResponse.getContent(), httpResponse.getContentCharset()); getResponseFuture().set(response); } else { getResponseFuture().set(null); diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java index 0bb70568e..642919e6e 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java @@ -32,6 +32,7 @@ import com.google.api.core.InternalApi; import com.google.api.core.InternalExtensionOnly; import java.io.InputStream; +import java.nio.charset.Charset; /** Interface for classes that parse parts of HTTP responses into the parameterized message type. */ @InternalExtensionOnly @@ -41,10 +42,11 @@ public interface HttpResponseParser { * Parse the http body content JSON stream into the MessageFormatT. * * @param httpContent the body of an HTTP response + * @param httpContentCharset the charset of the HTTP response body * @throws RestSerializationException if failed to parse the {@code httpContent} to a valid {@code * MessageFormatT} */ - MessageFormatT parse(InputStream httpContent); + MessageFormatT parse(InputStream httpContent, Charset httpContentCharset); /** * Serialize an object into an HTTP body, which is written out to output. diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java index 39c5071e8..dad1e644e 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java @@ -32,6 +32,7 @@ import com.google.api.core.BetaApi; import com.google.protobuf.Message; import java.io.InputStream; +import java.nio.charset.Charset; /** The implementation of {@link HttpResponseParser} which works with protobuf messages. */ @BetaApi @@ -51,9 +52,9 @@ ProtoMessageResponseParser.Builder newBuilder() { /* {@inheritDoc} */ @Override - public ResponseT parse(InputStream httpContent) { + public ResponseT parse(InputStream httpContent, Charset httpContentCharset) { return ProtoRestSerializer.create() - .fromJson(httpContent, defaultInstance.newBuilderForType()); + .fromJson(httpContent, httpContentCharset, defaultInstance.newBuilderForType()); } /* {@inheritDoc} */ diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java index 3e4c58a12..4ca49ed2d 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoRestSerializer.java @@ -38,6 +38,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.io.Reader; +import java.nio.charset.Charset; import java.util.List; import java.util.Map; @@ -76,13 +77,14 @@ String toJson(RequestT message) { * Deserializes a {@code message} from an input stream to a protobuf message. * * @param message the input stream with a JSON-encoded message in it + * @param messageCharset the message charset * @param builder an empty builder for the specific {@code RequestT} message to serialize * @throws RestSerializationException if failed to deserialize a protobuf message from the JSON * stream */ @SuppressWarnings("unchecked") - RequestT fromJson(InputStream message, Message.Builder builder) { - try (Reader json = new InputStreamReader(message)) { + RequestT fromJson(InputStream message, Charset messageCharset, Message.Builder builder) { + try (Reader json = new InputStreamReader(message, messageCharset)) { JsonFormat.parser().ignoringUnknownFields().merge(json, builder); return (RequestT) builder.build(); } catch (IOException e) { diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java index 250605aa8..b54505932 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java @@ -35,6 +35,7 @@ import com.google.api.pathtemplate.PathTemplate; import com.google.common.collect.ImmutableMap; import java.io.InputStream; +import java.nio.charset.Charset; import java.util.List; import java.util.Map; import org.junit.Test; @@ -189,7 +190,7 @@ public PathTemplate getPathTemplate() { private static final class FakeResponseParser implements HttpResponseParser { @Override - public String parse(InputStream httpContent) { + public String parse(InputStream httpContent, Charset httpContentCharset) { return "fake"; } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java index a614f348a..6c3c81fc2 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java @@ -43,6 +43,7 @@ import com.google.common.truth.Truth; import java.io.IOException; import java.io.InputStream; +import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -130,7 +131,7 @@ public PathTemplate getPathTemplate() { catParser = new HttpResponseParser() { @Override - public EmptyMessage parse(InputStream httpContent) { + public EmptyMessage parse(InputStream httpContent, Charset httpContentCharset) { return null; } @@ -182,7 +183,8 @@ public void testRequestUrlUnnormalized() throws IOException { .build(); HttpRequest httpRequest = httpRequestRunnable.createHttpRequest(); Truth.assertThat(httpRequest.getContent()).isInstanceOf(EmptyContent.class); - String expectedUrl = ENDPOINT + "name/feline" + "?food=bird&food=mouse&size=small"; + String expectedUrl = + "https://www.googleapis.com/animals/v1/projects/name/feline?food=bird&food=mouse&size=small"; Truth.assertThat(httpRequest.getUrl().toString()).isEqualTo(expectedUrl); } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/MockHttpServiceTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/MockHttpServiceTest.java index a0f03b93a..9aa786f07 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/MockHttpServiceTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/MockHttpServiceTest.java @@ -54,6 +54,7 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; +import java.nio.charset.Charset; import java.util.List; import java.util.Map; import org.junit.Before; @@ -94,7 +95,7 @@ public HumanMessage( private static final HttpResponseParser PET_RESPONSE_PARSER = new HttpResponseParser() { @Override - public PetMessage parse(InputStream httpContent) { + public PetMessage parse(InputStream httpContent, Charset httpContentCharset) { return null; } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java index 3794f66b1..043c7c563 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -77,14 +77,18 @@ public void setUp() { @Test public void parse() { Field actualField = - parser.parse(new ByteArrayInputStream(fieldJson.getBytes(StandardCharsets.UTF_8))); + parser.parse( + new ByteArrayInputStream(fieldJson.getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8); Truth.assertThat(actualField).isEqualTo(field); } @Test public void parseInvalidJson() { try { - parser.parse(new ByteArrayInputStream("invalid".getBytes(StandardCharsets.UTF_8))); + parser.parse( + new ByteArrayInputStream("invalid".getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8); Assert.fail(); } catch (RestSerializationException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java index 572a00597..77f9f72d3 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoRestSerializerTest.java @@ -86,6 +86,7 @@ public void fromJson() { Field fieldFromJson = requestSerializer.fromJson( new ByteArrayInputStream(fieldJson.getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8, Field.newBuilder()); Truth.assertThat(fieldFromJson).isEqualTo(field); @@ -95,7 +96,9 @@ public void fromJson() { public void fromJsonInvalidJson() { try { requestSerializer.fromJson( - new ByteArrayInputStream("heh".getBytes(StandardCharsets.UTF_8)), Field.newBuilder()); + new ByteArrayInputStream("heh".getBytes(StandardCharsets.UTF_8)), + StandardCharsets.UTF_8, + Field.newBuilder()); Assert.fail(); } catch (RestSerializationException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class); From 169fcf250281cfd9b5c741681ae6ff051e34b77c Mon Sep 17 00:00:00 2001 From: vam-google Date: Sun, 11 Oct 2020 22:23:14 -0700 Subject: [PATCH 16/16] Rollback HttpResponseParser.parse() surface change --- .../api/gax/httpjson/ApiMessageHttpResponseParser.java | 6 +++--- .../com/google/api/gax/httpjson/HttpRequestRunnable.java | 4 +--- .../com/google/api/gax/httpjson/HttpResponseParser.java | 4 +--- .../api/gax/httpjson/ProtoMessageResponseParser.java | 6 +++--- .../api/gax/httpjson/HttpJsonDirectCallableTest.java | 3 +-- .../google/api/gax/httpjson/HttpRequestRunnableTest.java | 3 +-- .../com/google/api/gax/httpjson/MockHttpServiceTest.java | 3 +-- .../api/gax/httpjson/ProtoMessageResponseParserTest.java | 8 ++------ 8 files changed, 13 insertions(+), 24 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java index bcb9158de..e7c7848c1 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageHttpResponseParser.java @@ -40,7 +40,7 @@ import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Type; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** Utility class to parse {@link ApiMessage}s from HTTP responses. */ @AutoValue @@ -89,7 +89,7 @@ ApiMessageHttpResponseParser.Builder newBuilder() { } @Override - public ResponseT parse(InputStream httpResponseBody, Charset httpResponseBodyCharset) { + public ResponseT parse(InputStream httpResponseBody) { if (getResponseInstance() == null) { return null; } else { @@ -97,7 +97,7 @@ public ResponseT parse(InputStream httpResponseBody, Charset httpResponseBodyCha try { return getResponseMarshaller() .fromJson( - new InputStreamReader(httpResponseBody, httpResponseBodyCharset), responseType); + new InputStreamReader(httpResponseBody, StandardCharsets.UTF_8), responseType); } catch (JsonIOException | JsonSyntaxException e) { throw new RestSerializationException(e); } diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java index 8155622c7..bbeb2362a 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpRequestRunnable.java @@ -149,9 +149,7 @@ public void run() { } if (getApiMethodDescriptor().getResponseParser() != null) { ResponseT response = - getApiMethodDescriptor() - .getResponseParser() - .parse(httpResponse.getContent(), httpResponse.getContentCharset()); + getApiMethodDescriptor().getResponseParser().parse(httpResponse.getContent()); getResponseFuture().set(response); } else { getResponseFuture().set(null); diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java index 642919e6e..0bb70568e 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpResponseParser.java @@ -32,7 +32,6 @@ import com.google.api.core.InternalApi; import com.google.api.core.InternalExtensionOnly; import java.io.InputStream; -import java.nio.charset.Charset; /** Interface for classes that parse parts of HTTP responses into the parameterized message type. */ @InternalExtensionOnly @@ -42,11 +41,10 @@ public interface HttpResponseParser { * Parse the http body content JSON stream into the MessageFormatT. * * @param httpContent the body of an HTTP response - * @param httpContentCharset the charset of the HTTP response body * @throws RestSerializationException if failed to parse the {@code httpContent} to a valid {@code * MessageFormatT} */ - MessageFormatT parse(InputStream httpContent, Charset httpContentCharset); + MessageFormatT parse(InputStream httpContent); /** * Serialize an object into an HTTP body, which is written out to output. diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java index dad1e644e..084b725e1 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ProtoMessageResponseParser.java @@ -32,7 +32,7 @@ import com.google.api.core.BetaApi; import com.google.protobuf.Message; import java.io.InputStream; -import java.nio.charset.Charset; +import java.nio.charset.StandardCharsets; /** The implementation of {@link HttpResponseParser} which works with protobuf messages. */ @BetaApi @@ -52,9 +52,9 @@ ProtoMessageResponseParser.Builder newBuilder() { /* {@inheritDoc} */ @Override - public ResponseT parse(InputStream httpContent, Charset httpContentCharset) { + public ResponseT parse(InputStream httpContent) { return ProtoRestSerializer.create() - .fromJson(httpContent, httpContentCharset, defaultInstance.newBuilderForType()); + .fromJson(httpContent, StandardCharsets.UTF_8, defaultInstance.newBuilderForType()); } /* {@inheritDoc} */ diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java index b54505932..250605aa8 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpJsonDirectCallableTest.java @@ -35,7 +35,6 @@ import com.google.api.pathtemplate.PathTemplate; import com.google.common.collect.ImmutableMap; import java.io.InputStream; -import java.nio.charset.Charset; import java.util.List; import java.util.Map; import org.junit.Test; @@ -190,7 +189,7 @@ public PathTemplate getPathTemplate() { private static final class FakeResponseParser implements HttpResponseParser { @Override - public String parse(InputStream httpContent, Charset httpContentCharset) { + public String parse(InputStream httpContent) { return "fake"; } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java index 6c3c81fc2..752a01946 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/HttpRequestRunnableTest.java @@ -43,7 +43,6 @@ import com.google.common.truth.Truth; import java.io.IOException; import java.io.InputStream; -import java.nio.charset.Charset; import java.util.Arrays; import java.util.List; import java.util.Map; @@ -131,7 +130,7 @@ public PathTemplate getPathTemplate() { catParser = new HttpResponseParser() { @Override - public EmptyMessage parse(InputStream httpContent, Charset httpContentCharset) { + public EmptyMessage parse(InputStream httpContent) { return null; } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/MockHttpServiceTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/MockHttpServiceTest.java index 9aa786f07..a0f03b93a 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/MockHttpServiceTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/MockHttpServiceTest.java @@ -54,7 +54,6 @@ import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; -import java.nio.charset.Charset; import java.util.List; import java.util.Map; import org.junit.Before; @@ -95,7 +94,7 @@ public HumanMessage( private static final HttpResponseParser PET_RESPONSE_PARSER = new HttpResponseParser() { @Override - public PetMessage parse(InputStream httpContent, Charset httpContentCharset) { + public PetMessage parse(InputStream httpContent) { return null; } diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java index 043c7c563..3794f66b1 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ProtoMessageResponseParserTest.java @@ -77,18 +77,14 @@ public void setUp() { @Test public void parse() { Field actualField = - parser.parse( - new ByteArrayInputStream(fieldJson.getBytes(StandardCharsets.UTF_8)), - StandardCharsets.UTF_8); + parser.parse(new ByteArrayInputStream(fieldJson.getBytes(StandardCharsets.UTF_8))); Truth.assertThat(actualField).isEqualTo(field); } @Test public void parseInvalidJson() { try { - parser.parse( - new ByteArrayInputStream("invalid".getBytes(StandardCharsets.UTF_8)), - StandardCharsets.UTF_8); + parser.parse(new ByteArrayInputStream("invalid".getBytes(StandardCharsets.UTF_8))); Assert.fail(); } catch (RestSerializationException e) { Truth.assertThat(e.getCause()).isInstanceOf(IOException.class);