From f3ca5426a33e88c653fb453f91a2e2c5b2d2503b Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 9 Mar 2021 12:58:00 -0500 Subject: [PATCH 1/4] - adds a serializer to handle actions & functions with primitive return types --- .../serializer/EdmNativeTypeSerializer.java | 47 ++++++++++++ .../graph/serializer/GsonFactory.java | 72 ++++++++++++++++++ .../EdmNativeTypeSerializerTests.java | 76 +++++++++++++++++++ 3 files changed, 195 insertions(+) create mode 100644 src/main/java/com/microsoft/graph/serializer/EdmNativeTypeSerializer.java create mode 100644 src/test/java/com/microsoft/graph/serializer/EdmNativeTypeSerializerTests.java diff --git a/src/main/java/com/microsoft/graph/serializer/EdmNativeTypeSerializer.java b/src/main/java/com/microsoft/graph/serializer/EdmNativeTypeSerializer.java new file mode 100644 index 00000000000..68d4fdc6282 --- /dev/null +++ b/src/main/java/com/microsoft/graph/serializer/EdmNativeTypeSerializer.java @@ -0,0 +1,47 @@ +package com.microsoft.graph.serializer; + +import java.lang.reflect.Type; +import java.math.BigDecimal; +import java.util.UUID; + +import com.google.gson.JsonElement; +import com.google.gson.JsonParseException; +import com.microsoft.graph.logger.ILogger; + +public class EdmNativeTypeSerializer { + public static T deserialize(final JsonElement json, final Class type, final ILogger logger) throws JsonParseException { + if (json == null || !(type instanceof Class)) { + return null; + } else if(json.isJsonPrimitive()) { + return getPrimitiveValue(json, type); + } else if(json.isJsonObject()) { + final JsonElement element = json.getAsJsonObject().get("@odata.null"); + if(element != null && element.isJsonPrimitive()) { + return getPrimitiveValue(element, type); + } else { + return null; + } + } + return null; + } + @SuppressWarnings("unchecked") + private static T getPrimitiveValue(final JsonElement json, final Class type) { + if(type == Boolean.class) { + return (T) Boolean.valueOf(json.getAsBoolean()); + } else if(type == String.class) { + return (T)json.getAsString(); + } else if(type == Integer.class) { + return (T) Integer.valueOf(json.getAsInt()); + } else if(type == UUID.class) { + return (T) UUID.fromString(json.getAsString()); + } else if(type == Long.class) { + return (T) Long.valueOf(json.getAsLong()); + } else if (type == Float.class) { + return (T) Float.valueOf(json.getAsFloat()); + } else if (type == BigDecimal.class) { + return (T) json.getAsBigDecimal(); + } else { + return null; + } + } +} diff --git a/src/main/java/com/microsoft/graph/serializer/GsonFactory.java b/src/main/java/com/microsoft/graph/serializer/GsonFactory.java index 746825efbd2..66574176fea 100644 --- a/src/main/java/com/microsoft/graph/serializer/GsonFactory.java +++ b/src/main/java/com/microsoft/graph/serializer/GsonFactory.java @@ -37,10 +37,12 @@ import com.microsoft.graph.models.extensions.TimeOfDay; import java.lang.reflect.Type; +import java.math.BigDecimal; import java.text.ParseException; import java.util.Calendar; import java.util.EnumSet; import java.util.GregorianCalendar; +import java.util.UUID; import javax.xml.datatype.DatatypeFactory; import javax.xml.datatype.Duration; @@ -244,8 +246,78 @@ public TimeOfDay deserialize(final JsonElement json, } }; + final JsonDeserializer booleanJsonDeserializer = new JsonDeserializer() { + @Override + public Boolean deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + return EdmNativeTypeSerializer.deserialize(json, Boolean.class, logger); + } + }; + + final JsonDeserializer stringJsonDeserializer = new JsonDeserializer() { + @Override + public String deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + return EdmNativeTypeSerializer.deserialize(json, String.class, logger); + } + }; + + final JsonDeserializer bigDecimalJsonDeserializer = new JsonDeserializer() { + @Override + public BigDecimal deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + return EdmNativeTypeSerializer.deserialize(json, BigDecimal.class, logger); + } + }; + + final JsonDeserializer integerJsonDeserializer = new JsonDeserializer() { + @Override + public Integer deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + return EdmNativeTypeSerializer.deserialize(json, Integer.class, logger); + } + }; + + final JsonDeserializer longJsonDeserializer = new JsonDeserializer() { + @Override + public Long deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + return EdmNativeTypeSerializer.deserialize(json, Long.class, logger); + } + }; + + final JsonDeserializer uuidJsonDeserializer = new JsonDeserializer() { + @Override + public UUID deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + return EdmNativeTypeSerializer.deserialize(json, UUID.class, logger); + } + }; + + final JsonDeserializer floatJsonDeserializer = new JsonDeserializer() { + @Override + public Float deserialize(final JsonElement json, + final Type typeOfT, + final JsonDeserializationContext context) throws JsonParseException { + return EdmNativeTypeSerializer.deserialize(json, Float.class, logger); + } + }; + return new GsonBuilder() .excludeFieldsWithoutExposeAnnotation() + .registerTypeAdapter(Boolean.class, booleanJsonDeserializer) + .registerTypeAdapter(String.class, stringJsonDeserializer) + .registerTypeAdapter(Float.class, floatJsonDeserializer) + .registerTypeAdapter(Integer.class, integerJsonDeserializer) + .registerTypeAdapter(BigDecimal.class, bigDecimalJsonDeserializer) + .registerTypeAdapter(UUID.class, uuidJsonDeserializer) + .registerTypeAdapter(Long.class, longJsonDeserializer) .registerTypeAdapter(Calendar.class, calendarJsonSerializer) .registerTypeAdapter(Calendar.class, calendarJsonDeserializer) .registerTypeAdapter(GregorianCalendar.class, calendarJsonSerializer) diff --git a/src/test/java/com/microsoft/graph/serializer/EdmNativeTypeSerializerTests.java b/src/test/java/com/microsoft/graph/serializer/EdmNativeTypeSerializerTests.java new file mode 100644 index 00000000000..c0137b7a23d --- /dev/null +++ b/src/test/java/com/microsoft/graph/serializer/EdmNativeTypeSerializerTests.java @@ -0,0 +1,76 @@ +package com.microsoft.graph.serializer; + +import static org.junit.Assert.assertEquals; + +import java.math.BigDecimal; +import java.util.UUID; + +import com.microsoft.graph.logger.DefaultLogger; + +import org.junit.Test; + +public class EdmNativeTypeSerializerTests { + @Test + public void testBoolean() throws Exception { + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + + final String source = "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#Edm.Null\",\"@odata.null\":true}"; + final Boolean result = serializer.deserializeObject(source, Boolean.class); + + assertEquals(Boolean.valueOf(true), result); + } + @Test + public void testInteger() throws Exception { + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + + final String source = "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#Edm.Null\",\"@odata.null\":12}"; + final Integer result = serializer.deserializeObject(source, Integer.class); + + assertEquals(Integer.valueOf(12), result); + } + @Test + public void testString() throws Exception { + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + + final String source = "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#Edm.Null\",\"@odata.null\":\"toto\"}"; + final String result = serializer.deserializeObject(source, String.class); + + assertEquals("toto", result); + } + @Test + public void testFloat() throws Exception { + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + + final String source = "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#Edm.Null\",\"@odata.null\":12.5}"; + final Float result = serializer.deserializeObject(source, Float.class); + + assertEquals(Float.valueOf("12.5"), result); + } + @Test + public void testLong() throws Exception { + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + + final String source = "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#Edm.Null\",\"@odata.null\":12}"; + final Long result = serializer.deserializeObject(source, Long.class); + + assertEquals(Long.valueOf(12), result); + } + @Test + public void testBigDecimal() throws Exception { + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + + final String source = "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#Edm.Null\",\"@odata.null\":12}"; + final BigDecimal result = serializer.deserializeObject(source, BigDecimal.class); + + assertEquals(BigDecimal.valueOf(12), result); + } + @Test + public void testUUID() throws Exception { + final DefaultSerializer serializer = new DefaultSerializer(new DefaultLogger()); + + final String source = "{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#Edm.Null\",\"@odata.null\":\"0E6558C3-9640-4385-860A-2A894AC5C246\"}"; + final UUID result = serializer.deserializeObject(source, UUID.class); + + assertEquals(UUID.fromString("0E6558C3-9640-4385-860A-2A894AC5C246"), result); + } +} From b9c9126ec0bd6f23a0bed4eab6ccff93d5a5ab2f Mon Sep 17 00:00:00 2001 From: Vincent Biret Date: Tue, 9 Mar 2021 14:00:26 -0500 Subject: [PATCH 2/4] - fixes a bug where null content on post request would have a wrong application/octet-stream content type --- .../graph/http/CoreHttpProvider.java | 26 +++++++++---------- .../microsoft/graph/functional/UserTests.java | 18 ++++++++++--- 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java b/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java index 725327cecfa..9f5e3bc8953 100644 --- a/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java +++ b/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java @@ -243,7 +243,7 @@ public Request getHttpRequest(final IHttpRequest request, if(this.connectionConfig == null) { this.connectionConfig = new DefaultConnectionConfig(); } - + // Request level middleware options RedirectOptions redirectOptions = new RedirectOptions(request.getMaxRedirects() > 0? request.getMaxRedirects() : this.connectionConfig.getMaxRedirects(), request.getShouldRedirect() != null? request.getShouldRedirect() : this.connectionConfig.getShouldRedirect()); @@ -256,7 +256,7 @@ public Request getHttpRequest(final IHttpRequest request, .newBuilder() .tag(RedirectOptions.class, redirectOptions) .tag(RetryOptions.class, retryOptions); - + String contenttype = null; logger.logDebug("Request Method " + request.getHttpMethod().toString()); @@ -276,11 +276,7 @@ public Request getHttpRequest(final IHttpRequest request, // This ensures that the Content-Length header is properly set if (request.getHttpMethod() == HttpMethod.POST) { bytesToWrite = new byte[0]; - if(contenttype == null) { - contenttype = Constants.BINARY_CONTENT_TYPE; - } - } - else { + } else { bytesToWrite = null; } } else if (serializable instanceof byte[]) { @@ -341,7 +337,11 @@ public void writeTo(BufferedSink sink) throws IOException { @Override public MediaType contentType() { - return MediaType.parse(mediaContentType); + if(mediaContentType == null || mediaContentType.isEmpty()) { + return null; + } else { + return MediaType.parse(mediaContentType); + } } }; } @@ -401,7 +401,7 @@ public Request authenticateRequest(Request request) { try { // Call being executed - + if (handler != null) { handler.configConnection(response); @@ -425,7 +425,7 @@ public Request authenticateRequest(Request request) { if (response.code() == HttpResponseCode.HTTP_NOBODY || response.code() == HttpResponseCode.HTTP_NOT_MODIFIED) { - logger.logDebug("Handling response with no body"); + logger.logDebug("Handling response with no body"); return handleEmptyResponse(responseHeadersHelper.getResponseHeadersAsMapOfStringList(response), resultClass); } @@ -442,7 +442,7 @@ public Request authenticateRequest(Request request) { return (Result) null; final String contentType = headers.get(Constants.CONTENT_TYPE_HEADER_NAME); - if (contentType != null && resultClass != InputStream.class && + if (contentType != null && resultClass != InputStream.class && contentType.contains(Constants.JSON_CONTENT_TYPE)) { logger.logDebug("Response json"); return handleJsonResponse(in, responseHeadersHelper.getResponseHeadersAsMapOfStringList(response), resultClass); @@ -534,12 +534,12 @@ private Result handleJsonResponse(final InputStream in, Map Result handleEmptyResponse(Map> responseHeaders, final Class clazz) + private Result handleEmptyResponse(Map> responseHeaders, final Class clazz) throws UnsupportedEncodingException{ //Create an empty object to attach the response headers to InputStream in = new ByteArrayInputStream("{}".getBytes(Constants.JSON_ENCODING)); diff --git a/src/test/java/com/microsoft/graph/functional/UserTests.java b/src/test/java/com/microsoft/graph/functional/UserTests.java index aa48c9ae977..49a8b6a68f5 100644 --- a/src/test/java/com/microsoft/graph/functional/UserTests.java +++ b/src/test/java/com/microsoft/graph/functional/UserTests.java @@ -3,6 +3,7 @@ import static org.junit.Assert.assertNotEquals; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; +import static org.junit.Assert.assertNull; import java.io.File; import java.io.FileInputStream; @@ -160,7 +161,7 @@ public void meInsightsUsed() { IUsedInsightCollectionPage usedInsightCollectionPage = graphServiceClient.me().insights().used().buildRequest().get(); assertNotNull(usedInsightCollectionPage); } - + @Test public void mailFoldertest() { //GET me/mailFolders @@ -172,7 +173,7 @@ public void mailFoldertest() { assertNotNull(messageCollectionPage); } } - + @Test public void meMemberof() { IDirectoryObjectCollectionWithReferencesPage page = graphServiceClient.me().memberOf().buildRequest().get(); @@ -201,7 +202,7 @@ public void run() { } @Test - public void emptyPostContentType() { + public void emptyPostContentTypeIsNotReset() { final String contentTypeValue = "application/json"; final HeaderOption ctype = new HeaderOption("Content-Type", contentTypeValue); final ArrayList