From d9b7641b381a5b96f2dbdafeb39b689517a8bbcc Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Thu, 21 Feb 2019 17:10:28 -0800 Subject: [PATCH 01/13] add diff --- .../ApiMessageOperationTransformers.java | 122 ++++++++++ .../ApiMessageOperationTransformersTest.java | 228 ++++++++++++++++++ 2 files changed, 350 insertions(+) create mode 100644 gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java create mode 100644 gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java new file mode 100644 index 000000000..04927977b --- /dev/null +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java @@ -0,0 +1,122 @@ +/* + * Copyright 2019 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.ApiFunction; +import com.google.api.core.BetaApi; +import com.google.api.gax.longrunning.OperationSnapshot; +import com.google.api.gax.rpc.ApiExceptionFactory; +import com.google.api.gax.rpc.StatusCode.Code; + +/** + * Transformers from OperationSnapshot wrappers to the underlying native ApiMessage objects. Public + * for technical reasons; intended for use by generated code. + */ +@BetaApi("The surface for use by generated code is not stable yet and may change in the future.") +public class ApiMessageOperationTransformers { + private ApiMessageOperationTransformers() {} + + public static class ResponseTransformer + implements ApiFunction { + private final Class responseTClass; + + private ResponseTransformer(Class responseTClass) { + this.responseTClass = responseTClass; + } + + /** Unwraps an OperationSnapshot and returns the contained method response message. */ + @SuppressWarnings("unchecked") + public ResponseT apply(OperationSnapshot operationSnapshot) { + if (!operationSnapshot.getErrorCode().getCode().equals(Code.OK)) { + throw ApiExceptionFactory.createException( + "Operation with name \"" + + operationSnapshot.getName() + + "\" failed with status = " + + operationSnapshot.getErrorCode() + + " and message = " + + operationSnapshot.getErrorMessage(), + null, + operationSnapshot.getErrorCode(), + false); + } + if (!operationSnapshot.getResponse().getClass().isAssignableFrom(responseTClass)) { + throw ApiExceptionFactory.createException( + new Throwable( + String.format( + "Operation with name \"%s\" succeeded, but its response type %s cannot be cast to %s.", + operationSnapshot.getName(), + operationSnapshot.getResponse().getClass().getCanonicalName(), + responseTClass.getCanonicalName())), + operationSnapshot.getErrorCode(), + false); + } + return (ResponseT) operationSnapshot.getResponse(); + } + + public static + ApiMessageOperationTransformers.ResponseTransformer create( + Class packedClass) { + return new ApiMessageOperationTransformers.ResponseTransformer<>(packedClass); + } + } + + @SuppressWarnings("unchecked") + public static class MetadataTransformer + implements ApiFunction { + private final Class metadataTClass; + + private MetadataTransformer(Class metadataTClass) { + this.metadataTClass = metadataTClass; + } + + /** Unwraps an OperationSnapshot and returns the contained operation metadata message. */ + @Override + public MetadataT apply(OperationSnapshot operationSnapshot) { + if (!operationSnapshot.getMetadata().getClass().isAssignableFrom(metadataTClass)) { + throw ApiExceptionFactory.createException( + new Throwable( + String.format( + "Operation with name \"%s\" succeeded, but its metadata type %s cannot be cast to %s.", + operationSnapshot.getName(), + operationSnapshot.getMetadata().getClass().getCanonicalName(), + metadataTClass.getCanonicalName())), + operationSnapshot.getErrorCode(), + false); + } + return (MetadataT) (operationSnapshot.getMetadata()); + } + + public static + ApiMessageOperationTransformers.MetadataTransformer create( + Class packedClass) { + return new ApiMessageOperationTransformers.MetadataTransformer<>(packedClass); + } + } +} diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java new file mode 100644 index 000000000..ba5a8a1ab --- /dev/null +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java @@ -0,0 +1,228 @@ +/* + * Copyright 2017 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.gax.httpjson.ApiMessageOperationTransformers.MetadataTransformer; +import com.google.api.gax.httpjson.ApiMessageOperationTransformers.ResponseTransformer; +import com.google.api.gax.httpjson.testing.FakeApiMessage; +import com.google.api.gax.longrunning.OperationSnapshot; +import com.google.api.gax.rpc.ApiException; +import com.google.api.gax.rpc.StatusCode; +import com.google.api.gax.rpc.StatusCode.Code; +import com.google.api.gax.rpc.UnavailableException; +import com.google.common.collect.ImmutableMap; +import com.google.common.truth.Truth; +import java.util.List; +import org.junit.Rule; +import org.junit.Test; +import org.junit.rules.ExpectedException; +import org.junit.runner.RunWith; +import org.junit.runners.JUnit4; + +/** Tests for ApiMessageOperationTransformers. */ +@RunWith(JUnit4.class) +public class ApiMessageOperationTransformersTest { + @Rule public ExpectedException thrown = ExpectedException.none(); + + @Test + public void testResponseTransformer() { + ResponseTransformer transformer = ResponseTransformer.create(EmptyMessage.class); + EmptyMessage emptyResponse = new EmptyMessage(); + + MetadataMessage metadata = new MetadataMessage(Status.PENDING, Code.OK); + OperationSnapshot operationSnapshot = + new OperationSnapshotImpl( + new OperationMessage<>("Pending; no response method", emptyResponse, metadata)); + + Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(emptyResponse); + } + + @Test + public void testResponseTransformer_exception() { + thrown.expect(UnavailableException.class); + ResponseTransformer transformer = ResponseTransformer.create(EmptyMessage.class); + EmptyMessage emptyResponse = new EmptyMessage(); + MetadataMessage metadata = new MetadataMessage(Status.PENDING, Code.UNAVAILABLE); + OperationSnapshot operationSnapshot = + new OperationSnapshotImpl( + new OperationMessage<>("Unavailable; no response method", emptyResponse, metadata)); + + Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(emptyResponse); + } + + @Test + public void testResponseTransformer_mismatchedTypes() { + thrown.expect(ApiException.class); + thrown.expectMessage("cannot be cast"); + ResponseTransformer transformer = ResponseTransformer.create(EmptyMessage.class); + MetadataMessage metadata = new MetadataMessage(Status.PENDING, Code.OK); + ApiMessage bananaResponse = + new FakeApiMessage(ImmutableMap.of("name", "banana"), null, null); + EmptyMessage emptyResponse = new EmptyMessage(); + OperationSnapshot operationSnapshot = + new OperationSnapshotImpl( + new OperationMessage<>("No response method", bananaResponse, metadata)); + Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(emptyResponse); + } + + @Test + public void testMetadataTransformer() { + MetadataTransformer transformer = + MetadataTransformer.create(MetadataMessage.class); + EmptyMessage returnType = new EmptyMessage(); + MetadataMessage metadataMessage = new MetadataMessage(Status.PENDING, Code.OK); + OperationMessage operation = new OperationMessage<>("foo", returnType, metadataMessage); + OperationSnapshot operationSnapshot = new OperationSnapshotImpl(operation); + Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(metadataMessage); + } + + @Test + public void testMetadataTransformer_mismatchedTypes() { + thrown.expect(ApiException.class); + thrown.expectMessage("cannot be cast"); + MetadataTransformer transformer = + MetadataTransformer.create(OperationMessage.class); + MetadataMessage metadataMessage = new MetadataMessage(Status.PENDING, Code.OK); + ApiMessage bananaResponse = + new FakeApiMessage(ImmutableMap.of("name", "banana"), null, null); + OperationMessage metadata = + new OperationMessage<>("No response method", bananaResponse, metadataMessage); + OperationSnapshot operationSnapshot = new OperationSnapshotImpl(metadata); + Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(bananaResponse); + } + + private enum Status { + PENDING, + DONE + } + + private static class MetadataMessage implements ApiMessage { + + private final Status status; + private final Code code; + + public MetadataMessage(Status status, Code code) { + this.status = status; + this.code = code; + } + + public Object getFieldValue(String fieldName) { + if ("status".equals(fieldName)) { + return status; + } + if ("code".equals(fieldName)) { + return code; + } + return null; + } + + public List getFieldMask() { + return null; + } + + public ApiMessage getApiMessageRequestBody() { + return null; + } + } + + private static class OperationMessage + implements ApiMessage { + + private final String name; + private final ResponseT responseT; + private final MetadataT metadata; + + public OperationMessage(String name, ResponseT responseT, MetadataT metadata) { + this.name = name; + this.responseT = responseT; + this.metadata = metadata; + } + + public Object getFieldValue(String fieldName) { + if ("name".equals(fieldName)) { + return name; + } + if ("responseT".equals(fieldName)) { + return responseT; + } + if ("metadata".equals(fieldName)) { + return metadata; + } + return null; + } + + public List getFieldMask() { + return null; + } + + public ResponseT getApiMessageRequestBody() { + return responseT; + } + } + + private static class OperationSnapshotImpl implements OperationSnapshot { + + private final OperationMessage operation; + + public OperationSnapshotImpl(OperationMessage operation) { + this.operation = operation; + } + + @Override + public String getName() { + return (String) operation.getFieldValue("name"); + } + + @Override + public Object getMetadata() { + return operation.metadata; + } + + @Override + public boolean isDone() { + return operation.metadata.getFieldValue("status") != Status.PENDING; + } + + @Override + public Object getResponse() { + return operation.getApiMessageRequestBody(); + } + + @Override + public StatusCode getErrorCode() { + return HttpJsonStatusCode.of((Code) operation.metadata.getFieldValue("code")); + } + + @Override + public String getErrorMessage() { + return ((Code) operation.metadata.getFieldValue("code")).name(); + } + } +} From 67482da42951b5d33d9038751a468c01b4cb2d89 Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Thu, 21 Feb 2019 22:00:18 -0800 Subject: [PATCH 02/13] 2019 --- .../api/gax/httpjson/ApiMessageOperationTransformersTest.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java index ba5a8a1ab..e7c965b33 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java @@ -1,5 +1,5 @@ /* - * Copyright 2017 Google LLC + * Copyright 2019 Google LLC * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are From 2ab22ddf935065cfb5a3dd63d048bc982d10fbf8 Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Fri, 1 Mar 2019 13:50:43 -0800 Subject: [PATCH 03/13] string.format exception message --- .../gax/httpjson/ApiMessageOperationTransformers.java | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java index 04927977b..9981b8a47 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java @@ -56,12 +56,11 @@ private ResponseTransformer(Class responseTClass) { public ResponseT apply(OperationSnapshot operationSnapshot) { if (!operationSnapshot.getErrorCode().getCode().equals(Code.OK)) { throw ApiExceptionFactory.createException( - "Operation with name \"" - + operationSnapshot.getName() - + "\" failed with status = " - + operationSnapshot.getErrorCode() - + " and message = " - + operationSnapshot.getErrorMessage(), + String.format( + "Operation with name \"%s\" failed with status = %s and message = %s", + operationSnapshot.getName(), + operationSnapshot.getErrorCode(), + operationSnapshot.getErrorMessage()), null, operationSnapshot.getErrorCode(), false); From f2abc319a92122af8b1cf7efcf7eb9a22b0dccfa Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Fri, 1 Mar 2019 13:56:33 -0800 Subject: [PATCH 04/13] make 200-299 codes 200 --- .../com/google/api/gax/httpjson/HttpJsonStatusCode.java | 7 +++++-- gax/src/main/java/com/google/api/gax/rpc/StatusCode.java | 2 +- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonStatusCode.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonStatusCode.java index 7e3f66a09..49d701fd6 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonStatusCode.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonStatusCode.java @@ -59,9 +59,12 @@ public static HttpJsonStatusCode of(StatusCode.Code statusCode) { static StatusCode.Code httpStatusToStatusCode(int httpStatus, String errorMessage) { String causeMessage = Strings.nullToEmpty(errorMessage).toUpperCase(); + if (httpStatus >= 200 && httpStatus < 300) { + // any 2xx code is considered successful; let's squash all successful codes into the + // 200 Code.OK entity. + return Code.OK; + } switch (httpStatus) { - case 200: - return Code.OK; case 400: if (causeMessage.contains(OUT_OF_RANGE)) { return Code.OUT_OF_RANGE; diff --git a/gax/src/main/java/com/google/api/gax/rpc/StatusCode.java b/gax/src/main/java/com/google/api/gax/rpc/StatusCode.java index 470f61cd1..6bd848806 100644 --- a/gax/src/main/java/com/google/api/gax/rpc/StatusCode.java +++ b/gax/src/main/java/com/google/api/gax/rpc/StatusCode.java @@ -42,7 +42,7 @@ public interface StatusCode { enum Code { - OK(200), + OK(200), // In CANCELLED(499), UNKNOWN(500), INVALID_ARGUMENT(400), From d3230f5797b3eecb5be7649c92d2f6ec30a533e2 Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Fri, 1 Mar 2019 13:58:47 -0800 Subject: [PATCH 05/13] move SuppressWarnings to the line that triggers it --- .../api/gax/httpjson/ApiMessageOperationTransformers.java | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java index 9981b8a47..d838557d0 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java @@ -52,7 +52,7 @@ private ResponseTransformer(Class responseTClass) { } /** Unwraps an OperationSnapshot and returns the contained method response message. */ - @SuppressWarnings("unchecked") + // @SuppressWarnings("unchecked") public ResponseT apply(OperationSnapshot operationSnapshot) { if (!operationSnapshot.getErrorCode().getCode().equals(Code.OK)) { throw ApiExceptionFactory.createException( @@ -76,7 +76,9 @@ public ResponseT apply(OperationSnapshot operationSnapshot) { operationSnapshot.getErrorCode(), false); } - return (ResponseT) operationSnapshot.getResponse(); + @SuppressWarnings("unchecked") + ResponseT response = (ResponseT) operationSnapshot.getResponse(); + return response; } public static From 681bc987ab063d6330dfbb97bc366d6604bd9742 Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Fri, 1 Mar 2019 14:07:51 -0800 Subject: [PATCH 06/13] again move suppresswarnings --- .../api/gax/httpjson/ApiMessageOperationTransformers.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java index d838557d0..ad8228a3b 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java @@ -88,7 +88,6 @@ ApiMessageOperationTransformers.ResponseTransformer create( } } - @SuppressWarnings("unchecked") public static class MetadataTransformer implements ApiFunction { private final Class metadataTClass; @@ -111,7 +110,9 @@ public MetadataT apply(OperationSnapshot operationSnapshot) { operationSnapshot.getErrorCode(), false); } - return (MetadataT) (operationSnapshot.getMetadata()); + @SuppressWarnings("unchecked") + MetadataT metadata = (MetadataT) (operationSnapshot.getMetadata()); + return metadata; } public static From 40ecd2ef744b4edc0d918d720d4c0defcf40a19e Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Fri, 1 Mar 2019 15:19:15 -0800 Subject: [PATCH 07/13] helper method transformEntityFromOperationSnapshot --- .../ApiMessageOperationTransformers.java | 55 +++++++++---------- 1 file changed, 26 insertions(+), 29 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java index ad8228a3b..22d72c707 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java @@ -52,7 +52,6 @@ private ResponseTransformer(Class responseTClass) { } /** Unwraps an OperationSnapshot and returns the contained method response message. */ - // @SuppressWarnings("unchecked") public ResponseT apply(OperationSnapshot operationSnapshot) { if (!operationSnapshot.getErrorCode().getCode().equals(Code.OK)) { throw ApiExceptionFactory.createException( @@ -65,20 +64,8 @@ public ResponseT apply(OperationSnapshot operationSnapshot) { operationSnapshot.getErrorCode(), false); } - if (!operationSnapshot.getResponse().getClass().isAssignableFrom(responseTClass)) { - throw ApiExceptionFactory.createException( - new Throwable( - String.format( - "Operation with name \"%s\" succeeded, but its response type %s cannot be cast to %s.", - operationSnapshot.getName(), - operationSnapshot.getResponse().getClass().getCanonicalName(), - responseTClass.getCanonicalName())), - operationSnapshot.getErrorCode(), - false); - } - @SuppressWarnings("unchecked") - ResponseT response = (ResponseT) operationSnapshot.getResponse(); - return response; + return transformEntityFromOperationSnapshot( + operationSnapshot, responseTClass, operationSnapshot.getResponse(), "response"); } public static @@ -99,20 +86,8 @@ private MetadataTransformer(Class metadataTClass) { /** Unwraps an OperationSnapshot and returns the contained operation metadata message. */ @Override public MetadataT apply(OperationSnapshot operationSnapshot) { - if (!operationSnapshot.getMetadata().getClass().isAssignableFrom(metadataTClass)) { - throw ApiExceptionFactory.createException( - new Throwable( - String.format( - "Operation with name \"%s\" succeeded, but its metadata type %s cannot be cast to %s.", - operationSnapshot.getName(), - operationSnapshot.getMetadata().getClass().getCanonicalName(), - metadataTClass.getCanonicalName())), - operationSnapshot.getErrorCode(), - false); - } - @SuppressWarnings("unchecked") - MetadataT metadata = (MetadataT) (operationSnapshot.getMetadata()); - return metadata; + return transformEntityFromOperationSnapshot( + operationSnapshot, metadataTClass, operationSnapshot.getMetadata(), "metadata"); } public static @@ -121,4 +96,26 @@ ApiMessageOperationTransformers.MetadataTransformer create( return new ApiMessageOperationTransformers.MetadataTransformer<>(packedClass); } } + + private static T transformEntityFromOperationSnapshot( + OperationSnapshot operationSnapshot, + Class clazz, + Object operationEntity, + String entityName) { + if (!clazz.isAssignableFrom(operationEntity.getClass())) { + throw ApiExceptionFactory.createException( + new Throwable( + String.format( + "Operation with name \"%s\" succeeded, but its %s type %s cannot be cast to %s.", + operationSnapshot.getName(), + entityName, + operationEntity.getClass().getCanonicalName(), + clazz.getCanonicalName())), + operationSnapshot.getErrorCode(), + false); + } + @SuppressWarnings("unchecked") + T typedEntity = (T) operationEntity; + return typedEntity; + } } From d1f3bb114773844c76dc9a97741f9ec673c54f0c Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Fri, 1 Mar 2019 15:24:11 -0800 Subject: [PATCH 08/13] renaming --- .../ApiMessageOperationTransformersTest.java | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java index e7c965b33..caf779ee2 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java @@ -56,10 +56,10 @@ public void testResponseTransformer() { ResponseTransformer transformer = ResponseTransformer.create(EmptyMessage.class); EmptyMessage emptyResponse = new EmptyMessage(); - MetadataMessage metadata = new MetadataMessage(Status.PENDING, Code.OK); + FakeMetadataMessage metadata = new FakeMetadataMessage(Status.PENDING, Code.OK); OperationSnapshot operationSnapshot = new OperationSnapshotImpl( - new OperationMessage<>("Pending; no response method", emptyResponse, metadata)); + new FakeOperationMessage<>("Pending; no response method", emptyResponse, metadata)); Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(emptyResponse); } @@ -69,10 +69,10 @@ public void testResponseTransformer_exception() { thrown.expect(UnavailableException.class); ResponseTransformer transformer = ResponseTransformer.create(EmptyMessage.class); EmptyMessage emptyResponse = new EmptyMessage(); - MetadataMessage metadata = new MetadataMessage(Status.PENDING, Code.UNAVAILABLE); + FakeMetadataMessage metadata = new FakeMetadataMessage(Status.PENDING, Code.UNAVAILABLE); OperationSnapshot operationSnapshot = new OperationSnapshotImpl( - new OperationMessage<>("Unavailable; no response method", emptyResponse, metadata)); + new FakeOperationMessage<>("Unavailable; no response method", emptyResponse, metadata)); Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(emptyResponse); } @@ -82,23 +82,23 @@ public void testResponseTransformer_mismatchedTypes() { thrown.expect(ApiException.class); thrown.expectMessage("cannot be cast"); ResponseTransformer transformer = ResponseTransformer.create(EmptyMessage.class); - MetadataMessage metadata = new MetadataMessage(Status.PENDING, Code.OK); + FakeMetadataMessage metadata = new FakeMetadataMessage(Status.PENDING, Code.OK); ApiMessage bananaResponse = new FakeApiMessage(ImmutableMap.of("name", "banana"), null, null); EmptyMessage emptyResponse = new EmptyMessage(); OperationSnapshot operationSnapshot = new OperationSnapshotImpl( - new OperationMessage<>("No response method", bananaResponse, metadata)); + new FakeOperationMessage<>("No response method", bananaResponse, metadata)); Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(emptyResponse); } @Test public void testMetadataTransformer() { - MetadataTransformer transformer = - MetadataTransformer.create(MetadataMessage.class); + MetadataTransformer transformer = + MetadataTransformer.create(FakeMetadataMessage.class); EmptyMessage returnType = new EmptyMessage(); - MetadataMessage metadataMessage = new MetadataMessage(Status.PENDING, Code.OK); - OperationMessage operation = new OperationMessage<>("foo", returnType, metadataMessage); + FakeMetadataMessage metadataMessage = new FakeMetadataMessage(Status.PENDING, Code.OK); + FakeOperationMessage operation = new FakeOperationMessage<>("foo", returnType, metadataMessage); OperationSnapshot operationSnapshot = new OperationSnapshotImpl(operation); Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(metadataMessage); } @@ -107,13 +107,13 @@ public void testMetadataTransformer() { public void testMetadataTransformer_mismatchedTypes() { thrown.expect(ApiException.class); thrown.expectMessage("cannot be cast"); - MetadataTransformer transformer = - MetadataTransformer.create(OperationMessage.class); - MetadataMessage metadataMessage = new MetadataMessage(Status.PENDING, Code.OK); + MetadataTransformer transformer = + MetadataTransformer.create(FakeOperationMessage.class); + FakeMetadataMessage metadataMessage = new FakeMetadataMessage(Status.PENDING, Code.OK); ApiMessage bananaResponse = new FakeApiMessage(ImmutableMap.of("name", "banana"), null, null); - OperationMessage metadata = - new OperationMessage<>("No response method", bananaResponse, metadataMessage); + FakeOperationMessage metadata = + new FakeOperationMessage<>("No response method", bananaResponse, metadataMessage); OperationSnapshot operationSnapshot = new OperationSnapshotImpl(metadata); Truth.assertThat(transformer.apply(operationSnapshot)).isEqualTo(bananaResponse); } @@ -123,12 +123,12 @@ private enum Status { DONE } - private static class MetadataMessage implements ApiMessage { + private static class FakeMetadataMessage implements ApiMessage { private final Status status; private final Code code; - public MetadataMessage(Status status, Code code) { + public FakeMetadataMessage(Status status, Code code) { this.status = status; this.code = code; } @@ -152,14 +152,14 @@ public ApiMessage getApiMessageRequestBody() { } } - private static class OperationMessage + private static class FakeOperationMessage implements ApiMessage { private final String name; private final ResponseT responseT; private final MetadataT metadata; - public OperationMessage(String name, ResponseT responseT, MetadataT metadata) { + public FakeOperationMessage(String name, ResponseT responseT, MetadataT metadata) { this.name = name; this.responseT = responseT; this.metadata = metadata; @@ -189,9 +189,9 @@ public ResponseT getApiMessageRequestBody() { private static class OperationSnapshotImpl implements OperationSnapshot { - private final OperationMessage operation; + private final FakeOperationMessage operation; - public OperationSnapshotImpl(OperationMessage operation) { + public OperationSnapshotImpl(FakeOperationMessage operation) { this.operation = operation; } From a19e0dd45a2f81472e7cc4fd3ae34972306f505c Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Fri, 1 Mar 2019 15:31:35 -0800 Subject: [PATCH 09/13] formatting --- .../api/gax/httpjson/ApiMessageOperationTransformersTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java index caf779ee2..0cc91f8ea 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java @@ -152,7 +152,8 @@ public ApiMessage getApiMessageRequestBody() { } } - private static class FakeOperationMessage + private static class FakeOperationMessage< + ResponseT extends ApiMessage, MetadataT extends ApiMessage> implements ApiMessage { private final String name; From b5cec719b24dede2c54a67d70090daf8ca73dc70 Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Fri, 1 Mar 2019 15:50:11 -0800 Subject: [PATCH 10/13] rebase on master --- .../gax/httpjson/ApiMessageOperationTransformersTest.java | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java index 0cc91f8ea..783408632 100644 --- a/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java +++ b/gax-httpjson/src/test/java/com/google/api/gax/httpjson/ApiMessageOperationTransformersTest.java @@ -54,7 +54,7 @@ public class ApiMessageOperationTransformersTest { @Test public void testResponseTransformer() { ResponseTransformer transformer = ResponseTransformer.create(EmptyMessage.class); - EmptyMessage emptyResponse = new EmptyMessage(); + EmptyMessage emptyResponse = EmptyMessage.getDefaultInstance(); FakeMetadataMessage metadata = new FakeMetadataMessage(Status.PENDING, Code.OK); OperationSnapshot operationSnapshot = @@ -68,7 +68,7 @@ public void testResponseTransformer() { public void testResponseTransformer_exception() { thrown.expect(UnavailableException.class); ResponseTransformer transformer = ResponseTransformer.create(EmptyMessage.class); - EmptyMessage emptyResponse = new EmptyMessage(); + EmptyMessage emptyResponse = EmptyMessage.getDefaultInstance(); FakeMetadataMessage metadata = new FakeMetadataMessage(Status.PENDING, Code.UNAVAILABLE); OperationSnapshot operationSnapshot = new OperationSnapshotImpl( @@ -85,7 +85,7 @@ public void testResponseTransformer_mismatchedTypes() { FakeMetadataMessage metadata = new FakeMetadataMessage(Status.PENDING, Code.OK); ApiMessage bananaResponse = new FakeApiMessage(ImmutableMap.of("name", "banana"), null, null); - EmptyMessage emptyResponse = new EmptyMessage(); + EmptyMessage emptyResponse = EmptyMessage.getDefaultInstance(); OperationSnapshot operationSnapshot = new OperationSnapshotImpl( new FakeOperationMessage<>("No response method", bananaResponse, metadata)); @@ -96,7 +96,7 @@ public void testResponseTransformer_mismatchedTypes() { public void testMetadataTransformer() { MetadataTransformer transformer = MetadataTransformer.create(FakeMetadataMessage.class); - EmptyMessage returnType = new EmptyMessage(); + EmptyMessage returnType = EmptyMessage.getDefaultInstance(); FakeMetadataMessage metadataMessage = new FakeMetadataMessage(Status.PENDING, Code.OK); FakeOperationMessage operation = new FakeOperationMessage<>("foo", returnType, metadataMessage); OperationSnapshot operationSnapshot = new OperationSnapshotImpl(operation); From a53600ac445af1180a8dda7ad7c83d8f29f519d5 Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Mon, 4 Mar 2019 16:01:01 -0800 Subject: [PATCH 11/13] revert status code --- .../com/google/api/gax/httpjson/HttpJsonStatusCode.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonStatusCode.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonStatusCode.java index 49d701fd6..7e3f66a09 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonStatusCode.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/HttpJsonStatusCode.java @@ -59,12 +59,9 @@ public static HttpJsonStatusCode of(StatusCode.Code statusCode) { static StatusCode.Code httpStatusToStatusCode(int httpStatus, String errorMessage) { String causeMessage = Strings.nullToEmpty(errorMessage).toUpperCase(); - if (httpStatus >= 200 && httpStatus < 300) { - // any 2xx code is considered successful; let's squash all successful codes into the - // 200 Code.OK entity. - return Code.OK; - } switch (httpStatus) { + case 200: + return Code.OK; case 400: if (causeMessage.contains(OUT_OF_RANGE)) { return Code.OUT_OF_RANGE; From ac2fd7cadb23a639298780741a27990c67e22ae9 Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Mon, 4 Mar 2019 16:02:04 -0800 Subject: [PATCH 12/13] revert again --- gax/src/main/java/com/google/api/gax/rpc/StatusCode.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/gax/src/main/java/com/google/api/gax/rpc/StatusCode.java b/gax/src/main/java/com/google/api/gax/rpc/StatusCode.java index 6bd848806..470f61cd1 100644 --- a/gax/src/main/java/com/google/api/gax/rpc/StatusCode.java +++ b/gax/src/main/java/com/google/api/gax/rpc/StatusCode.java @@ -42,7 +42,7 @@ public interface StatusCode { enum Code { - OK(200), // In + OK(200), CANCELLED(499), UNKNOWN(500), INVALID_ARGUMENT(400), From a31105606241d839949f318bc09b9c71a9a30836 Mon Sep 17 00:00:00 2001 From: Andrea Lin Date: Mon, 4 Mar 2019 16:13:58 -0800 Subject: [PATCH 13/13] comment for 2xx --- .../google/api/gax/httpjson/ApiMessageOperationTransformers.java | 1 + 1 file changed, 1 insertion(+) diff --git a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java index 22d72c707..fffeb8c07 100644 --- a/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java +++ b/gax-httpjson/src/main/java/com/google/api/gax/httpjson/ApiMessageOperationTransformers.java @@ -54,6 +54,7 @@ private ResponseTransformer(Class responseTClass) { /** Unwraps an OperationSnapshot and returns the contained method response message. */ public ResponseT apply(OperationSnapshot operationSnapshot) { if (!operationSnapshot.getErrorCode().getCode().equals(Code.OK)) { + // We potentially need to handle 2xx codes that are also successful. throw ApiExceptionFactory.createException( String.format( "Operation with name \"%s\" failed with status = %s and message = %s",