diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS
index 10a1e9e17..88bfd2d01 100644
--- a/.github/CODEOWNERS
+++ b/.github/CODEOWNERS
@@ -1 +1 @@
-* @baywet @ddyett @MichaelMainer @nikithauc @zengin
\ No newline at end of file
+* @baywet @ddyett @MichaelMainer @nikithauc @zengin @ramsessanchez
diff --git a/android/build.gradle b/android/build.gradle
index 204b25314..1086c94d3 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -23,13 +23,13 @@ apply plugin: "com.android.library"
apply plugin: "com.github.ben-manes.versions"
android {
- compileSdkVersion 30
+ compileSdkVersion 31
defaultConfig {
versionCode 1
versionName "1.0"
minSdkVersion 26
- targetSdkVersion 30
+ targetSdkVersion 31
}
buildTypes {
diff --git a/gradle.properties b/gradle.properties
index 907c5c302..884db4ece 100644
--- a/gradle.properties
+++ b/gradle.properties
@@ -25,7 +25,7 @@ mavenGroupId = com.microsoft.graph
mavenArtifactId = microsoft-graph-core
mavenMajorVersion = 2
mavenMinorVersion = 0
-mavenPatchVersion = 7
+mavenPatchVersion = 8
mavenArtifactSuffix =
#These values are used to run functional tests
diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle
index 38c853013..38372fff6 100644
--- a/gradle/dependencies.gradle
+++ b/gradle/dependencies.gradle
@@ -2,12 +2,12 @@ dependencies {
// Use JUnit test framework
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
- testImplementation 'org.mockito:mockito-inline:3.11.1'
+ testImplementation 'org.mockito:mockito-inline:3.11.2'
api 'com.squareup.okhttp3:okhttp:4.9.1'
implementation 'com.google.guava:guava:30.1.1-jre'
implementation 'com.google.code.gson:gson:2.8.7'
- api 'com.azure:azure-core:1.17.0'
+ api 'com.azure:azure-core:1.18.0'
}
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index f0e1d0f8c..2367f3c17 100644
--- a/pom.xml
+++ b/pom.xml
@@ -8,7 +8,7 @@
com.microsoft.graphmicrosoft-graph-core
- 2.0.2
+ 2.0.8pom
@@ -35,7 +35,7 @@
com.azureazure-core
- 1.17.0
+ 1.18.0org.junit.jupiter
@@ -46,8 +46,8 @@
org.mockitomockito-inline
- 3.11.1
+ 3.11.2test
-
\ No newline at end of file
+
diff --git a/readme.md b/readme.md
index 219f6c662..688c3d4ae 100644
--- a/readme.md
+++ b/readme.md
@@ -22,7 +22,7 @@ repositories {
dependencies {
// Include the sdk as a dependency
- implementation 'com.microsoft.graph:microsoft-graph-core:2.0.7'
+ implementation 'com.microsoft.graph:microsoft-graph-core:2.0.8'
// This dependency is only needed if you are using the TokenCrendentialAuthProvider
implementation 'com.azure:azure-identity:1.3.1'
}
@@ -37,7 +37,7 @@ Add the dependency in `dependencies` in pom.xml
com.microsoft.graphmicrosoft-graph-core
- 2.0.7
+ 2.0.8com.azureazure-identity
diff --git a/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java b/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java
index 4b53f5bbe..3b50842c5 100644
--- a/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java
+++ b/src/main/java/com/microsoft/graph/http/CoreHttpProvider.java
@@ -296,7 +296,15 @@ public Request getHttpRequest(@Nonnull final IHttpRequest request
}
} else {
logger.logDebug("Sending " + serializable.getClass().getName() + " as request body");
- final String serializeObject = serializer.serializeObject(serializable);
+
+ String serializeObject = null;
+
+ if ("text/plain".equals(contenttype) && serializable instanceof String) {
+ serializeObject = (String)serializable;
+ } else {
+ serializeObject = serializer.serializeObject(serializable);
+ }
+
if(serializeObject == null) {
throw new ClientException("Error during serialization of request body, the result was null", null);
}
diff --git a/src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java b/src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java
index 9e4abf7d2..44632a60c 100644
--- a/src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java
+++ b/src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java
@@ -24,7 +24,7 @@ public class TelemetryHandler implements Interceptor{
/**
* Current SDK version
*/
- public static final String VERSION = "v2.0.7";
+ public static final String VERSION = "v2.0.8";
/**
* Verion prefix
*/
diff --git a/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java b/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java
index dd305b532..424503af1 100644
--- a/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java
+++ b/src/main/java/com/microsoft/graph/serializer/CollectionPageSerializer.java
@@ -24,7 +24,6 @@
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Type;
-import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
@@ -112,7 +111,7 @@ public static > BaseCollectionPage
final Class> responseClass = Class.forName(responseClassCanonicalName);
final JsonObject responseJson = new JsonObject();
responseJson.add("value", json);
- final BaseCollectionResponse response = CollectionResponseSerializer.deserialize(responseJson, responseClass, logger);
+ final BaseCollectionResponse response = CollectionResponseDeserializer.deserialize(responseJson, responseClass, logger);
/** eg: com.microsoft.graph.requests.AttachmentCollectionRequestBuilder */
final String responseBuilderCanonicalName = responseClassCanonicalName
.substring(0, responseClassCanonicalName.length() - responseLength) + "RequestBuilder";
diff --git a/src/main/java/com/microsoft/graph/serializer/CollectionResponseSerializer.java b/src/main/java/com/microsoft/graph/serializer/CollectionResponseDeserializer.java
similarity index 88%
rename from src/main/java/com/microsoft/graph/serializer/CollectionResponseSerializer.java
rename to src/main/java/com/microsoft/graph/serializer/CollectionResponseDeserializer.java
index 2f63fb66c..5b9c5c5a1 100644
--- a/src/main/java/com/microsoft/graph/serializer/CollectionResponseSerializer.java
+++ b/src/main/java/com/microsoft/graph/serializer/CollectionResponseDeserializer.java
@@ -37,13 +37,13 @@
import com.microsoft.graph.http.BaseCollectionResponse;
import com.microsoft.graph.logger.ILogger;
-/** Specialized serializer to handle collection responses */
-public class CollectionResponseSerializer {
+/** Specialized de-serializer to handle collection responses */
+public class CollectionResponseDeserializer {
private static DefaultSerializer serializer;
/**
* Not available for instantiation
*/
- private CollectionResponseSerializer() {}
+ private CollectionResponseDeserializer() {}
/**
* Deserializes the JsonElement
*
@@ -86,15 +86,7 @@ public static BaseCollectionResponse deserialize(@Nonnull final JsonEle
for(JsonElement sourceElement : sourceArray) {
if(sourceElement.isJsonObject()) {
final JsonObject sourceObject = sourceElement.getAsJsonObject();
- Class> entityClass = serializer.getDerivedClass(sourceObject, baseEntityClass);
- if(entityClass == null) {
- if(baseEntityClass == null) {
- logger.logError("Could not find target class for object " + sourceObject.toString(), null);
- continue;
- } else
- entityClass = baseEntityClass; // it is possible the odata type is absent or we can't find the derived type (not in SDK yet)
- }
- final T1 targetObject = (T1)serializer.deserializeObject(sourceObject, entityClass);
+ final T1 targetObject = (T1)serializer.deserializeObject(sourceObject, baseEntityClass);
((IJsonBackedObject)targetObject).setRawObject(serializer, sourceObject);
list.add(targetObject);
} else if (sourceElement.isJsonPrimitive()) {
diff --git a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java
index 2d9d5d2e8..99c31906e 100644
--- a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java
+++ b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java
@@ -22,12 +22,10 @@
package com.microsoft.graph.serializer;
-import com.google.common.base.CaseFormat;
import com.google.gson.Gson;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
-
import com.microsoft.graph.logger.ILogger;
import java.io.IOException;
@@ -38,9 +36,8 @@
import java.util.Iterator;
import java.util.List;
import java.util.Map;
-import java.util.Objects;
import java.util.Map.Entry;
-
+import java.util.Objects;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
@@ -48,17 +45,19 @@
* The default serializer implementation for the SDK
*/
public class DefaultSerializer implements ISerializer {
- private static final String graphResponseHeadersKey = "graphResponseHeaders";
+
+ private static final String GRAPH_RESPONSE_HEADERS_KEY = "graphResponseHeaders";
+
+ /**
+ * The logger
+ */
+ private final ILogger logger;
/**
* The instance of the internal serializer
*/
private final Gson gson;
- /**
- * The logger
- */
- private final ILogger logger;
/**
* Creates a DefaultSerializer
@@ -66,10 +65,26 @@ public class DefaultSerializer implements ISerializer {
* @param logger the logger
*/
public DefaultSerializer(@Nonnull final ILogger logger) {
- this.logger = Objects.requireNonNull(logger, "parameter logger cannot be null");
- this.gson = GsonFactory.getGsonInstance(logger);
+ this(logger, false);
}
+
+ /**
+ * Creates a DefaultSerializer with an option to enable serializing of the null values.
+ *
+ * Serializing of null values can have side effects on the service behavior.
+ * Sending null values in a PATCH request might reset existing values on the service side.
+ * Sending null values in a POST request might prevent the service from assigning default values to the properties.
+ * It is not recommended to send null values to the service in general and this setting should only be used when serializing information for a local store.
+ *
+ * @param logger the logger
+ * @param serializeNulls the setting of whether or not to serialize the null values in the JSON object
+ */
+ public DefaultSerializer(@Nonnull final ILogger logger, @Nonnull final boolean serializeNulls) {
+ this.logger = Objects.requireNonNull(logger, "parameter logger cannot be null");
+ this.gson = GsonFactory.getGsonInstance(logger, serializeNulls);
+ }
+
@Override
@Nullable
public T deserializeObject(@Nonnull final String inputString, @Nonnull final Class clazz, @Nullable final Map> responseHeaders) {
@@ -104,16 +119,7 @@ public T deserializeObject(@Nonnull final JsonElement rawElement, @Nonnull f
if (jsonObject instanceof IJsonBackedObject) {
logger.logDebug("Deserializing type " + clazz.getSimpleName());
final JsonObject rawObject = rawElement.isJsonObject() ? rawElement.getAsJsonObject() : null;
-
- // If there is a derived class, try to get it and deserialize to it
- T jo = jsonObject;
- if (rawElement.isJsonObject()) {
- final Class> derivedClass = this.getDerivedClass(rawObject, clazz);
- if (derivedClass != null)
- jo = (T) gson.fromJson(rawElement, derivedClass);
- }
-
- final IJsonBackedObject jsonBackedObject = (IJsonBackedObject) jo;
+ final IJsonBackedObject jsonBackedObject = (IJsonBackedObject) jsonObject;
if(rawElement.isJsonObject()) {
jsonBackedObject.setRawObject(this, rawObject);
@@ -123,9 +129,9 @@ public T deserializeObject(@Nonnull final JsonElement rawElement, @Nonnull f
if (responseHeaders != null) {
JsonElement convertedHeaders = gson.toJsonTree(responseHeaders);
- jsonBackedObject.additionalDataManager().put(graphResponseHeadersKey, convertedHeaders);
+ jsonBackedObject.additionalDataManager().put(GRAPH_RESPONSE_HEADERS_KEY, convertedHeaders);
}
- return jo;
+ return jsonObject;
} else {
logger.logDebug("Deserializing a non-IJsonBackedObject type " + clazz.getSimpleName());
return jsonObject;
@@ -304,52 +310,12 @@ private void addAdditionalDataFromJsonObjectToJson (final Object item, final Jso
*/
private void addAdditionalDataFromManagerToJson(AdditionalDataManager additionalDataManager, JsonObject jsonNode) {
for (Map.Entry entry : additionalDataManager.entrySet()) {
- if(!entry.getKey().equals(graphResponseHeadersKey)) {
+ if(!entry.getKey().equals(GRAPH_RESPONSE_HEADERS_KEY)) {
jsonNode.add(entry.getKey(), entry.getValue());
}
}
}
- private final static String ODATA_TYPE_KEY = "@odata.type";
- /**
- * Get the derived class for the given JSON object
- * This covers scenarios in which the service may return one of several derived types
- * of a base object, which it defines using the odata.type parameter
- *
- * @param jsonObject the raw JSON object of the response
- * @param parentClass the parent class the derived class should inherit from
- * @return the derived class if found, or null if not applicable
- */
- @Nullable
- public Class> getDerivedClass(@Nonnull final JsonObject jsonObject, @Nullable final Class> parentClass) {
- Objects.requireNonNull(jsonObject, "parameter jsonObject cannot be null");
- //Identify the odata.type information if provided
- if (jsonObject.get(ODATA_TYPE_KEY) != null) {
- /** #microsoft.graph.user or #microsoft.graph.callrecords.callrecord */
- final String odataType = jsonObject.get(ODATA_TYPE_KEY).getAsString();
- final int lastDotIndex = odataType.lastIndexOf(".");
- final String derivedType = (odataType.substring(0, lastDotIndex) +
- ".models." +
- CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
- odataType.substring(lastDotIndex + 1)))
- .replace("#", "com.");
- try {
- Class> derivedClass = Class.forName(derivedType);
- //Check that the derived class inherits from the given parent class
- if (parentClass == null || parentClass.isAssignableFrom(derivedClass)) {
- return derivedClass;
- }
- return null;
- } catch (ClassNotFoundException e) {
- logger.logDebug("Unable to find a corresponding class for derived type " + derivedType + ". Falling back to parent class.");
- //If we cannot determine the derived type to cast to, return null
- //This may happen if the API and the SDK are out of sync
- return null;
- }
- }
- //If there is no defined OData type, return null
- return null;
- }
/**
* Gets the logger in use
diff --git a/src/main/java/com/microsoft/graph/serializer/DerivedClassIdentifier.java b/src/main/java/com/microsoft/graph/serializer/DerivedClassIdentifier.java
new file mode 100644
index 000000000..e01346a02
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/serializer/DerivedClassIdentifier.java
@@ -0,0 +1,64 @@
+package com.microsoft.graph.serializer;
+
+import com.google.common.base.CaseFormat;
+import com.google.gson.JsonObject;
+import com.microsoft.graph.logger.ILogger;
+
+import java.util.Objects;
+import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
+
+/** This class provides methods to get the derived class corresponding to the OData type when deserializing payloads. */
+public class DerivedClassIdentifier {
+
+ private final static String ODATA_TYPE_KEY = "@odata.type";
+
+ private final ILogger logger;
+ /**
+ * Creates a new instance of the dereived class identifier.
+ * @param logger The logger to use.
+ */
+ public DerivedClassIdentifier(@Nonnull ILogger logger) {
+ this.logger = Objects.requireNonNull(logger, "logger parameter cannot be null");;
+ }
+
+ /**
+ * Get the derived class for the given JSON object
+ * This covers scenarios in which the service may return one of several derived types
+ * of a base object, which it defines using the odata.type parameter
+ *
+ * @param jsonObject the raw JSON object of the response
+ * @param parentClass the parent class the derived class should inherit from
+ * @return the derived class if found, or null if not applicable
+ */
+ @Nullable
+ public Class> identify(@Nonnull final JsonObject jsonObject, @Nullable final Class> parentClass) {
+ Objects.requireNonNull(jsonObject, "parameter jsonObject cannot be null");
+ //Identify the odata.type information if provided
+ if (jsonObject.get(ODATA_TYPE_KEY) != null) {
+ /** #microsoft.graph.user or #microsoft.graph.callrecords.callrecord */
+ final String odataType = jsonObject.get(ODATA_TYPE_KEY).getAsString();
+ final int lastDotIndex = odataType.lastIndexOf(".");
+ final String derivedType = (odataType.substring(0, lastDotIndex) +
+ ".models." +
+ CaseFormat.LOWER_CAMEL.to(CaseFormat.UPPER_CAMEL,
+ odataType.substring(lastDotIndex + 1)))
+ .replace("#", "com.");
+ try {
+ Class> derivedClass = Class.forName(derivedType);
+ //Check that the derived class inherits from the given parent class
+ if (parentClass == null || parentClass.isAssignableFrom(derivedClass)) {
+ return derivedClass;
+ }
+ return null;
+ } catch (ClassNotFoundException e) {
+ logger.logDebug("Unable to find a corresponding class for derived type " + derivedType + ". Falling back to parent class.");
+ //If we cannot determine the derived type to cast to, return null
+ //This may happen if the API and the SDK are out of sync
+ return null;
+ }
+ }
+ //If there is no defined OData type, return null
+ return null;
+ }
+}
diff --git a/src/main/java/com/microsoft/graph/serializer/FallbackTypeAdapterFactory.java b/src/main/java/com/microsoft/graph/serializer/FallbackTypeAdapterFactory.java
index 9c439c65d..849e8750b 100644
--- a/src/main/java/com/microsoft/graph/serializer/FallbackTypeAdapterFactory.java
+++ b/src/main/java/com/microsoft/graph/serializer/FallbackTypeAdapterFactory.java
@@ -22,23 +22,23 @@
package com.microsoft.graph.serializer;
-import java.io.IOException;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.Objects;
-
import com.google.common.base.CaseFormat;
import com.google.gson.Gson;
import com.google.gson.TypeAdapter;
import com.google.gson.TypeAdapterFactory;
+import com.google.gson.internal.bind.ReflectiveTypeAdapterFactory;
import com.google.gson.reflect.TypeToken;
import com.google.gson.stream.JsonReader;
import com.google.gson.stream.JsonToken;
import com.google.gson.stream.JsonWriter;
import com.microsoft.graph.logger.ILogger;
-import javax.annotation.Nullable;
+import java.io.IOException;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.Objects;
import javax.annotation.Nonnull;
+import javax.annotation.Nullable;
/**
* Handles serialization/deserialization for special types (especially of
@@ -67,14 +67,14 @@ public void write(JsonWriter out, Void value) throws IOException {
}
@Override
- public Void read(JsonReader in) throws IOException {
+ public Void read(JsonReader in) {
return null;
}
};
/**
- * Instanciates a new type adapter factory
+ * Instantiates a new type adapter factory
*
* @param logger logger to use for the factory
*/
@@ -89,10 +89,21 @@ public FallbackTypeAdapterFactory(@Nonnull final ILogger logger) {
public TypeAdapter create(@Nonnull final Gson gson, @Nonnull final TypeToken type) {
Objects.requireNonNull(type, "parameter type cannot be null");
final Class rawType = (Class) type.getRawType();
+
if (rawType.isEnum()) {
- return new EnumTypeAdapter(rawType, logger);
+ return new EnumTypeAdapter<>(rawType, logger);
} else if (rawType == Void.class) {
return (TypeAdapter) voidAdapter;
+ } else if (IJsonBackedObject.class.isAssignableFrom(type.getRawType())) {
+
+ final TypeAdapter delegatedAdapter = (TypeAdapter) gson.getDelegateAdapter(this, type);
+
+ // Avoid overriding custom IJsonBackedObject type adapters defined in GsonFactory
+ if (!(delegatedAdapter instanceof ReflectiveTypeAdapterFactory.Adapter)) {
+ return null;
+ }
+
+ return (TypeAdapter) new ODataTypeParametrizedIJsonBackedTypedAdapter(this, gson, delegatedAdapter, (TypeToken) type, logger);
}
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 4a040cc99..4b305e5ae 100644
--- a/src/main/java/com/microsoft/graph/serializer/GsonFactory.java
+++ b/src/main/java/com/microsoft/graph/serializer/GsonFactory.java
@@ -44,8 +44,10 @@
import java.util.Arrays;
import java.util.EnumSet;
import java.util.GregorianCalendar;
+import java.util.Objects;
import java.util.UUID;
+import javax.annotation.Nonnull;
import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;
@@ -68,8 +70,26 @@ private GsonFactory() {
* @param logger the logger
* @return the new instance
*/
- public static Gson getGsonInstance(final ILogger logger) {
+ @Nonnull
+ public static Gson getGsonInstance(@Nonnull final ILogger logger) {
+ return getGsonInstance(logger, false);
+ }
+ /**
+ * Creates an instance of GSON
+ *
+ * Serializing of null values can have side effects on the service behavior.
+ * Sending null values in a PATCH request might reset existing values on the service side.
+ * Sending null values in a POST request might prevent the service from assigning default values to the properties.
+ * It is not recommended to send null values to the service in general and this setting should only be used when serializing information for a local store.
+ *
+ * @param logger the logger
+ * @param serializeNulls the setting of whether or not to serialize the null values in the JSON object
+ * @return the new instance
+ */
+ @Nonnull
+ public static Gson getGsonInstance(@Nonnull final ILogger logger, final boolean serializeNulls) {
+ Objects.requireNonNull(logger, "parameter logger cannot be null");
final JsonSerializer calendarJsonSerializer = new JsonSerializer() {
@Override
public JsonElement serialize(final OffsetDateTime src,
@@ -239,7 +259,7 @@ public JsonElement serialize(final BaseCollectionPage, ?> src,
public BaseCollectionResponse> deserialize(final JsonElement json,
final Type typeOfT,
final JsonDeserializationContext context) throws JsonParseException {
- return CollectionResponseSerializer.deserialize(json, typeOfT, logger);
+ return CollectionResponseDeserializer.deserialize(json, typeOfT, logger);
}
};
@@ -328,7 +348,11 @@ public Float deserialize(final JsonElement json,
}
};
- return new GsonBuilder()
+ GsonBuilder builder = new GsonBuilder();
+ if (serializeNulls) {
+ builder.serializeNulls();
+ }
+ return builder
.excludeFieldsWithoutExposeAnnotation()
.registerTypeAdapter(Boolean.class, booleanJsonDeserializer)
.registerTypeAdapter(String.class, stringJsonDeserializer)
diff --git a/src/main/java/com/microsoft/graph/serializer/ODataTypeParametrizedIJsonBackedTypedAdapter.java b/src/main/java/com/microsoft/graph/serializer/ODataTypeParametrizedIJsonBackedTypedAdapter.java
new file mode 100644
index 000000000..4b2620d5b
--- /dev/null
+++ b/src/main/java/com/microsoft/graph/serializer/ODataTypeParametrizedIJsonBackedTypedAdapter.java
@@ -0,0 +1,63 @@
+package com.microsoft.graph.serializer;
+
+import com.google.gson.Gson;
+import com.google.gson.JsonElement;
+import com.google.gson.TypeAdapter;
+import com.google.gson.internal.Streams;
+import com.google.gson.reflect.TypeToken;
+import com.google.gson.stream.JsonReader;
+import com.google.gson.stream.JsonWriter;
+import com.microsoft.graph.logger.ILogger;
+
+import java.io.IOException;
+import java.util.Objects;
+import javax.annotation.Nonnull;
+
+/**
+ * This adapter is responsible for deserialization of IJsonBackedObjects where service
+ * returns one of several derived types of a base object, which is defined using the
+ * odata.type parameter. If odata.type parameter is not found, the Gson default
+ * (delegated) type adapter is used.
+ */
+class ODataTypeParametrizedIJsonBackedTypedAdapter extends TypeAdapter {
+
+ private final FallbackTypeAdapterFactory fallbackTypeAdapterFactory;
+ private final Gson gson;
+ private final TypeAdapter delegatedAdapter;
+ private final TypeToken type;
+ private final DerivedClassIdentifier derivedClassIdentifier;
+
+ public ODataTypeParametrizedIJsonBackedTypedAdapter(FallbackTypeAdapterFactory fallbackTypeAdapterFactory, @Nonnull Gson gson,
+ @Nonnull TypeAdapter delegatedAdapter, @Nonnull final TypeToken type, @Nonnull final ILogger logger)
+ {
+ super();
+ this.fallbackTypeAdapterFactory = fallbackTypeAdapterFactory;
+ this.gson = Objects.requireNonNull(gson, "parameter gson cannot be null");
+ this.delegatedAdapter = Objects.requireNonNull(delegatedAdapter, "object delegated adapted cannot be null");
+ this.type = Objects.requireNonNull(type, "object type cannot be null");
+ this.derivedClassIdentifier = new DerivedClassIdentifier(logger);
+ }
+
+ @Override
+ public void write(JsonWriter out, IJsonBackedObject value)
+ throws IOException
+ {
+ this.delegatedAdapter.write(out, value);
+ }
+
+ @Override
+ public IJsonBackedObject read(JsonReader in) {
+ JsonElement jsonElement = Streams.parse(in);
+
+ if (jsonElement.isJsonObject()) {
+ final Class> derivedClass = derivedClassIdentifier.identify(jsonElement.getAsJsonObject(), type.getRawType());
+
+ if (derivedClass != null) {
+ final TypeAdapter> subTypeAdapter = gson.getDelegateAdapter(fallbackTypeAdapterFactory, TypeToken.get(derivedClass));
+ return (IJsonBackedObject) subTypeAdapter.fromJsonTree(jsonElement);
+ }
+ }
+
+ return delegatedAdapter.fromJsonTree(jsonElement);
+ }
+}
diff --git a/src/test/java/com/microsoft/graph/http/CoreHttpProviderTests.java b/src/test/java/com/microsoft/graph/http/CoreHttpProviderTests.java
index 001d9a080..43b8ebe76 100644
--- a/src/test/java/com/microsoft/graph/http/CoreHttpProviderTests.java
+++ b/src/test/java/com/microsoft/graph/http/CoreHttpProviderTests.java
@@ -20,6 +20,7 @@
import com.microsoft.graph.serializer.DefaultSerializer;
import com.microsoft.graph.serializer.ISerializer;
+import okio.Buffer;
import org.junit.jupiter.api.Test;
import java.io.ByteArrayInputStream;
@@ -39,6 +40,8 @@
import okhttp3.Request;
import okhttp3.Response;
import okhttp3.ResponseBody;
+import okhttp3.RequestBody;
+import org.mockito.Mockito;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -49,15 +52,17 @@
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.never;
+import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-public class CoreHttpProviderTests {
+class CoreHttpProviderTests {
private CoreHttpProvider mProvider;
private Gson GSON = new GsonBuilder().create();
@Test
- public void testErrorResponse() throws Exception {
+ void testErrorResponse() throws Exception {
final GraphErrorCodes expectedErrorCode = GraphErrorCodes.INVALID_REQUEST;
final String expectedMessage = "Test error!";
final GraphErrorResponse toSerialize = new GraphErrorResponse();
@@ -80,7 +85,7 @@ public void testErrorResponse() throws Exception {
}
@Test
- public void testVerboseErrorResponse() throws Exception {
+ void testVerboseErrorResponse() throws Exception {
final GraphErrorCodes expectedErrorCode = GraphErrorCodes.INVALID_REQUEST;
final String expectedMessage = "Test error!";
final GraphErrorResponse toSerialize = new GraphErrorResponse();
@@ -114,25 +119,25 @@ public void testVerboseErrorResponse() throws Exception {
}
@Test
- public void testHasHeaderReturnsTrue() {
+ void testHasHeaderReturnsTrue() {
HeaderOption h = new HeaderOption("name", "value");
assertTrue(CoreHttpProvider.hasHeader(Arrays.asList(h), "name"));
}
@Test
- public void testHasHeaderReturnsTrueWhenDifferentCase() {
+ void testHasHeaderReturnsTrueWhenDifferentCase() {
HeaderOption h = new HeaderOption("name", "value");
assertTrue(CoreHttpProvider.hasHeader(Arrays.asList(h), "NAME"));
}
@Test
- public void testHasHeaderReturnsFalse() {
+ void testHasHeaderReturnsFalse() {
HeaderOption h = new HeaderOption("name", "value");
assertFalse(CoreHttpProvider.hasHeader(Arrays.asList(h), "blah"));
}
@Test
- public void testStreamToStringReturnsData() {
+ void testStreamToStringReturnsData() {
String data = GSON.toJson(Maps.newHashMap(
ImmutableMap.builder()
.put("key", "value")
@@ -144,14 +149,14 @@ public void testStreamToStringReturnsData() {
}
@Test
- public void testStreamToStringReturnsEmpty() {
+ void testStreamToStringReturnsEmpty() {
final InputStream inputStream = new ByteArrayInputStream(new byte[0]);
String convertedData = CoreHttpProvider.streamToString(inputStream);
assertEquals("", convertedData);
}
@Test
- public void emptyPostContentTypeIsNotReset() {
+ void emptyPostContentTypeIsNotReset() {
final String contentTypeValue = "application/json";
final HeaderOption ctype = new HeaderOption("Content-Type", contentTypeValue);
final ArrayList