diff --git a/.github/workflows/api-level-lint.yml b/.github/workflows/api-level-lint.yml index cf0413555..fa711e65a 100644 --- a/.github/workflows/api-level-lint.yml +++ b/.github/workflows/api-level-lint.yml @@ -1,6 +1,7 @@ name: "Checks the SDK only using APIs from the targeted API level" -on: +on: + workflow_dispatch: push: branches: - dev diff --git a/.github/workflows/codeql-analysis.yml b/.github/workflows/codeql-analysis.yml index 7d984b319..fc68b709f 100644 --- a/.github/workflows/codeql-analysis.yml +++ b/.github/workflows/codeql-analysis.yml @@ -1,6 +1,7 @@ name: "CodeQL" on: + workflow_dispatch: push: branches: [dev, master] pull_request: diff --git a/.github/workflows/conflicting-pr-label.yml b/.github/workflows/conflicting-pr-label.yml index 135603af1..c8ba2b21b 100644 --- a/.github/workflows/conflicting-pr-label.yml +++ b/.github/workflows/conflicting-pr-label.yml @@ -5,6 +5,7 @@ name: PullRequestConflicting # Controls when the action will run. Triggers the workflow on push or pull request # events but only for the master branch on: + workflow_dispatch: push: branches: [ master, dev ] pull_request: diff --git a/.github/workflows/sample-build-check.yml b/.github/workflows/sample-build-check.yml index be4146069..397ee1662 100644 --- a/.github/workflows/sample-build-check.yml +++ b/.github/workflows/sample-build-check.yml @@ -1,6 +1,7 @@ name: "samples build check" on: + workflow_dispatch: push: paths: - '**/*.java' diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml index 4bf6cde65..4e7b8921d 100644 --- a/.github/workflows/sonarcloud.yml +++ b/.github/workflows/sonarcloud.yml @@ -1,5 +1,6 @@ name: Static analysis with SonarCloud on: + workflow_dispatch: push: branches: - master diff --git a/build.gradle b/build.gradle index 504846f20..e695699f5 100644 --- a/build.gradle +++ b/build.gradle @@ -15,7 +15,7 @@ plugins { id 'signing' id 'jacoco' id 'com.github.spotbugs' version '4.7.1' - id "org.sonarqube" version "3.2.0" + id "org.sonarqube" version "3.3" } java { @@ -91,8 +91,8 @@ def pomConfig = { sonarqube { properties { - property "sonar.projectKey", "msgraph-sdk-java-core" - property "sonar.organization", "microsoftgraph" + property "sonar.projectKey", "microsoftgraph_msgraph-sdk-java-core" + property "sonar.organization", "microsoftgraph2" property "sonar.host.url", "https://sonarcloud.io" } } diff --git a/gradle.properties b/gradle.properties index 2e7cee095..6663f2b4c 100644 --- a/gradle.properties +++ b/gradle.properties @@ -25,7 +25,7 @@ mavenGroupId = com.microsoft.graph mavenArtifactId = microsoft-graph-core mavenMajorVersion = 2 mavenMinorVersion = 0 -mavenPatchVersion = 4 +mavenPatchVersion = 5 mavenArtifactSuffix = #These values are used to run functional tests diff --git a/gradle/dependencies.gradle b/gradle/dependencies.gradle index fac6a9c11..38c853013 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.10.0' + testImplementation 'org.mockito:mockito-inline:3.11.1' 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.16.0' + api 'com.azure:azure-core:1.17.0' } \ No newline at end of file diff --git a/pom.xml b/pom.xml index 7984c278d..f0e1d0f8c 100644 --- a/pom.xml +++ b/pom.xml @@ -35,7 +35,7 @@ com.azure azure-core - 1.16.0 + 1.17.0 org.junit.jupiter @@ -46,7 +46,7 @@ org.mockito mockito-inline - 3.10.0 + 3.11.1 test diff --git a/readme.md b/readme.md index 236c359b5..9da26ba4c 100644 --- a/readme.md +++ b/readme.md @@ -22,9 +22,9 @@ repositories { dependencies { // Include the sdk as a dependency - implementation 'com.microsoft.graph:microsoft-graph-core:2.0.4' + implementation 'com.microsoft.graph:microsoft-graph-core:2.0.5' // This dependency is only needed if you are using the TokenCrendentialAuthProvider - implementation 'com.azure:azure-identity:1.2.5' + implementation 'com.azure:azure-identity:1.3.1' } ``` @@ -37,11 +37,11 @@ Add the dependency in `dependencies` in pom.xml com.microsoft.graph microsoft-graph-core - 2.0.4 + 2.0.5 com.azure azure-identity - 1.2.5 + 1.3.1 ``` diff --git a/src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java b/src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java index ddc7ccdcf..40072fc03 100644 --- a/src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java +++ b/src/main/java/com/microsoft/graph/httpcore/TelemetryHandler.java @@ -25,7 +25,7 @@ public class TelemetryHandler implements Interceptor{ /** * Current SDK version */ - public static final String VERSION = "v2.0.4"; + public static final String VERSION = "v2.0.5"; /** * Verion prefix */ diff --git a/src/main/java/com/microsoft/graph/serializer/AdditionalDataManager.java b/src/main/java/com/microsoft/graph/serializer/AdditionalDataManager.java index 456721cdf..24489c638 100644 --- a/src/main/java/com/microsoft/graph/serializer/AdditionalDataManager.java +++ b/src/main/java/com/microsoft/graph/serializer/AdditionalDataManager.java @@ -1,16 +1,16 @@ // ------------------------------------------------------------------------------ // Copyright (c) 2017 Microsoft Corporation -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sub-license, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in // all copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -38,12 +38,12 @@ public class AdditionalDataManager extends HashMap { private static final long serialVersionUID = 8641634955796941429L; - + private final transient IJsonBackedObject jsonBackedObject; /** * Instanciates a new additional data manager from the json backed object - * + * * @param jsonBackedObject the object to read values from */ public AdditionalDataManager(@Nullable final IJsonBackedObject jsonBackedObject) { @@ -56,15 +56,15 @@ public AdditionalDataManager(@Nullable final IJsonBackedObject jsonBackedObject) * * @param json the raw JSON to set as additionalData */ - final void setAdditionalData(JsonObject json) { + final void setAdditionalData(final JsonObject json) { // Get the names of all the fields on this object's hierarchy - Set objectFields = getFields(); + final Set objectFields = getFields(); // Get the keys on this JSON - Set jsonKeys = getJsonKeys(json); + final Set jsonKeys = getJsonKeys(json); // Get all keys present in JSON and *NOT* present in fields - Set additionalDataKeys = new HashSet<>(jsonKeys); + final Set additionalDataKeys = new HashSet<>(jsonKeys); additionalDataKeys.removeAll(objectFields); // set the additionalData @@ -73,9 +73,9 @@ final void setAdditionalData(JsonObject json) { } } - private Set getJsonKeys(JsonObject json) { - Set keys = new HashSet<>(); - Set> entries = json.entrySet(); + private Set getJsonKeys(final JsonObject json) { + final Set keys = new HashSet<>(); + final Set> entries = json.entrySet(); for (Map.Entry entry : entries) { keys.add(entry.getKey()); } @@ -83,13 +83,15 @@ private Set getJsonKeys(JsonObject json) { } private Set getFields() { - Field[] fields = jsonBackedObject.getClass().getFields(); - Set serializingFields = new HashSet<>(); - for (Field field : fields) { - SerializedName serializedName; - if (null != (serializedName = field.getAnnotation(SerializedName.class)) - && null != field.getAnnotation(Expose.class)) { - serializingFields.add(serializedName.value()); + final Set serializingFields = new HashSet<>(); + if(jsonBackedObject != null ) { + final Field[] fields = jsonBackedObject.getClass().getFields(); + for (Field field : fields) { + final SerializedName serializedName = field.getAnnotation(SerializedName.class); + if (null != serializedName + && null != field.getAnnotation(Expose.class)) { + serializingFields.add(serializedName.value()); + } } } return serializingFields; diff --git a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java index ec9f7999c..54a7d24ed 100644 --- a/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java +++ b/src/main/java/com/microsoft/graph/serializer/DefaultSerializer.java @@ -223,50 +223,13 @@ else if (fieldObject instanceof IJsonBackedObject) { public String serializeObject(@Nonnull final T serializableObject) { Objects.requireNonNull(serializableObject, "parameter serializableObject cannot be null"); logger.logDebug("Serializing type " + serializableObject.getClass().getSimpleName()); - JsonElement outJsonTree = gson.toJsonTree(serializableObject); - - if (serializableObject instanceof IJsonBackedObject) { - outJsonTree = getDataFromAdditionalDataManager(outJsonTree, serializableObject); - } else if (outJsonTree.isJsonObject()) { - final Field[] fields = serializableObject.getClass().getDeclaredFields(); - JsonObject outJson = outJsonTree.getAsJsonObject(); - for(Field field : fields) { - if(outJson.has(field.getName())) { - final Type[] interfaces = field.getType().getGenericInterfaces(); - for(Type interfaceType : interfaces) { - if(interfaceType == IJsonBackedObject.class && outJson.get(field.getName()).isJsonObject()) { - try { - final JsonElement outdatedValue = outJson.remove(field.getName()); - outJson.add(field.getName(), getDataFromAdditionalDataManager(outdatedValue.getAsJsonObject(), field.get(serializableObject))); - } catch (IllegalAccessException ex ) { - logger.logDebug("Couldn't access prop" + field.getName()); - } - break; - } - } - } - } - } - - return outJsonTree.toString(); - } - private JsonElement getDataFromAdditionalDataManager(JsonElement outJsonTree, final T serializableObject) { - final IJsonBackedObject serializableJsonObject = (IJsonBackedObject) serializableObject; - final AdditionalDataManager additionalData = serializableJsonObject.additionalDataManager(); - - // If the item is a valid Graph object, add its additional data - if (outJsonTree.isJsonObject()) { - final JsonObject outJson = outJsonTree.getAsJsonObject(); - - addAdditionalDataFromManagerToJson(additionalData, outJson); - getChildAdditionalData(serializableJsonObject, outJson); - - return outJson; - } else { - return outJsonTree; + final JsonElement outJsonTree = gson.toJsonTree(serializableObject); + if(outJsonTree != null) { + getChildAdditionalData(serializableObject, outJsonTree); + return outJsonTree.toString(); } + return ""; } - /** * Recursively populates additional data for each child object * @@ -274,58 +237,60 @@ private JsonElement getDataFromAdditionalDataManager(JsonElement outJsonTree * @param outJson the serialized output JSON to add to */ @SuppressWarnings("unchecked") - private void getChildAdditionalData(final IJsonBackedObject serializableObject, final JsonObject outJson) { - if(outJson == null) - return; - // Use reflection to iterate through fields for eligible Graph children - for (java.lang.reflect.Field field : serializableObject.getClass().getFields()) { - try { - final Object fieldObject = field.get(serializableObject); - final JsonElement fieldJsonElement = outJson.get(field.getName()); - if(fieldObject == null || fieldJsonElement == null) - continue; - - // If the object is a HashMap, iterate through its children - if (fieldObject instanceof Map && fieldJsonElement.isJsonObject()) { - final Map serializableChildren = (Map) fieldObject; - final Iterator> it = serializableChildren.entrySet().iterator(); - final JsonObject fieldJsonObject = fieldJsonElement.getAsJsonObject(); - - while (it.hasNext()) { - final Map.Entry pair = (Map.Entry)it.next(); - final Object child = pair.getValue(); - final JsonElement childJsonElement = fieldJsonObject.get(pair.getKey().toString()); - // If the item is a valid Graph object, add its additional data - addAdditionalDataFromJsonElementToJson(child, childJsonElement); - } - } - // If the object is a list of Graph objects, iterate through elements - else if (fieldObject instanceof List && fieldJsonElement.isJsonArray()) { - final JsonArray fieldArrayValue = fieldJsonElement.getAsJsonArray(); - final List fieldObjectList = (List) fieldObject; - for (int index = 0; index < fieldObjectList.size(); index++) { - final Object item = fieldObjectList.get(index); - final JsonElement itemJsonElement = fieldArrayValue.get(index); - addAdditionalDataFromJsonElementToJson(item, itemJsonElement); - } - } - // If the object is a valid Graph object, add its additional data - addAdditionalDataFromJsonElementToJson(fieldObject, fieldJsonElement); - } catch (IllegalArgumentException | IllegalAccessException e) { - logger.logError("Unable to access child fields of " + serializableObject.getClass().getSimpleName(), e); - } - } - } + private void getChildAdditionalData(final Object serializableObject, final JsonElement outJson) { + if(outJson == null || serializableObject == null || !outJson.isJsonObject()) + return; + final JsonObject outJsonObject = outJson.getAsJsonObject(); + // Use reflection to iterate through fields for eligible Graph children + for (java.lang.reflect.Field field : serializableObject.getClass().getFields()) { + try { + final Object fieldObject = field.get(serializableObject); + final JsonElement fieldJsonElement = outJsonObject.get(field.getName()); + if(fieldObject == null || fieldJsonElement == null) + continue; + + // If the object is a HashMap, iterate through its children + if (fieldObject instanceof Map && fieldJsonElement.isJsonObject()) { + final Map serializableChildren = (Map) fieldObject; + final Iterator> it = serializableChildren.entrySet().iterator(); + final JsonObject fieldJsonObject = fieldJsonElement.getAsJsonObject(); + + while (it.hasNext()) { + final Map.Entry pair = (Map.Entry)it.next(); + final Object child = pair.getValue(); + final JsonElement childJsonElement = fieldJsonObject.get(pair.getKey().toString()); + // If the item is a valid Graph object, add its additional data + getChildAdditionalData(child, childJsonElement); + } + } + // If the object is a list of Graph objects, iterate through elements + else if (fieldObject instanceof List && fieldJsonElement.isJsonArray()) { + final JsonArray fieldArrayValue = fieldJsonElement.getAsJsonArray(); + final List fieldObjectList = (List) fieldObject; + for (int index = 0; index < fieldObjectList.size(); index++) { + final Object item = fieldObjectList.get(index); + final JsonElement itemJsonElement = fieldArrayValue.get(index); + getChildAdditionalData(item, itemJsonElement); + } + } else if(fieldJsonElement.isJsonObject()) { + // If the object is a valid Graph object, add its additional data + final JsonObject fieldJsonObject = fieldJsonElement.getAsJsonObject(); + addAdditionalDataFromJsonObjectToJson(fieldObject, fieldJsonObject); + } + } catch (IllegalArgumentException | IllegalAccessException e) { + logger.logError("Unable to access child fields of " + serializableObject.getClass().getSimpleName(), e); + } + } + } /** * Add each non-transient additional data property to the given JSON node * * @param item the object containing additional data - * @param itemJsonElement the JSON node to add the additional data properties to + * @param itemJsonObject the JSON node to add the additional data properties to */ - private void addAdditionalDataFromJsonElementToJson (final Object item, final JsonElement itemJsonElement) { - if (item instanceof IJsonBackedObject && itemJsonElement.isJsonObject()) { - final JsonObject itemJsonObject = itemJsonElement.getAsJsonObject(); + private void addAdditionalDataFromJsonObjectToJson (final Object item, final JsonObject itemJsonObject) { + if (item instanceof IJsonBackedObject && itemJsonObject != null) { final IJsonBackedObject serializableItem = (IJsonBackedObject) item; final AdditionalDataManager itemAdditionalData = serializableItem.additionalDataManager(); addAdditionalDataFromManagerToJson(itemAdditionalData, itemJsonObject); diff --git a/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java b/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java index e6aa2a98d..176f16f4f 100644 --- a/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java +++ b/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java @@ -4,6 +4,7 @@ import static org.junit.jupiter.api.Assertions.assertNotNull; import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.any; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; @@ -14,6 +15,7 @@ import java.util.ArrayList; import com.google.gson.JsonObject; +import com.google.gson.JsonPrimitive; import com.microsoft.graph.authentication.IAuthenticationProvider; import com.microsoft.graph.core.BaseClient; import com.microsoft.graph.core.IBaseClient; @@ -37,12 +39,12 @@ import okhttp3.Response; import okhttp3.ResponseBody; -public class BatchRequestContentTest { +class BatchRequestContentTest { String testurl = "http://graph.microsoft.com/me"; @Test - public void testBatchRequestContentCreation() throws MalformedURLException { + void testBatchRequestContentCreation() throws MalformedURLException { BatchRequestContent requestContent = new BatchRequestContent(); for (int i = 0; i < 5; i++) { IHttpRequest requestStep = mock(IHttpRequest.class); @@ -53,7 +55,7 @@ public void testBatchRequestContentCreation() throws MalformedURLException { } @Test - public void testGetBatchRequestContent() throws MalformedURLException { + void testGetBatchRequestContent() throws MalformedURLException { IHttpRequest requestStep = mock(IHttpRequest.class); when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); BatchRequestContent requestContent = new BatchRequestContent(); @@ -65,7 +67,7 @@ public void testGetBatchRequestContent() throws MalformedURLException { } @Test - public void testGetBatchRequestContentWithHeader() throws MalformedURLException { + void testGetBatchRequestContentWithHeader() throws MalformedURLException { IHttpRequest requestStep = mock(IHttpRequest.class); when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); when(requestStep.getHeaders()).thenReturn(Arrays.asList(new HeaderOption("testkey", "testvalue"))); @@ -78,7 +80,7 @@ public void testGetBatchRequestContentWithHeader() throws MalformedURLException } @Test - public void testRemoveBatchRequesStepWithId() throws MalformedURLException { + void testRemoveBatchRequesStepWithId() throws MalformedURLException { IHttpRequest requestStep = mock(IHttpRequest.class); when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); BatchRequestContent requestContent = new BatchRequestContent(); @@ -90,7 +92,7 @@ public void testRemoveBatchRequesStepWithId() throws MalformedURLException { } @Test - public void testRemoveBatchRequesStepWithIdByAddingMultipleBatchSteps() throws MalformedURLException { + void testRemoveBatchRequesStepWithIdByAddingMultipleBatchSteps() throws MalformedURLException { IHttpRequest requestStep = mock(IHttpRequest.class); when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); BatchRequestContent requestContent = new BatchRequestContent(); @@ -109,7 +111,7 @@ public void testRemoveBatchRequesStepWithIdByAddingMultipleBatchSteps() throws M } @Test - public void defensiveProgrammingTests() { + void defensiveProgrammingTests() { assertThrows(NullPointerException.class, () -> { new BatchRequestContent().addBatchRequestStep(null); }, "should throw argument exception"); @@ -135,7 +137,7 @@ public void defensiveProgrammingTests() { } @Test - public void executeBatchTest() throws Throwable { + void executeBatchTest() throws Throwable { final BatchRequestContent content = new BatchRequestContent(); IHttpRequest requestStep = mock(IHttpRequest.class); when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); @@ -164,7 +166,7 @@ public void executeBatchTest() throws Throwable { } @Test - public void usesHttpMethodFromRequestIfAlreadySet() throws MalformedURLException { + void usesHttpMethodFromRequestIfAlreadySet() throws MalformedURLException { IHttpRequest requestStep = mock(IHttpRequest.class); when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); when(requestStep.getHttpMethod()).thenReturn(HttpMethod.DELETE); @@ -174,13 +176,14 @@ public void usesHttpMethodFromRequestIfAlreadySet() throws MalformedURLException } @Test - public void doesNotThrowWhenTryingToRemoveRequestFromNull() { + void doesNotThrowWhenTryingToRemoveRequestFromNull() { final BatchRequestContent batchRequest = new BatchRequestContent(); batchRequest.removeBatchRequestStepWithId("id"); + assertNull(batchRequest.requests); } @Test - public void doesNotRemoveDependsOnWhenNotEmpty() throws MalformedURLException { + void doesNotRemoveDependsOnWhenNotEmpty() throws MalformedURLException { IHttpRequest requestStep = mock(IHttpRequest.class); when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); final BatchRequestContent batchRequest = new BatchRequestContent(); @@ -194,7 +197,7 @@ public void doesNotRemoveDependsOnWhenNotEmpty() throws MalformedURLException { } @Test - public void addsContentTypeForBodies() throws MalformedURLException { + void addsContentTypeForBodies() throws MalformedURLException { IHttpRequest requestStep = mock(IHttpRequest.class); when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); final BatchRequestContent batchRequest = new BatchRequestContent(); @@ -235,4 +238,18 @@ public AdditionalDataManager additionalDataManager() { assertNotNull(step4.headers); assertEquals("application/octet-stream", step4.headers.get("content-type")); } + @Test + void serializesAdditionalData() throws MalformedURLException { + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + final BatchRequestContent batchRequest = new BatchRequestContent(); + final String bindValue = "https://somebindvalue"; + final BatchRequestTestBody body = new BatchRequestTestBody(); // using a dynamic implementation doesn't work as "this" maps to the current test class + body.additionalDataManager().put("teamsApp@odata.bind", new JsonPrimitive(bindValue)); + batchRequest.addBatchRequestStep(requestStep, HttpMethod.POST, body); + final ISerializer serializer = new DefaultSerializer(mock(ILogger.class)); + final String result = serializer.serializeObject(batchRequest); + assertNotNull(result); + assertTrue(result.contains(bindValue)); + } } diff --git a/src/test/java/com/microsoft/graph/content/BatchRequestTestBody.java b/src/test/java/com/microsoft/graph/content/BatchRequestTestBody.java new file mode 100644 index 000000000..63f7d8dca --- /dev/null +++ b/src/test/java/com/microsoft/graph/content/BatchRequestTestBody.java @@ -0,0 +1,30 @@ +package com.microsoft.graph.content; + +import javax.annotation.Nullable; + +import com.google.gson.JsonObject; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.microsoft.graph.serializer.AdditionalDataManager; +import com.microsoft.graph.serializer.IJsonBackedObject; +import com.microsoft.graph.serializer.ISerializer; + +public class BatchRequestTestBody implements IJsonBackedObject { + @Expose + @Nullable + @SerializedName("id") + public String id; + + @Override + public void setRawObject(ISerializer serializer, JsonObject json) { + // TODO Auto-generated method stub + + } + final AdditionalDataManager manager = new AdditionalDataManager(this); + + @Override + public AdditionalDataManager additionalDataManager() { + return manager; + } + +}