diff --git a/src/main/java/com/microsoft/graph/content/BatchRequest.java b/src/main/java/com/microsoft/graph/content/BatchRequest.java new file mode 100644 index 000000000..ce65cdb42 --- /dev/null +++ b/src/main/java/com/microsoft/graph/content/BatchRequest.java @@ -0,0 +1,85 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) 2021 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ------------------------------------------------------------------------------ + +package com.microsoft.graph.content; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.microsoft.graph.core.ClientException; +import com.microsoft.graph.core.IBaseClient; +import com.microsoft.graph.http.BaseRequest; +import com.microsoft.graph.http.HttpMethod; +import com.microsoft.graph.options.Option; + +/** Request for batch requests */ +public class BatchRequest extends BaseRequest { + + /** + * Instantiates a new batch request + * + * @param requestUrl the URL to send the request to + * @param client the client to use to execute the request + * @param options the options to apply to the request + */ + public BatchRequest(@Nonnull final String requestUrl, @Nonnull final IBaseClient client, @Nonnull final List options) { + super(requestUrl, client, options, BatchResponseContent.class); + } + + /** + * Send this request + * + * @return the response object + * @param content content of the batch request to execute + * @throws ClientException an exception occurs if there was an error while the request was sent + */ + @Nullable + public BatchResponseContent post(@Nonnull final BatchRequestContent content) throws ClientException { + this.setHttpMethod(HttpMethod.POST); + final BatchResponseContent response = this.getClient().getHttpProvider().send(this, BatchResponseContent.class, content); + setSerializerOnSteps(response); + return response; + } + /** + * Send this request + * + * @return the response object + * @param content content of the batch request to execute + * @throws ClientException an exception occurs if there was an error while the request was sent + */ + @Nullable + public java.util.concurrent.CompletableFuture postAsync(@Nonnull final BatchRequestContent content) throws ClientException { + this.setHttpMethod(HttpMethod.POST); + return this.getClient().getHttpProvider().sendAsync(this, BatchResponseContent.class, content).thenApply(response -> { + setSerializerOnSteps(response); + return response; + }); + } + private void setSerializerOnSteps(final BatchResponseContent response) { + if(response.responses != null) + for(final BatchResponseStep step : response.responses) { + step.serializer = this.getClient().getSerializer(); + } + } +} diff --git a/src/main/java/com/microsoft/graph/content/BatchRequestBuilder.java b/src/main/java/com/microsoft/graph/content/BatchRequestBuilder.java new file mode 100644 index 000000000..731dfd9aa --- /dev/null +++ b/src/main/java/com/microsoft/graph/content/BatchRequestBuilder.java @@ -0,0 +1,66 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) 2021 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ------------------------------------------------------------------------------ + +package com.microsoft.graph.content; + +import java.util.List; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.microsoft.graph.core.IBaseClient; +import com.microsoft.graph.http.BaseRequestBuilder; +import com.microsoft.graph.options.Option; + +/** Request builder for batch requests */ +public class BatchRequestBuilder extends BaseRequestBuilder { + /** + * Instantiates a new batch request builder + * @param requestUrl the URL for the request + * @param client the client to use to execute the request + * @param options the request options + */ + public BatchRequestBuilder(@Nonnull final String requestUrl, @Nonnull final IBaseClient client, @Nonnull final List options) { + super(requestUrl, client, options); + } + /** + * Creates the request + * + * @param requestOptions the options for this request + * @return the IUserRequest instance + */ + @Nonnull + public BatchRequest buildRequest(@Nullable final com.microsoft.graph.options.Option... requestOptions) { + return buildRequest(getOptions(requestOptions)); + } + + /** + * Creates the request + * + * @param requestOptions the options for this request + * @return the IUserRequest instance + */ + @Nonnull + public BatchRequest buildRequest(@Nonnull final java.util.List requestOptions) { + return new BatchRequest(this.getRequestUrl(), getClient(), requestOptions); + } +} diff --git a/src/main/java/com/microsoft/graph/content/BatchRequestContent.java b/src/main/java/com/microsoft/graph/content/BatchRequestContent.java new file mode 100644 index 000000000..8a6f7d163 --- /dev/null +++ b/src/main/java/com/microsoft/graph/content/BatchRequestContent.java @@ -0,0 +1,184 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) 2021 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ------------------------------------------------------------------------------ + +package com.microsoft.graph.content; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashMap; +import java.util.HashSet; +import java.util.List; +import java.util.Locale; +import java.util.Objects; +import java.util.concurrent.ThreadLocalRandom; +import java.util.regex.Matcher; +import java.util.regex.Pattern; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.microsoft.graph.http.HttpMethod; +import com.microsoft.graph.http.IHttpRequest; +import com.microsoft.graph.options.HeaderOption; +import com.microsoft.graph.serializer.IJsonBackedObject; + +/** Respresents the content of a JSON batch request */ +public class BatchRequestContent { + /** Steps part of the batch request */ + @Expose + @Nullable + @SerializedName("requests") + public List> requests; + + /** + * Adds a request as a step to the current batch. Defaults to GET if the HTTP method is not set in the request. + * @param request the request to add as a step + * @return the id of the step that was just added to the batch + */ + @Nonnull + public String addBatchRequestStep(@Nonnull final IHttpRequest request) { + Objects.requireNonNull(request, "request parameter cannot be null"); + return addBatchRequestStep(request, request.getHttpMethod() == null ? HttpMethod.GET : request.getHttpMethod()); + } + /** + * Adds a request as a step to the current batch + * @param request the request to add as a step + * @param httpMethod the HttpMethod to execute the request with + * @return the id of the step that was just added to the batch + */ + @Nonnull + public String addBatchRequestStep(@Nonnull final IHttpRequest request, @Nonnull final HttpMethod httpMethod) { + return addBatchRequestStep(request, httpMethod, null); + } + /** + * Adds a request as a step to the current batch + * @param request the request to add as a step + * @param httpMethod the HttpMethod to execute the request with + * @param the type of the request body + * @param serializableBody the body of the request to be serialized + * @return the id of the step that was just added to the batch + */ + @Nonnull + public String addBatchRequestStep(@Nonnull final IHttpRequest request, @Nonnull final HttpMethod httpMethod, @Nullable final T serializableBody) { + return addBatchRequestStep(request, httpMethod, serializableBody, (String[])null); + } + /** + * Adds a request as a step to the current batch + * @param request the request to add as a step + * @param httpMethod the HttpMethod to execute the request with + * @param the type of the request body + * @param serializableBody the body of the request to be serialized + * @param dependsOnRequestsIds the ids of steps this request depends on + * @return the id of the step that was just added to the batch + */ + @Nonnull + public String addBatchRequestStep(@Nonnull final IHttpRequest request, @Nonnull final HttpMethod httpMethod, @Nullable final T serializableBody, @Nullable final String ...dependsOnRequestsIds) { + Objects.requireNonNull(request, "request parameter cannot be null"); + Objects.requireNonNull(httpMethod, "httpMethod parameter cannot be null"); + if(dependsOnRequestsIds != null) + for(final String id : dependsOnRequestsIds) { + if(getStepById(id) == null) + throw new IllegalArgumentException("the current request depends on a inexisting request"); + } + + if(requests == null) + requests = new ArrayList<>(); + + final Matcher protocolAndHostReplacementMatcher = protocolAndHostReplacementPattern.matcher(request.getRequestUrl().toString()); + final BatchRequestStep step = new BatchRequestStep() {{ + url = protocolAndHostReplacementMatcher.replaceAll(""); + body = serializableBody; + method = httpMethod.toString().toUpperCase(Locale.getDefault()); + dependsOn = dependsOnRequestsIds != null && dependsOnRequestsIds.length > 0 ? new HashSet(Arrays.asList(dependsOnRequestsIds)) : null; + id = getNextRequestId(); + }}; + + if(!request.getHeaders().isEmpty()) { + step.headers = new HashMap<>(); + for(final HeaderOption headerOption : request.getHeaders()) + step.headers.putIfAbsent(headerOption.getName().toLowerCase(Locale.getDefault()), headerOption.getValue().toString()); + } + if(step.body != null && step.body instanceof IJsonBackedObject && + (step.headers == null || !step.headers.containsKey(contentTypeHeaderKey))) { + if(step.headers == null) + step.headers = new HashMap<>(); + step.headers.putIfAbsent(contentTypeHeaderKey, "application/json"); + } + requests.add(step); + return step.id; + } + private static final String contentTypeHeaderKey = "content-type"; + /** + * Removes requests from the requests to be executed by the batch. Also removes any dependency references that might exist. + * @param stepIds ids of steps to be removed. + */ + public void removeBatchRequestStepWithId(@Nonnull final String ...stepIds) { + if(requests == null) return; + + for(final String stepId : stepIds) { + Objects.requireNonNull(stepId, "parameter stepIds cannot contain null values"); + requests.removeIf(x -> x.id == stepId); + for(final BatchRequestStep step : requests) { + if(step.dependsOn != null) { + step.dependsOn.removeIf(x -> x == stepId); + if(step.dependsOn.isEmpty()) + step.dependsOn = null; // so we don't send dependsOn: [] over the wire + } + } + } + } + /** + * Gets a step by its step id + * @param the type of the request body + * @param stepId the request step id returned from the add method + * @return the request corresponding to the provided id or null + */ + @Nullable + @SuppressWarnings("unchecked") + public BatchRequestStep getStepById(@Nonnull final String stepId) { + Objects.requireNonNull(stepId, "parameter stepId cannot be null"); + if(requests == null) return null; + + for(final BatchRequestStep step : requests) { + if(stepId.equals(step.id)) + return (BatchRequestStep)step; + } + return null; + } + /** pattern to replace the protocol and host part of the request if specified */ + @Nonnull + protected static final Pattern protocolAndHostReplacementPattern = Pattern.compile("(?i)^http[s]?:\\/\\/graph\\.microsoft\\.com\\/(?>v1\\.0|beta)\\/?"); // (?i) case insensitive + /** + * Generates a randomly available request id + * @return a random request id + */ + @Nonnull + protected String getNextRequestId() { + String requestId; + do { + requestId = Integer.toString(ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE)); + } while(getStepById(requestId) != null); + return requestId; + } +} diff --git a/src/main/java/com/microsoft/graph/content/BatchRequestStep.java b/src/main/java/com/microsoft/graph/content/BatchRequestStep.java new file mode 100644 index 000000000..cb6ae415a --- /dev/null +++ b/src/main/java/com/microsoft/graph/content/BatchRequestStep.java @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) 2021 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ------------------------------------------------------------------------------ + +package com.microsoft.graph.content; + +import java.util.HashSet; + +import javax.annotation.Nullable; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** the http request for the batch step */ +public class BatchRequestStep extends BatchStep { + /** The URL to query for the step */ + @Nullable + @Expose + @SerializedName("url") + public String url; + /** The HTTP method to use to execute the request */ + @Nullable + @Expose + @SerializedName("method") + public String method; + /** The IDs of the steps this step depends on before being executed */ + @Nullable + @Expose + @SerializedName("dependsOn") + public HashSet dependsOn; +} diff --git a/src/main/java/com/microsoft/graph/content/BatchResponseContent.java b/src/main/java/com/microsoft/graph/content/BatchResponseContent.java new file mode 100644 index 000000000..fe8022524 --- /dev/null +++ b/src/main/java/com/microsoft/graph/content/BatchResponseContent.java @@ -0,0 +1,59 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) 2021 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ------------------------------------------------------------------------------ + +package com.microsoft.graph.content; + +import java.util.List; +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.google.gson.JsonElement; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** Respresents the result of a JSON batch request */ +public class BatchResponseContent { + /** Responses to the steps from the request */ + @Nullable + @Expose + @SerializedName("responses") + public List> responses; + + /** + * Gets a response to a request in the batch by its id + * @param stepId Id of the request step in the batch request + * @return The step response corresponding to the ID or null + */ + @Nullable + public BatchResponseStep getResponseById(@Nonnull final String stepId) { + Objects.requireNonNull(stepId, "parameter stepId cannot be null"); + if(responses == null) return null; + + for(final BatchResponseStep step : responses) { + if(stepId.equals(step.id)) + return step; + } + return null; + } +} diff --git a/src/main/java/com/microsoft/graph/content/BatchResponseStep.java b/src/main/java/com/microsoft/graph/content/BatchResponseStep.java new file mode 100644 index 000000000..feb1d13a1 --- /dev/null +++ b/src/main/java/com/microsoft/graph/content/BatchResponseStep.java @@ -0,0 +1,67 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) 2021 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ------------------------------------------------------------------------------ +package com.microsoft.graph.content; + +import java.util.ArrayList; +import java.util.Objects; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import com.google.gson.JsonElement; +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; +import com.microsoft.graph.http.GraphErrorResponse; +import com.microsoft.graph.http.GraphFatalServiceException; +import com.microsoft.graph.http.GraphServiceException; +import com.microsoft.graph.serializer.ISerializer; + +/** Response for the batch step */ +public class BatchResponseStep extends BatchStep { + /** Http status code of the response */ + @Expose + @SerializedName("status") + public int status; + /** Serializer to use for response deserialization */ + @Nullable + protected ISerializer serializer; + + /** + * Returned the deserialized response body of the current step + * @param type of the response body + * @param resultClass class of the resulting response body + * @return the deserialized response body + * @throws GraphServiceException when a bad request was sent + * @throws GraphFatalServiceException when the service did not complete the operation as expected because of an internal error + */ + @Nullable + public T2 getDeserializedBody(@Nonnull final Class resultClass) throws GraphServiceException, GraphFatalServiceException { + Objects.requireNonNull(resultClass, "parameter resultClass cannot be null"); + if(serializer == null || body == null || !(body instanceof JsonElement)) return null; + + final GraphErrorResponse error = serializer.deserializeObject((JsonElement)body, GraphErrorResponse.class); + if(error == null || error.error == null) { + return serializer.deserializeObject((JsonElement)body, resultClass); + } else + throw GraphServiceException.createFromResponse("", "", new ArrayList<>(), "", headers, "", status, error, false); + } +} diff --git a/src/main/java/com/microsoft/graph/content/BatchStep.java b/src/main/java/com/microsoft/graph/content/BatchStep.java new file mode 100644 index 000000000..89773904e --- /dev/null +++ b/src/main/java/com/microsoft/graph/content/BatchStep.java @@ -0,0 +1,49 @@ +// ------------------------------------------------------------------------------ +// Copyright (c) 2021 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 +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +// THE SOFTWARE. +// ------------------------------------------------------------------------------ + +package com.microsoft.graph.content; + +import java.util.HashMap; + +import javax.annotation.Nullable; + +import com.google.gson.annotations.Expose; +import com.google.gson.annotations.SerializedName; + +/** Common abstractions between batch request steps and batch response steps */ +public abstract class BatchStep { + /** The Id of the step */ + @Expose + @Nullable + @SerializedName("id") + public String id; + /** The request/response headers for the step */ + @Expose + @Nullable + @SerializedName("headers") + public HashMap headers; + /** The body of request/response for the step */ + @Nullable + @Expose + @SerializedName("body") + public T body; +} diff --git a/src/main/java/com/microsoft/graph/content/MSBatchRequestContent.java b/src/main/java/com/microsoft/graph/content/MSBatchRequestContent.java deleted file mode 100644 index 013f47509..000000000 --- a/src/main/java/com/microsoft/graph/content/MSBatchRequestContent.java +++ /dev/null @@ -1,242 +0,0 @@ -package com.microsoft.graph.content; - -import java.io.IOException; -import java.util.HashSet; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; -import java.util.Objects; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.ThreadLocalRandom; -import java.util.regex.Matcher; -import java.util.regex.Pattern; - -import javax.annotation.Nullable; -import javax.annotation.Nonnull; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.google.gson.JsonPrimitive; -import com.microsoft.graph.core.ClientException; -import com.microsoft.graph.core.IBaseClient; -import com.microsoft.graph.logger.DefaultLogger; -import com.microsoft.graph.logger.ILogger; -import com.google.gson.JsonParseException; - -import okhttp3.Headers; -import okhttp3.Request; -import okhttp3.RequestBody; -import okio.Buffer; - -/** - * Represents the content of a batch request - */ -public class MSBatchRequestContent { - private final LinkedHashMap batchRequestStepsHashMap; - - /** - * Maximum number of requests that can be sent in a batch - */ - public static final int MAX_NUMBER_OF_REQUESTS = 20; - private final ILogger logger; - - /** - * Creates Batch request content using list provided - * - * @param batchRequestStepsArray List of batch steps for batching - */ - public MSBatchRequestContent(@Nonnull final MSBatchRequestStep... batchRequestStepsArray) { - this(new DefaultLogger(), batchRequestStepsArray); - } - - /** - * Creates Batch request content using list provided - * - * @param batchRequestStepsArray List of batch steps for batching - * @param logger logger to use for telemetry - */ - public MSBatchRequestContent(@Nonnull final ILogger logger, @Nonnull final MSBatchRequestStep... batchRequestStepsArray) { - if (batchRequestStepsArray.length > MAX_NUMBER_OF_REQUESTS) - throw new IllegalArgumentException("Number of batch request steps cannot exceed " + MAX_NUMBER_OF_REQUESTS); - - this.logger = Objects.requireNonNull(logger, "logger cannot be null"); - - this.batchRequestStepsHashMap = new LinkedHashMap<>(); - for (final MSBatchRequestStep requestStep : batchRequestStepsArray) - if(requestStep != null) - addBatchRequestStep(requestStep); - } - - /** - * Adds a step to the current batch - * @param batchRequestStep Batch request step adding to batch content - * @return true or false based on addition or no addition of batch request step - * given - */ - public boolean addBatchRequestStep(@Nonnull final MSBatchRequestStep batchRequestStep) { - Objects.requireNonNull(batchRequestStep, "batchRequestStep parameter cannot be null"); - if (batchRequestStepsHashMap.containsKey(batchRequestStep.getRequestId()) || - batchRequestStepsHashMap.size() >= MAX_NUMBER_OF_REQUESTS) - return false; - batchRequestStepsHashMap.put(batchRequestStep.getRequestId(), batchRequestStep); - return true; - } - - /** - * Add steps to batch from OkHttp.Request - * @param request the request to add to the batch - * @param arrayOfDependsOnIds ids of steps this step depends on - * @return the step id - */ - @Nonnull - public String addBatchRequestStep(@Nonnull final Request request, @Nullable final String... arrayOfDependsOnIds) { - Objects.requireNonNull(request, "request parameter cannot be null"); - String requestId; - do { - requestId = Integer.toString(ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE)); - } while(batchRequestStepsHashMap.keySet().contains(requestId)); - if(addBatchRequestStep(new MSBatchRequestStep(requestId, request, arrayOfDependsOnIds))) - return requestId; - else - throw new IllegalArgumentException("unable to add step to batch. Number of batch request steps cannot exceed " + MAX_NUMBER_OF_REQUESTS); - } - - /** - * @param requestId Id of Batch request step to be removed - * - * @return true or false based on removal or no removal of batch request step - * with given id - */ - public boolean removeBatchRequestStepWithId(@Nonnull final String requestId) { - boolean removed = false; - if (batchRequestStepsHashMap.containsKey(requestId)) { - batchRequestStepsHashMap.remove(requestId); - removed = true; - for (final Map.Entry steps : batchRequestStepsHashMap.entrySet()) { - if (steps.getValue() != null && steps.getValue().getDependsOnIds() != null) { - while (steps.getValue().getDependsOnIds().remove(requestId)) - ; - } - } - } - return removed; - } - - private JsonObject getBatchRequestContentAsJson() { - final JsonObject batchRequestContentMap = new JsonObject(); - final JsonArray batchContentArray = new JsonArray(); - for (final Map.Entry requestStep : batchRequestStepsHashMap.entrySet()) { - batchContentArray.add(getBatchRequestObjectFromRequestStep(requestStep.getValue())); - } - batchRequestContentMap.add("requests", batchContentArray); - return batchRequestContentMap; - } - /** - * @return Batch request content's json as String - */ - @Nonnull - public String getBatchRequestContent() { - return getBatchRequestContentAsJson().toString(); - } - - /** - * Executes the batch requests and returns the response - * @param client client to use for the request - * @return the batch response - * @throws ClientException when the batch couldn't be executed because of client issues. - */ - @Nonnull - public MSBatchResponseContent execute(@Nonnull final IBaseClient client) { - final JsonObject content = getBatchRequestContentAsJson(); - return new MSBatchResponseContent(logger, client.getServiceRoot() + "/", - content, - client.customRequest("/$batch") - .buildRequest() - .post(content) - .getAsJsonObject()); - } - /** - * Executes the batch requests asynchronously and returns the response - * @param client client to use for the request - * @return a future with the batch response - */ - @Nonnull - public CompletableFuture executeAsync(@Nonnull final IBaseClient client) { - Objects.requireNonNull(client, "client parameter cannot be null"); - final JsonObject content = getBatchRequestContentAsJson(); - return client.customRequest("/$batch") - .buildRequest() - .postAsync(content) - .thenApply(resp -> new MSBatchResponseContent(logger, client.getServiceRoot() + "/", content, resp.getAsJsonObject())); - } - - private static final Pattern protocolAndHostReplacementPattern = Pattern.compile("(?i)^http[s]?:\\/\\/graph\\.microsoft\\.com\\/(?>v1\\.0|beta)\\/?"); // (?i) case insensitive - private JsonObject getBatchRequestObjectFromRequestStep(final MSBatchRequestStep batchRequestStep) { - final JsonObject contentmap = new JsonObject(); - contentmap.add("id", new JsonPrimitive(batchRequestStep.getRequestId())); - - final Matcher protocolAndHostReplacementMatcher = protocolAndHostReplacementPattern.matcher(batchRequestStep.getRequest().url().toString()); - - final String url = protocolAndHostReplacementMatcher.replaceAll(""); - contentmap.add("url", new JsonPrimitive(url)); - - contentmap.add("method", new JsonPrimitive(batchRequestStep.getRequest().method().toString())); - - final Headers headers = batchRequestStep.getRequest().headers(); - if (headers != null && headers.size() != 0) { - final JsonObject headerMap = new JsonObject(); - for (final Map.Entry> entry : headers.toMultimap().entrySet()) { - headerMap.add(entry.getKey(), new JsonPrimitive(getHeaderValuesAsString(entry.getValue()))); - } - contentmap.add("headers", headerMap); - } - - final HashSet arrayOfDependsOnIds = batchRequestStep.getDependsOnIds(); - if (arrayOfDependsOnIds != null) { - final JsonArray array = new JsonArray(arrayOfDependsOnIds.size()); - for (final String dependsOnId : arrayOfDependsOnIds) - array.add(dependsOnId); - contentmap.add("dependsOn", array); - } - - final RequestBody body = batchRequestStep.getRequest().body(); - if (body != null) { - try { - contentmap.add("body", requestBodyToJSONObject(batchRequestStep.getRequest())); - } catch (IOException | JsonParseException e) { - logger.logError("error pasing the request JSON body", e); - } - } - return contentmap; - } - - private String getHeaderValuesAsString(final List list) { - if (list == null || list.size() == 0) - return ""; - final StringBuilder builder = new StringBuilder(list.get(0)); - for (int i = 1; i < list.size(); i++) { - builder.append(";"); - builder.append(list.get(i)); - } - return builder.toString(); - } - - private JsonObject requestBodyToJSONObject(final Request request) throws IOException, JsonParseException { - if (request == null || request.body() == null) - return null; - final Request copy = request.newBuilder().build(); - final Buffer buffer = new Buffer(); - copy.body().writeTo(buffer); - final String requestBody = buffer.readUtf8(); - if(requestBody == null || requestBody == "") - return null; - final JsonElement requestBodyElement = JsonParser.parseString(requestBody); - if(requestBodyElement == null || !requestBodyElement.isJsonObject()) - return null; - else - return requestBodyElement.getAsJsonObject(); - } - -} diff --git a/src/main/java/com/microsoft/graph/content/MSBatchRequestStep.java b/src/main/java/com/microsoft/graph/content/MSBatchRequestStep.java deleted file mode 100644 index edb36b9d7..000000000 --- a/src/main/java/com/microsoft/graph/content/MSBatchRequestStep.java +++ /dev/null @@ -1,62 +0,0 @@ -package com.microsoft.graph.content; - -import java.util.Arrays; -import java.util.HashSet; -import java.util.Objects; - -import javax.annotation.Nullable; -import javax.annotation.Nonnull; - -import okhttp3.Request; - -/** - * Respresents a step in a batch request - */ -public class MSBatchRequestStep { - private String requestId; - private Request request; - private HashSet dependsOnIds; - - /** - * Initializes a batch step from a raw HTTP request - * @param requestId the id to assign to this step - * @param request the request to send in the batch - * @param arrayOfDependsOnIds the ids of steps this step depends on - */ - public MSBatchRequestStep(@Nonnull final String requestId, @Nonnull final Request request, @Nullable final String... arrayOfDependsOnIds) { - Objects.requireNonNull(requestId, "Request Id cannot be null."); - if(requestId.isEmpty()) - throw new IllegalArgumentException("Request Id cannot be empty."); - - this.requestId = requestId; - this.request = Objects.requireNonNull(request, "Request cannot be null."); - this.dependsOnIds = new HashSet<>(Arrays.asList(arrayOfDependsOnIds)); - } - - /** - * Gets the current step ID - * @return the current step ID - */ - @Nonnull - public String getRequestId() { - return requestId; - } - - /** - * Gets the raw HTTP request representation for the step - * @return the raw HTTP request representation for the step - */ - @Nonnull - public Request getRequest() { - return request; - } - - /** - * Gets the list of steps this step depends on - * @return the list of steps this step depends on - */ - @Nullable - public HashSet getDependsOnIds(){ - return dependsOnIds; - } -} diff --git a/src/main/java/com/microsoft/graph/content/MSBatchResponseContent.java b/src/main/java/com/microsoft/graph/content/MSBatchResponseContent.java deleted file mode 100644 index 9bdb8d242..000000000 --- a/src/main/java/com/microsoft/graph/content/MSBatchResponseContent.java +++ /dev/null @@ -1,310 +0,0 @@ -package com.microsoft.graph.content; - -import java.io.IOException; -import java.util.LinkedHashMap; -import java.util.Iterator; -import java.util.Map; -import java.util.Objects; - -import javax.annotation.Nullable; -import javax.annotation.Nonnull; - -import com.google.gson.JsonArray; -import com.google.gson.JsonElement; -import com.google.gson.JsonObject; -import com.google.gson.JsonParser; -import com.microsoft.graph.logger.DefaultLogger; -import com.microsoft.graph.logger.ILogger; -import com.google.gson.JsonParseException; - -import okhttp3.MediaType; -import okhttp3.Protocol; -import okhttp3.Request; -import okhttp3.RequestBody; -import okhttp3.Response; -import okhttp3.ResponseBody; -import okio.Buffer; - -/** - * Represents the response of a batch request - */ -public class MSBatchResponseContent { - - private final Protocol protocol; - private final String message; - private LinkedHashMap batchRequestsHashMap = new LinkedHashMap<>(); - private JsonArray batchResponseArray; - private String nextLink; - private final ILogger logger; - - /** - * @param batchResponse OkHttp batch response on execution of batch requests - */ - public MSBatchResponseContent(@Nullable final Response batchResponse) { - this(new DefaultLogger(), batchResponse); - } - - /** - * @param batchResponse OkHttp batch response on execution of batch requests - * @param logger logger to use for telemetry - */ - public MSBatchResponseContent(@Nonnull final ILogger logger, @Nullable final Response batchResponse) { - this.logger = Objects.requireNonNull(logger, "logger parameter cannot be null"); - update(batchResponse); - this.message = batchResponse.message(); - this.protocol = batchResponse.protocol(); - } - /** - * instantiates a new response - * internal only, used when the content executes the requests - * @param baseUrl the base service URL without a trailing slash - * @param batchRequestData the batch request payload data as a JSON string - * @param batchResponseData the batch response body as a JSON string - * @param logger logger to use for telemetry - */ - protected MSBatchResponseContent(@Nonnull final ILogger logger, @Nonnull final String baseUrl, @Nonnull final JsonObject batchRequestData, @Nonnull final JsonObject batchResponseData) { - this.logger = logger; - this.protocol = Protocol.HTTP_1_1; - this.message = "OK"; - final Map requestMap = createBatchRequestsHashMap(baseUrl, batchRequestData); - if (requestMap != null) - batchRequestsHashMap.putAll(requestMap); - updateFromResponseBody(batchResponseData); - } - - /** - * Returns OkHttp Response of given request Id - * - * @param requestId Request Id of batch step - * - * @return OkHttp Response corresponding to requestId - */ - @Nullable - public Response getResponseById(@Nonnull final String requestId) { - if (batchResponseArray == null) - return null; - - final JsonArray responses = batchResponseArray; - - for (final JsonElement response : responses) { - if(!response.isJsonObject()) - continue; - final JsonObject jsonresponse = response.getAsJsonObject(); - final JsonElement idElement = jsonresponse.get("id"); - if (idElement != null && idElement.isJsonPrimitive()) { - final String id = idElement.getAsString(); - if (id.compareTo(requestId) == 0) { - final Response.Builder builder = new Response.Builder(); - - // Put corresponding request into the constructed response - builder.request(batchRequestsHashMap.get(requestId)); - // copy protocol and message same as of batch response - builder.protocol(protocol); - builder.message(message); - - // Put status code of the corresponding request in JsonArray - final JsonElement statusElement = jsonresponse.get("status"); - if (statusElement != null && statusElement.isJsonPrimitive()) { - builder.code(statusElement.getAsInt()); - } - - // Put body from response array for corresponding id into constructing response - final JsonElement jsonBodyElement = jsonresponse.get("body"); - if (jsonBodyElement != null && jsonBodyElement.isJsonObject()) { - final JsonObject JsonObject = jsonBodyElement.getAsJsonObject(); - final String bodyAsString = JsonObject.toString(); - final ResponseBody responseBody = ResponseBody - .create(bodyAsString, MediaType.parse("application/json; charset=utf-8")); - builder.body(responseBody); - } - - // Put headers from response array for corresponding id into constructing - // response - final JsonElement jsonheadersElement = jsonresponse.get("headers"); - if (jsonheadersElement != null && jsonheadersElement.isJsonObject()) { - final JsonObject jsonheaders = jsonheadersElement.getAsJsonObject(); - for (final String key : jsonheaders.keySet()) { - final JsonElement strValueElement = jsonheaders.get(key); - if (strValueElement != null && strValueElement.isJsonPrimitive()) { - final String strvalue = strValueElement.getAsString(); - for (final String value : strvalue.split(";")) { - builder.header(key, value); - } - } - } - } - return builder.build(); - } - } - } - return null; - } - - /** - * Get map of id and responses - * - * @return responses in Map of id and response - */ - @Nonnull - public Map getResponses() { - if (batchResponseArray == null) - return null; - final Map responsesMap = new LinkedHashMap<>(); - for (final String id : batchRequestsHashMap.keySet()) { - responsesMap.put(id, getResponseById(id)); - } - return responsesMap; - } - - /** - * Get iterator over the responses - * - * @return iterator for responses - */ - @Nullable - public Iterator> getResponsesIterator() { - final Map responsesMap = getResponses(); - return responsesMap != null ? responsesMap.entrySet().iterator() : null; - } - - /** - * Updates the response content using the raw http response object - * @param batchResponse the response from the service. - */ - public void update(@Nonnull final Response batchResponse) { - final Map requestMap = createBatchRequestsHashMap(Objects.requireNonNull(batchResponse, "Batch Response cannot be null")); - if (requestMap != null) - batchRequestsHashMap.putAll(requestMap); - - if (batchResponse.body() != null) { - try { - final String batchResponseData = batchResponse.body().string(); - if (batchResponseData != null) { - updateFromResponseBody(stringToJSONObject(batchResponseData)); - } - } catch (final IOException e) { - logger.logError("error updating batch response content from response body", e); - } - } - } - private void updateFromResponseBody(@Nonnull final JsonObject batchResponseObj) { - if (batchResponseObj != null) { - final JsonElement nextLinkElement = batchResponseObj.get("@odata.nextLink"); - if (nextLinkElement != null && nextLinkElement.isJsonPrimitive()) - nextLink = nextLinkElement.getAsString(); - - if (batchResponseArray == null) - batchResponseArray = new JsonArray(); - - final JsonElement responseArrayElement = batchResponseObj.get("responses"); - if (responseArrayElement != null && responseArrayElement.isJsonArray()) { - final JsonArray responseArray = responseArrayElement.getAsJsonArray(); - batchResponseArray.addAll(responseArray); - } - } - } - - /** - * @return nextLink of batch response - */ - @Nullable - public String nextLink() { - return nextLink; - } - - private Map createBatchRequestsHashMap(final Response batchResponse) { - if (batchResponse == null) - return null; - try { - final JsonObject requestJSONObject = requestBodyToJSONObject(batchResponse.request()); - final String baseUrl = batchResponse.request().url().toString().replace("$batch", ""); - return createBatchRequestsHashMap(baseUrl, requestJSONObject); - } catch (IOException ex) { - logger.logError("error parsing batch requests to get batch response content", ex); - return null; - } - } - private Map createBatchRequestsHashMap(@Nonnull final String baseUrl, @Nonnull final JsonObject requestJSONObject) { - if(baseUrl == null || baseUrl == "" || requestJSONObject == null) { - return null; - } - try { - final Map batchRequestsHashMap = new LinkedHashMap<>(); - final JsonElement requestArrayElement = requestJSONObject.get("requests"); - if (requestArrayElement != null && requestArrayElement.isJsonArray()) { - final JsonArray requestArray = requestArrayElement.getAsJsonArray(); - for (final JsonElement item : requestArray) { - if(!item.isJsonObject()) - continue; - final JsonObject requestObject = item.getAsJsonObject(); - - final Request.Builder builder = new Request.Builder(); - - final JsonElement urlElement = requestObject.get("url"); - if (urlElement != null && urlElement.isJsonPrimitive()) { - final StringBuilder fullUrl = new StringBuilder(baseUrl); - fullUrl.append(urlElement.getAsString()); - builder.url(fullUrl.toString()); - } - final JsonElement jsonHeadersElement = requestObject.get("headers"); - if (jsonHeadersElement != null && jsonHeadersElement.isJsonObject()) { - final JsonObject jsonheaders = jsonHeadersElement.getAsJsonObject(); - for (final String key : jsonheaders.keySet()) { - final JsonElement strvalueElement = jsonheaders.get(key); - if (strvalueElement != null && strvalueElement.isJsonPrimitive()) { - final String strvalue = strvalueElement.getAsString(); - for (final String value : strvalue.split("; ")) { - builder.header(key, value); - } - } - } - } - final JsonElement jsonBodyElement = requestObject.get("body"); - final JsonElement jsonMethodElement = requestObject.get("method"); - if (jsonBodyElement != null && jsonMethodElement != null - && jsonBodyElement.isJsonObject() && jsonMethodElement.isJsonPrimitive()) { - final JsonObject JsonObject = jsonBodyElement.getAsJsonObject(); - final String bodyAsString = JsonObject.toString(); - final RequestBody requestBody = RequestBody - .create(bodyAsString, MediaType.parse("application/json; charset=utf-8")); - builder.method(jsonMethodElement.getAsString(), requestBody); - } else if (jsonMethodElement != null) { - builder.method(jsonMethodElement.getAsString(), null); - } - final JsonElement jsonIdElement = requestObject.get("id"); - if (jsonIdElement != null && jsonIdElement.isJsonPrimitive()) { - batchRequestsHashMap.put(jsonIdElement.getAsString(), builder.build()); - } - } - } - return batchRequestsHashMap; - - } catch (JsonParseException e) { - logger.logError("error parsing batch request", e); - } - return null; - } - - private JsonObject stringToJSONObject(final String input) { - JsonObject JsonObject = null; - try { - if (input != null) { - JsonObject = JsonParser.parseString(input).getAsJsonObject(); - } - } catch (final Exception e) { - logger.logError("error parsing input into JSONObject", e); - } - return JsonObject; - } - - private JsonObject requestBodyToJSONObject(final Request request) throws IOException, JsonParseException { - if (request == null || request.body() == null) - return null; - final Request copy = request.newBuilder().build(); - final Buffer buffer = new Buffer(); - copy.body().writeTo(buffer); - final String requestBody = buffer.readUtf8(); - final JsonObject JsonObject = JsonParser.parseString(requestBody).getAsJsonObject(); - return JsonObject; - } -} diff --git a/src/main/java/com/microsoft/graph/core/BaseClient.java b/src/main/java/com/microsoft/graph/core/BaseClient.java index bcf49d951..d9e4e22fe 100644 --- a/src/main/java/com/microsoft/graph/core/BaseClient.java +++ b/src/main/java/com/microsoft/graph/core/BaseClient.java @@ -27,6 +27,7 @@ import com.microsoft.graph.http.IHttpProvider; import com.microsoft.graph.httpcore.HttpClients; import com.microsoft.graph.authentication.IAuthenticationProvider; +import com.microsoft.graph.content.BatchRequestBuilder; import com.microsoft.graph.logger.DefaultLogger; import com.microsoft.graph.logger.ILogger; import com.microsoft.graph.serializer.DefaultSerializer; @@ -99,7 +100,16 @@ public CustomRequestBuilder customRequest(@Nonnull final String url, @Non public CustomRequestBuilder customRequest(@Nonnull final String url) { return new CustomRequestBuilder(getServiceRoot() + url, this, null, JsonElement.class); - } + } + + /** + * Get the batch request builder. + * @return a request builder to execute a batch. + */ + @Nonnull + public BatchRequestBuilder batch() { + return new BatchRequestBuilder(getServiceRoot() + "/$batch", this, null); + } /** * Gets the builder to start configuring the client diff --git a/src/main/java/com/microsoft/graph/core/IBaseClient.java b/src/main/java/com/microsoft/graph/core/IBaseClient.java index 9941f6417..ec49764d7 100644 --- a/src/main/java/com/microsoft/graph/core/IBaseClient.java +++ b/src/main/java/com/microsoft/graph/core/IBaseClient.java @@ -26,6 +26,7 @@ import javax.annotation.Nonnull; import com.google.gson.JsonElement; +import com.microsoft.graph.content.BatchRequestBuilder; import com.microsoft.graph.http.IHttpProvider; import com.microsoft.graph.logger.ILogger; import com.microsoft.graph.serializer.ISerializer; @@ -93,6 +94,13 @@ public interface IBaseClient { @Nonnull CustomRequestBuilder customRequest(@Nonnull final String url); + /** + * Get the batch request builder. + * @return a request builder to execute a batch. + */ + @Nonnull + public BatchRequestBuilder batch(); + /** * Gets the service SDK version if the service SDK is in use, null otherwise * @return the service SDK version if the service SDK is in use, null otherwise diff --git a/src/main/java/com/microsoft/graph/http/GraphServiceException.java b/src/main/java/com/microsoft/graph/http/GraphServiceException.java index eece63096..77a363c39 100644 --- a/src/main/java/com/microsoft/graph/http/GraphServiceException.java +++ b/src/main/java/com/microsoft/graph/http/GraphServiceException.java @@ -368,8 +368,32 @@ public static GraphServiceException createFromResponse(@Nonnull final IHttpR } final int responseCode = response.code(); - final List responseHeaders = new LinkedList<>(); final Map headers = getResponseHeadersAsMapStringString(response); + final String responseMessage = response.message(); + GraphErrorResponse error = parseErrorResponse(serializer, response); + + return createFromResponse(url, method, requestHeaders, requestBody, headers, responseMessage, responseCode, error, isVerbose); + + } + /** + * Creates a Graph service exception. + * @param url url of the original request + * @param method http method of the original request + * @param requestHeaders headers of the original request + * @param requestBody body of the original request + * @param headers response headers + * @param responseMessage reponse status message + * @param responseCode response status code + * @param error graph error response object + * @param isVerbose whether to display a verbose message or not + * @return the Exception to be thrown + */ + @Nonnull + public static GraphServiceException createFromResponse(@Nullable final String url, @Nullable final String method, @Nonnull final List requestHeaders, + @Nullable final String requestBody, + @Nonnull final Map headers, @Nonnull final String responseMessage, final int responseCode, + @Nonnull final GraphErrorResponse error, final boolean isVerbose) { + final List responseHeaders = new LinkedList<>(); for (final String key : headers.keySet()) { final String fieldPrefix; if (key == null) { @@ -380,9 +404,7 @@ public static GraphServiceException createFromResponse(@Nonnull final IHttpR responseHeaders.add(fieldPrefix + headers.get(key)); } - final String responseMessage = response.message(); - GraphErrorResponse error = parseErrorResponse(serializer, response); if (responseCode >= INTERNAL_SERVER_ERROR) { return new GraphFatalServiceException(method, diff --git a/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java b/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java new file mode 100644 index 000000000..c857eb6b2 --- /dev/null +++ b/src/test/java/com/microsoft/graph/content/BatchRequestContentTest.java @@ -0,0 +1,245 @@ +package com.microsoft.graph.content; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +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; + +import java.net.MalformedURLException; +import java.net.URL; +import java.util.Arrays; +import java.util.ArrayList; +import java.util.HashSet; +import java.util.concurrent.ThreadLocalRandom; + +import com.google.gson.JsonObject; +import com.google.gson.JsonParser; +import com.microsoft.graph.authentication.IAuthenticationProvider; +import com.microsoft.graph.core.BaseClient; +import com.microsoft.graph.core.IBaseClient; +import com.microsoft.graph.http.CoreHttpProvider; +import com.microsoft.graph.http.HttpMethod; +import com.microsoft.graph.http.IHttpRequest; +import com.microsoft.graph.logger.ILogger; +import com.microsoft.graph.options.HeaderOption; +import com.microsoft.graph.serializer.AdditionalDataManager; +import com.microsoft.graph.serializer.DefaultSerializer; +import com.microsoft.graph.serializer.IJsonBackedObject; +import com.microsoft.graph.serializer.ISerializer; + +import org.junit.jupiter.api.Test; +import org.mockito.invocation.InvocationOnMock; +import org.mockito.stubbing.Answer; + +import okhttp3.Call; +import okhttp3.MediaType; +import okhttp3.OkHttpClient; +import okhttp3.Protocol; +import okhttp3.Request; +import okhttp3.Response; +import okhttp3.ResponseBody; + +public class BatchRequestContentTest { + + String testurl = "http://graph.microsoft.com/me"; + + @Test + public void testBatchRequestContentCreation() throws MalformedURLException { + BatchRequestContent requestContent = new BatchRequestContent(); + for (int i = 0; i < 5; i++) { + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + requestContent.addBatchRequestStep(requestStep); + } + assertNotNull(requestContent.requests); + } + + @Test + public void testGetBatchRequestContent() throws MalformedURLException { + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + BatchRequestContent requestContent = new BatchRequestContent(); + String stepId = requestContent.addBatchRequestStep(requestStep); + String content = new DefaultSerializer(mock(ILogger.class)).serializeObject(requestContent); + String expectedContent = "{\"requests\":[{\"url\":\"http://graph.microsoft.com/me\",\"method\":\"GET\",\"id\":\"" + + stepId + "\"}]}"; + assertEquals(expectedContent, content); + } + + @Test + public 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"))); + BatchRequestContent requestContent = new BatchRequestContent(); + String stepId = requestContent.addBatchRequestStep(requestStep); + String content = new DefaultSerializer(mock(ILogger.class)).serializeObject(requestContent); + String expectedContent = "{\"requests\":[{\"url\":\"http://graph.microsoft.com/me\",\"method\":\"GET\",\"id\":\"" + + stepId + "\",\"headers\":{\"testkey\":\"testvalue\"}}]}"; + assertEquals(expectedContent, content); + } + + @Test + public void testRemoveBatchRequesStepWithId() throws MalformedURLException { + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + BatchRequestContent requestContent = new BatchRequestContent(); + String stepId = requestContent.addBatchRequestStep(requestStep); + requestContent.removeBatchRequestStepWithId(stepId); + String content = new DefaultSerializer(mock(ILogger.class)).serializeObject(requestContent); + String expectedContent = "{\"requests\":[]}"; + assertEquals(expectedContent, content); + } + + @Test + public void testRemoveBatchRequesStepWithIdByAddingMultipleBatchSteps() throws MalformedURLException { + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + BatchRequestContent requestContent = new BatchRequestContent(); + String stepId = requestContent.addBatchRequestStep(requestStep); + + IHttpRequest requestStep1 = mock(IHttpRequest.class); + when(requestStep1.getRequestUrl()).thenReturn(new URL(testurl)); + + String step1Id = requestContent.addBatchRequestStep(requestStep1, HttpMethod.GET, null, stepId); + + requestContent.removeBatchRequestStepWithId(stepId); + String content = new DefaultSerializer(mock(ILogger.class)).serializeObject(requestContent); + String expectedContent = "{\"requests\":[{\"url\":\"http://graph.microsoft.com/me\",\"method\":\"GET\",\"id\":\"" + + step1Id + "\"}]}"; + assertEquals(expectedContent, content); + } + + @Test + public void defensiveProgrammingTests() { + assertThrows(NullPointerException.class, () -> { + new BatchRequestContent().addBatchRequestStep(null); + }, "should throw argument exception"); + assertThrows(NullPointerException.class, () -> { + new BatchRequestContent().addBatchRequestStep(mock(IHttpRequest.class), null); + }, "null http method throws"); + + assertThrows(NullPointerException.class, () -> { + new BatchRequestContent().getStepById(null); + }, "get step by id with null id throws"); + + assertThrows(NullPointerException.class, () -> { + new BatchRequestContent() { + { + requests = new ArrayList<>(); + } + }.removeBatchRequestStepWithId((String) null); + }, "remove step by id with null id throws"); + + assertThrows(IllegalArgumentException.class, () -> { + new BatchRequestContent().addBatchRequestStep(mock(IHttpRequest.class), HttpMethod.GET, null, "1"); + }, "dependency on inexisting step throws"); + } + + @Test + public void executeBatchTest() throws Throwable { + final BatchRequestContent content = new BatchRequestContent(); + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + final String stepId = content.addBatchRequestStep(requestStep); + + final OkHttpClient mHttpClient = mock(OkHttpClient.class); + final Call mCall = mock(Call.class); + when(mHttpClient.newCall(any(Request.class))).thenReturn(mCall); + + final CoreHttpProvider mHttpProvider = new CoreHttpProvider(new DefaultSerializer(mock(ILogger.class)), + mock(ILogger.class), mHttpClient); + final IBaseClient mClient = BaseClient.builder().authenticationProvider(mock(IAuthenticationProvider.class)) + .httpProvider(mHttpProvider).buildClient(); + final Response mResponse = new Response.Builder() + .request(new Request.Builder().url("https://graph.microsoft.com/v1.0/$batch").build()).code(200) + .protocol(Protocol.HTTP_1_1).message("OK").addHeader("Content-type", "application/json") + .body(ResponseBody.create( + "{\"responses\": [{\"id\": \"" + stepId + "\",\"status\": 200,\"body\": null}]}", + MediaType.parse("application/json"))) + .build(); + when(mCall.execute()).thenReturn(mResponse); + final BatchResponseContent batchResponse = mClient.batch().buildRequest().post(content); + assertNotNull(mClient.batch().buildRequest().postAsync(content)); + final BatchResponseStep response = batchResponse.getResponseById(stepId); + assertNotNull(response); + } + + @Test + public void usesHttpMethodFromRequestIfAlreadySet() throws MalformedURLException { + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + when(requestStep.getHttpMethod()).thenReturn(HttpMethod.DELETE); + final BatchRequestContent batchRequest = new BatchRequestContent(); + final String stepId = batchRequest.addBatchRequestStep(requestStep); + assertEquals(HttpMethod.DELETE.toString(), batchRequest.getStepById(stepId).method); + } + + @Test + public void doesNotThrowWhenTryingToRemoveRequestFromNull() { + final BatchRequestContent batchRequest = new BatchRequestContent(); + batchRequest.removeBatchRequestStepWithId("id"); + } + + @Test + public void doesNotRemoveDependsOnWhenNotEmpty() throws MalformedURLException { + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + final BatchRequestContent batchRequest = new BatchRequestContent(); + final String stepId = batchRequest.addBatchRequestStep(requestStep); + final String stepId2 = batchRequest.addBatchRequestStep(requestStep); + final String stepId3 = batchRequest.addBatchRequestStep(requestStep, HttpMethod.GET, null, stepId, stepId2); + + batchRequest.removeBatchRequestStepWithId(stepId); + + assertNotNull(batchRequest.getStepById(stepId3).dependsOn); + } + + @Test + public void addsContentTypeForBodies() throws MalformedURLException { + IHttpRequest requestStep = mock(IHttpRequest.class); + when(requestStep.getRequestUrl()).thenReturn(new URL(testurl)); + final BatchRequestContent batchRequest = new BatchRequestContent(); + final IJsonBackedObject body = new IJsonBackedObject() { + + @Override + public void setRawObject(ISerializer serializer, JsonObject json) { + // TODO Auto-generated method stub + + } + + @Override + public AdditionalDataManager additionalDataManager() { + // TODO Auto-generated method stub + return null; + } + + }; + final String stepId = batchRequest.addBatchRequestStep(requestStep, HttpMethod.POST, body); + final BatchRequestStep step = batchRequest.getStepById(stepId); + assertNotNull(step.headers); + assertEquals("application/json", step.headers.get("content-type")); + + when(requestStep.getHeaders()).thenReturn(Arrays.asList(new HeaderOption("dummy", "dummy"))); + final String stepId2 = batchRequest.addBatchRequestStep(requestStep, HttpMethod.POST, body); + final BatchRequestStep step2 = batchRequest.getStepById(stepId2); + assertNotNull(step2.headers); + assertEquals("application/json", step2.headers.get("content-type")); + + final String stepId3 = batchRequest.addBatchRequestStep(requestStep, HttpMethod.POST, Integer.valueOf(10)); + final BatchRequestStep step3 = batchRequest.getStepById(stepId3); + assertNotNull(step3.headers); + assertNull(step3.headers.get("content-type")); + + when(requestStep.getHeaders()).thenReturn(Arrays.asList(new HeaderOption("Content-type", "application/octet-stream"))); + final String stepId4 = batchRequest.addBatchRequestStep(requestStep, HttpMethod.POST, Integer.valueOf(10)); + final BatchRequestStep step4 = batchRequest.getStepById(stepId4); + assertNotNull(step4.headers); + assertEquals("application/octet-stream", step4.headers.get("content-type")); + } +} diff --git a/src/test/java/com/microsoft/graph/content/MSBatchResponseContentTest.java b/src/test/java/com/microsoft/graph/content/BatchResponseContentTest.java similarity index 66% rename from src/test/java/com/microsoft/graph/content/MSBatchResponseContentTest.java rename to src/test/java/com/microsoft/graph/content/BatchResponseContentTest.java index eda781691..27ae876e5 100644 --- a/src/test/java/com/microsoft/graph/content/MSBatchResponseContentTest.java +++ b/src/test/java/com/microsoft/graph/content/BatchResponseContentTest.java @@ -2,11 +2,22 @@ import static org.junit.jupiter.api.Assertions.assertThrows; import static org.junit.jupiter.api.Assertions.assertTrue; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertNull; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.mock; import java.io.IOException; import java.util.Iterator; import java.util.Map; +import com.google.gson.JsonElement; +import com.microsoft.graph.http.GraphErrorResponse; +import com.microsoft.graph.http.GraphServiceException; +import com.microsoft.graph.logger.ILogger; +import com.microsoft.graph.serializer.DefaultSerializer; +import com.microsoft.graph.serializer.ISerializer; + import org.junit.jupiter.api.Test; import okhttp3.MediaType; @@ -16,105 +27,95 @@ import okhttp3.Response; import okhttp3.ResponseBody; -public class MSBatchResponseContentTest { - - @Test - public void testNullMSBatchResponseContent() { - assertThrows(NullPointerException.class, () -> { - new MSBatchResponseContent((Response)null); - }, "should throw argument exception"); - } - +public class BatchResponseContentTest { @Test - public void testValidMSBatchResponseContent() { + public void testValidBatchResponseContent() { String responsebody = "{\"responses\": [{\"id\": \"1\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"businessPhones\":[\"8006427676\"],\"displayName\":\"MOD Administrator\",\"givenName\":\"MOD\",\"jobTitle\":null,\"mail\":\"admin@M365x751487.OnMicrosoft.com\",\"mobilePhone\":\"425-882-1032\",\"officeLocation\":null,\"preferredLanguage\":\"en-US\",\"surname\":\"Administrator\",\"userPrincipalName\":\"admin@M365x751487.onmicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\"}},{\"id\": \"2\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-store, no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#drives/$entity\",\"createdDateTime\":\"2019-01-12T09:05:38Z\",\"description\":\"\",\"id\":\"b!nlu9o5I9g0y8gsHXfUM_bPTZ0oM_wVNArHM5R4-VkHLlnxx5SpqHRJledwfICP9f\",\"lastModifiedDateTime\":\"2019-03-06T06:59:04Z\",\"name\":\"OneDrive\",\"webUrl\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents\",\"driveType\":\"business\",\"createdBy\":{\"user\":{\"displayName\":\"System Account\"}},\"lastModifiedBy\":{\"user\":{\"displayName\":\"System Account\"}},\"owner\":{\"user\":{\"email\":\"admin@M365x751487.OnMicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"quota\":{\"deleted\":0,\"remaining\":1099509670098,\"state\":\"normal\",\"total\":1099511627776,\"used\":30324}}},{\"id\": \"3\",\"status\":201,\"headers\" : {\"Location\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"Preference-Applied\":\"odata.include-annotations=*\",\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c')/onenote/notebooks/$entity\",\"id\":\"1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"self\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"createdDateTime\":\"2019-03-06T08:08:09Z\",\"displayName\":\"My Notebook -442293399\",\"lastModifiedDateTime\":\"2019-03-06T08:08:09Z\",\"isDefault\":false,\"userRole\":\"Owner\",\"isShared\":false,\"sectionsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sections\",\"sectionGroupsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sectionGroups\",\"createdBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"lastModifiedBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"links\":{\"oneNoteClientUrl\":{\"href\":\"onenote:https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"},\"oneNoteWebUrl\":{\"href\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"}}}}]}"; - String requestbody = "{\"requests\":[{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"1\",\"url\":\"me\"},{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"2\",\"url\":\"me\\/drive\"},{\"headers\":{\"content-type\":\"application\\/json\"},\"method\":\"POST\",\"dependsOn\":[],\"id\":\"3\",\"body\":{\"displayName\":\"My Notebook -1263732088\"},\"url\":\"me\\/onenote\\/notebooks\"}]}"; - Response responsedata = TestResponse(responsebody, requestbody); - MSBatchResponseContent batchresponse = new MSBatchResponseContent(responsedata); - assertTrue(batchresponse.getResponses() != null); + BatchResponseContent batchresponse = new DefaultSerializer(mock(ILogger.class)).deserializeObject(responsebody, BatchResponseContent.class); + assertTrue(batchresponse.responses != null); } @Test - public void testInvalidMSBatchResponseContentWithEmptyResponse() { + public void testInvalidBatchResponseContentWithEmptyResponse() { String responsebody = "{\"responses\": [] }"; - String requestbody = "{\"requests\":[]}"; - Response responsedata = TestResponse(responsebody, requestbody); - MSBatchResponseContent batchresponse = new MSBatchResponseContent(responsedata); + BatchResponseContent batchresponse = new DefaultSerializer(mock(ILogger.class)).deserializeObject(responsebody, BatchResponseContent.class); assertTrue(batchresponse.getResponseById("1") == null); } @Test - public void testInvalidMSBatchResponseContentWithNullResponseString() { - assertThrows(NullPointerException.class, () -> { - new MSBatchResponseContent(null); - }, "should throw argument exception"); - } - - @Test - public void testInvalidMSBatchResponseContentWithMalformedResponse() { + public void testInvalidBatchResponseContentWithMalformedResponse() { String invalidResponsebody = "{responses: [] }"; - String requestbody = "{requests:[]}"; - Response invalidResponsedata = TestResponse(invalidResponsebody, requestbody); - MSBatchResponseContent batchresponse = new MSBatchResponseContent(invalidResponsedata); - assertTrue(batchresponse.getResponses().keySet().isEmpty()); + BatchResponseContent batchresponse = new DefaultSerializer(mock(ILogger.class)).deserializeObject(invalidResponsebody, BatchResponseContent.class); + assertTrue(batchresponse.responses.isEmpty()); } @Test - public void testGetMSBatchResponseContentByID() throws IOException { + public void testGetBatchResponseContentByID() throws IOException { String responsebody = "{\"responses\": [{\"id\": \"1\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"businessPhones\":[\"8006427676\"],\"displayName\":\"MOD Administrator\",\"givenName\":\"MOD\",\"jobTitle\":null,\"mail\":\"admin@M365x751487.OnMicrosoft.com\",\"mobilePhone\":\"425-882-1032\",\"officeLocation\":null,\"preferredLanguage\":\"en-US\",\"surname\":\"Administrator\",\"userPrincipalName\":\"admin@M365x751487.onmicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\"}},{\"id\": \"2\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-store, no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#drives/$entity\",\"createdDateTime\":\"2019-01-12T09:05:38Z\",\"description\":\"\",\"id\":\"b!nlu9o5I9g0y8gsHXfUM_bPTZ0oM_wVNArHM5R4-VkHLlnxx5SpqHRJledwfICP9f\",\"lastModifiedDateTime\":\"2019-03-06T06:59:04Z\",\"name\":\"OneDrive\",\"webUrl\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents\",\"driveType\":\"business\",\"createdBy\":{\"user\":{\"displayName\":\"System Account\"}},\"lastModifiedBy\":{\"user\":{\"displayName\":\"System Account\"}},\"owner\":{\"user\":{\"email\":\"admin@M365x751487.OnMicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"quota\":{\"deleted\":0,\"remaining\":1099509670098,\"state\":\"normal\",\"total\":1099511627776,\"used\":30324}}},{\"id\": \"3\",\"status\":201,\"headers\" : {\"Location\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"Preference-Applied\":\"odata.include-annotations=*\",\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c')/onenote/notebooks/$entity\",\"id\":\"1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"self\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"createdDateTime\":\"2019-03-06T08:08:09Z\",\"displayName\":\"My Notebook -442293399\",\"lastModifiedDateTime\":\"2019-03-06T08:08:09Z\",\"isDefault\":false,\"userRole\":\"Owner\",\"isShared\":false,\"sectionsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sections\",\"sectionGroupsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sectionGroups\",\"createdBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"lastModifiedBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"links\":{\"oneNoteClientUrl\":{\"href\":\"onenote:https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"},\"oneNoteWebUrl\":{\"href\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"}}}}]}"; - String requestbody = "{\"requests\":[{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"1\",\"url\":\"me\"},{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"2\",\"url\":\"me\\/drive\"},{\"headers\":{\"content-type\":\"application\\/json\"},\"method\":\"POST\",\"dependsOn\":[],\"id\":\"3\",\"body\":{\"displayName\":\"My Notebook -1263732088\"},\"url\":\"me\\/onenote\\/notebooks\"}]}"; - Response responsedata = TestResponse(responsebody,requestbody); - MSBatchResponseContent batchresponse = new MSBatchResponseContent(responsedata); - Response response = batchresponse.getResponseById("1"); + BatchResponseContent batchresponse = new DefaultSerializer(mock(ILogger.class)).deserializeObject(responsebody, BatchResponseContent.class); + BatchResponseStep response = batchresponse.getResponseById("1"); assertTrue(response != null); } @Test - public void testGetMSBatchResponseContentIteratorOverResponse() throws IOException { + public void testGetBatchResponseContentIteratorOverResponse() throws IOException { String responsebody = "{\"responses\": [{\"id\": \"1\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"businessPhones\":[\"8006427676\"],\"displayName\":\"MOD Administrator\",\"givenName\":\"MOD\",\"jobTitle\":null,\"mail\":\"admin@M365x751487.OnMicrosoft.com\",\"mobilePhone\":\"425-882-1032\",\"officeLocation\":null,\"preferredLanguage\":\"en-US\",\"surname\":\"Administrator\",\"userPrincipalName\":\"admin@M365x751487.onmicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\"}},{\"id\": \"2\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-store, no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#drives/$entity\",\"createdDateTime\":\"2019-01-12T09:05:38Z\",\"description\":\"\",\"id\":\"b!nlu9o5I9g0y8gsHXfUM_bPTZ0oM_wVNArHM5R4-VkHLlnxx5SpqHRJledwfICP9f\",\"lastModifiedDateTime\":\"2019-03-06T06:59:04Z\",\"name\":\"OneDrive\",\"webUrl\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents\",\"driveType\":\"business\",\"createdBy\":{\"user\":{\"displayName\":\"System Account\"}},\"lastModifiedBy\":{\"user\":{\"displayName\":\"System Account\"}},\"owner\":{\"user\":{\"email\":\"admin@M365x751487.OnMicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"quota\":{\"deleted\":0,\"remaining\":1099509670098,\"state\":\"normal\",\"total\":1099511627776,\"used\":30324}}},{\"id\": \"3\",\"status\":201,\"headers\" : {\"Location\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"Preference-Applied\":\"odata.include-annotations=*\",\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c')/onenote/notebooks/$entity\",\"id\":\"1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"self\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"createdDateTime\":\"2019-03-06T08:08:09Z\",\"displayName\":\"My Notebook -442293399\",\"lastModifiedDateTime\":\"2019-03-06T08:08:09Z\",\"isDefault\":false,\"userRole\":\"Owner\",\"isShared\":false,\"sectionsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sections\",\"sectionGroupsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sectionGroups\",\"createdBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"lastModifiedBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"links\":{\"oneNoteClientUrl\":{\"href\":\"onenote:https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"},\"oneNoteWebUrl\":{\"href\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"}}}}]}"; - String requestbody = "{\"requests\":[{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"1\",\"url\":\"me\"},{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"2\",\"url\":\"me\\/drive\"},{\"headers\":{\"content-type\":\"application\\/json\"},\"method\":\"POST\",\"dependsOn\":[],\"id\":\"3\",\"body\":{\"displayName\":\"My Notebook -1263732088\"},\"url\":\"me\\/onenote\\/notebooks\"}]}"; - Response responsedata = TestResponse(responsebody,requestbody); - MSBatchResponseContent batchresponse = new MSBatchResponseContent(responsedata); - Iterator> it = batchresponse.getResponsesIterator(); + BatchResponseContent batchresponse = new DefaultSerializer(mock(ILogger.class)).deserializeObject(responsebody, BatchResponseContent.class); + Iterator> it = batchresponse.responses.iterator(); while(it.hasNext()) { - Map.Entry entry = it.next(); - assertTrue(entry.getKey()!=null && entry.getValue()!=null); + BatchResponseStep entry = it.next(); + assertTrue(entry.id != null && entry.body !=null); } } @Test - public void testGetMSBatchResponseContentMapResponse() throws IOException { + public void testGetBatchResponseContentMapResponse() throws IOException { String responsebody = "{\"responses\": [{\"id\": \"1\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"businessPhones\":[\"8006427676\"],\"displayName\":\"MOD Administrator\",\"givenName\":\"MOD\",\"jobTitle\":null,\"mail\":\"admin@M365x751487.OnMicrosoft.com\",\"mobilePhone\":\"425-882-1032\",\"officeLocation\":null,\"preferredLanguage\":\"en-US\",\"surname\":\"Administrator\",\"userPrincipalName\":\"admin@M365x751487.onmicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\"}},{\"id\": \"2\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-store, no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#drives/$entity\",\"createdDateTime\":\"2019-01-12T09:05:38Z\",\"description\":\"\",\"id\":\"b!nlu9o5I9g0y8gsHXfUM_bPTZ0oM_wVNArHM5R4-VkHLlnxx5SpqHRJledwfICP9f\",\"lastModifiedDateTime\":\"2019-03-06T06:59:04Z\",\"name\":\"OneDrive\",\"webUrl\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents\",\"driveType\":\"business\",\"createdBy\":{\"user\":{\"displayName\":\"System Account\"}},\"lastModifiedBy\":{\"user\":{\"displayName\":\"System Account\"}},\"owner\":{\"user\":{\"email\":\"admin@M365x751487.OnMicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"quota\":{\"deleted\":0,\"remaining\":1099509670098,\"state\":\"normal\",\"total\":1099511627776,\"used\":30324}}},{\"id\": \"3\",\"status\":201,\"headers\" : {\"Location\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"Preference-Applied\":\"odata.include-annotations=*\",\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c')/onenote/notebooks/$entity\",\"id\":\"1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"self\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"createdDateTime\":\"2019-03-06T08:08:09Z\",\"displayName\":\"My Notebook -442293399\",\"lastModifiedDateTime\":\"2019-03-06T08:08:09Z\",\"isDefault\":false,\"userRole\":\"Owner\",\"isShared\":false,\"sectionsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sections\",\"sectionGroupsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sectionGroups\",\"createdBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"lastModifiedBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"links\":{\"oneNoteClientUrl\":{\"href\":\"onenote:https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"},\"oneNoteWebUrl\":{\"href\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"}}}}]}"; - String requestbody = "{\"requests\":[{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"1\",\"url\":\"me\"},{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"2\",\"url\":\"me\\/drive\"},{\"headers\":{\"content-type\":\"application\\/json\"},\"method\":\"POST\",\"dependsOn\":[],\"id\":\"3\",\"body\":{\"displayName\":\"My Notebook -1263732088\"},\"url\":\"me\\/onenote\\/notebooks\"}]}"; - Response responsedata = TestResponse(responsebody,requestbody); - MSBatchResponseContent batchresponse = new MSBatchResponseContent(responsedata); - for(Map.Entry entry: batchresponse.getResponses().entrySet()) { - assertTrue(entry.getKey() != null && entry.getValue() != null); + BatchResponseContent batchresponse = new DefaultSerializer(mock(ILogger.class)).deserializeObject(responsebody, BatchResponseContent.class); + for(BatchResponseStep entry: batchresponse.responses) { + assertTrue(entry.id != null && entry.body != null); } } @Test - public void testGetMSBatchResponseContentUpdate() throws IOException { + public void testGetBatchResponseContentSize() throws IOException { String responsebody = "{\"responses\": [{\"id\": \"1\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"businessPhones\":[\"8006427676\"],\"displayName\":\"MOD Administrator\",\"givenName\":\"MOD\",\"jobTitle\":null,\"mail\":\"admin@M365x751487.OnMicrosoft.com\",\"mobilePhone\":\"425-882-1032\",\"officeLocation\":null,\"preferredLanguage\":\"en-US\",\"surname\":\"Administrator\",\"userPrincipalName\":\"admin@M365x751487.onmicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\"}},{\"id\": \"2\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-store, no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#drives/$entity\",\"createdDateTime\":\"2019-01-12T09:05:38Z\",\"description\":\"\",\"id\":\"b!nlu9o5I9g0y8gsHXfUM_bPTZ0oM_wVNArHM5R4-VkHLlnxx5SpqHRJledwfICP9f\",\"lastModifiedDateTime\":\"2019-03-06T06:59:04Z\",\"name\":\"OneDrive\",\"webUrl\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents\",\"driveType\":\"business\",\"createdBy\":{\"user\":{\"displayName\":\"System Account\"}},\"lastModifiedBy\":{\"user\":{\"displayName\":\"System Account\"}},\"owner\":{\"user\":{\"email\":\"admin@M365x751487.OnMicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"quota\":{\"deleted\":0,\"remaining\":1099509670098,\"state\":\"normal\",\"total\":1099511627776,\"used\":30324}}},{\"id\": \"3\",\"status\":201,\"headers\" : {\"Location\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"Preference-Applied\":\"odata.include-annotations=*\",\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c')/onenote/notebooks/$entity\",\"id\":\"1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"self\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"createdDateTime\":\"2019-03-06T08:08:09Z\",\"displayName\":\"My Notebook -442293399\",\"lastModifiedDateTime\":\"2019-03-06T08:08:09Z\",\"isDefault\":false,\"userRole\":\"Owner\",\"isShared\":false,\"sectionsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sections\",\"sectionGroupsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sectionGroups\",\"createdBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"lastModifiedBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"links\":{\"oneNoteClientUrl\":{\"href\":\"onenote:https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"},\"oneNoteWebUrl\":{\"href\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"}}}}]}"; - String requestbody = "{\"requests\":[{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"1\",\"url\":\"me\"},{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"2\",\"url\":\"me\\/drive\"},{\"headers\":{\"content-type\":\"application\\/json\"},\"method\":\"POST\",\"dependsOn\":[],\"id\":\"3\",\"body\":{\"displayName\":\"My Notebook -1263732088\"},\"url\":\"me\\/onenote\\/notebooks\"}]}"; - Response responsedata = TestResponse(responsebody,requestbody); - MSBatchResponseContent batchresponse = new MSBatchResponseContent(responsedata); - assertTrue(batchresponse.getResponses().size() == 3); - String responsebody2 = "{\"responses\": [{\"id\": \"4\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users/$entity\",\"businessPhones\":[\"8006427676\"],\"displayName\":\"MOD Administrator\",\"givenName\":\"MOD\",\"jobTitle\":null,\"mail\":\"admin@M365x751487.OnMicrosoft.com\",\"mobilePhone\":\"425-882-1032\",\"officeLocation\":null,\"preferredLanguage\":\"en-US\",\"surname\":\"Administrator\",\"userPrincipalName\":\"admin@M365x751487.onmicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\"}},{\"id\": \"5\",\"status\":200,\"headers\" : {\"Cache-Control\":\"no-store, no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#drives/$entity\",\"createdDateTime\":\"2019-01-12T09:05:38Z\",\"description\":\"\",\"id\":\"b!nlu9o5I9g0y8gsHXfUM_bPTZ0oM_wVNArHM5R4-VkHLlnxx5SpqHRJledwfICP9f\",\"lastModifiedDateTime\":\"2019-03-06T06:59:04Z\",\"name\":\"OneDrive\",\"webUrl\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents\",\"driveType\":\"business\",\"createdBy\":{\"user\":{\"displayName\":\"System Account\"}},\"lastModifiedBy\":{\"user\":{\"displayName\":\"System Account\"}},\"owner\":{\"user\":{\"email\":\"admin@M365x751487.OnMicrosoft.com\",\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"quota\":{\"deleted\":0,\"remaining\":1099509670098,\"state\":\"normal\",\"total\":1099511627776,\"used\":30324}}},{\"id\": \"6\",\"status\":201,\"headers\" : {\"Location\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"Preference-Applied\":\"odata.include-annotations=*\",\"Cache-Control\":\"no-cache\",\"OData-Version\":\"4.0\",\"Content-Type\":\"application/json;odata.metadata=minimal;odata.streaming=true;IEEE754Compatible=false;charset=utf-8\"},\"body\":{\"@odata.context\":\"https://graph.microsoft.com/v1.0/$metadata#users('6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c')/onenote/notebooks/$entity\",\"id\":\"1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"self\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0\",\"createdDateTime\":\"2019-03-06T08:08:09Z\",\"displayName\":\"My Notebook -442293399\",\"lastModifiedDateTime\":\"2019-03-06T08:08:09Z\",\"isDefault\":false,\"userRole\":\"Owner\",\"isShared\":false,\"sectionsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sections\",\"sectionGroupsUrl\":\"https://graph.microsoft.com/v1.0/users/6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c/onenote/notebooks/1-94e4376a-a1c1-441a-8b41-af5c86ee39d0/sectionGroups\",\"createdBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"lastModifiedBy\":{\"user\":{\"id\":\"6b4fa8ea-7e6e-486e-a8f4-d00a5b23488c\",\"displayName\":\"MOD Administrator\"}},\"links\":{\"oneNoteClientUrl\":{\"href\":\"onenote:https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"},\"oneNoteWebUrl\":{\"href\":\"https://m365x751487-my.sharepoint.com/personal/admin_m365x751487_onmicrosoft_com/Documents/Notebooks/My%20Notebook%20-442293399\"}}}}]}"; - String requestbody2 = "{\"requests\":[{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"4\",\"url\":\"me\"},{\"method\":\"GET\",\"dependsOn\":[],\"id\":\"5\",\"url\":\"me\\/drive\"},{\"headers\":{\"content-type\":\"application\\/json\"},\"method\":\"POST\",\"dependsOn\":[],\"id\":\"6\",\"body\":{\"displayName\":\"My Notebook -1263732088\"},\"url\":\"me\\/onenote\\/notebooks\"}]}"; - Response responsedata2 = TestResponse(responsebody2,requestbody2); - batchresponse.update(responsedata2); - assertTrue(batchresponse.getResponses().size() == 6); + BatchResponseContent batchresponse = new DefaultSerializer(mock(ILogger.class)).deserializeObject(responsebody, BatchResponseContent.class); + assertTrue(batchresponse.responses.size() == 3); } - - private Response TestResponse(String responsebody, String requestbody) { - Response.Builder builder = new Response.Builder(); - builder.body(ResponseBody.create(MediaType.parse("application/json"), responsebody)); - Request request = new Request.Builder().url("https://graph.microsoft.com/").method("POST", RequestBody.create(MediaType.parse("application/json"), requestbody)).build(); - builder.request(request); - builder.protocol(Protocol.HTTP_1_0); - builder.code(200); - builder.message("test message"); - return builder.build(); + @Test + public void responseParsing() { + final BatchResponseContent batchResponse = new DefaultSerializer(mock(ILogger.class)).deserializeObject("{\"responses\": [{\"id\": \"1\",\"status\": 200,\"body\": null}]}", BatchResponseContent.class); + assertEquals(1, batchResponse.responses.size()); + final BatchResponseStep response = batchResponse.getResponseById("1"); + assertNotNull(response); + assertNull(batchResponse.getResponseById("2")); + } + @Test + public void getResponseWithIdReturnsNullOnEmptyResponses() + { + final BatchResponseContent batchResponse = new BatchResponseContent(); + assertNull(batchResponse.getResponseById("id")); + } + @Test + public void deserializesErrorsProperly() { + String responsebody = "{\"responses\":[{\"id\":\"1\",\"status\":400,\"headers\":{\"Cache-Control\":\"no-cache\",\"x-ms-resource-unit\":\"1\",\"Content-Type\":\"application/json\"},\"body\":{\"error\":{\"code\":\"Request_BadRequest\",\"message\":\"Avalueisrequiredforproperty'displayName'ofresource'User'.\",\"innerError\":{\"date\":\"2021-02-02T19:19:38\",\"request-id\":\"408b8e64-4047-4c97-95b6-46e9f212ab48\",\"client-request-id\":\"102910da-260c-3028-0fb3-7d6903a02622\"}}}}]}"; + ISerializer serializer = new DefaultSerializer(mock(ILogger.class)); + BatchResponseContent batchresponse = serializer.deserializeObject(responsebody, BatchResponseContent.class); + if(batchresponse != null && batchresponse.responses != null) // this is done by the batch request in the fluent API + for(final BatchResponseStep step : batchresponse.responses) { + step.serializer = serializer; + } + assertThrows(GraphServiceException.class, () -> { + final Object body = batchresponse.getResponseById("1").getDeserializedBody(BatchRequestContent.class); + }); + try { + final Object body = batchresponse.getResponseById("1").getDeserializedBody(BatchRequestContent.class); + } catch(GraphServiceException ex) { + final GraphErrorResponse response = ex.getError(); + assertNotNull(response); + assertNotNull(response.error); + assertNotNull(response.error.message); + } } } diff --git a/src/test/java/com/microsoft/graph/content/MSBatchRequestContentTest.java b/src/test/java/com/microsoft/graph/content/MSBatchRequestContentTest.java deleted file mode 100644 index ab1fff017..000000000 --- a/src/test/java/com/microsoft/graph/content/MSBatchRequestContentTest.java +++ /dev/null @@ -1,201 +0,0 @@ -package com.microsoft.graph.content; - -import static org.junit.jupiter.api.Assertions.assertFalse; -import static org.junit.jupiter.api.Assertions.assertNotNull; -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; - -import java.util.HashSet; -import java.util.concurrent.ThreadLocalRandom; - -import com.google.gson.JsonParser; -import com.microsoft.graph.authentication.IAuthenticationProvider; -import com.microsoft.graph.core.BaseClient; -import com.microsoft.graph.core.IBaseClient; -import com.microsoft.graph.http.CoreHttpProvider; -import com.microsoft.graph.logger.ILogger; -import com.microsoft.graph.serializer.DefaultSerializer; - -import org.junit.jupiter.api.Test; -import org.mockito.invocation.InvocationOnMock; -import org.mockito.stubbing.Answer; - -import okhttp3.Call; -import okhttp3.MediaType; -import okhttp3.OkHttpClient; -import okhttp3.Protocol; -import okhttp3.Request; -import okhttp3.Response; -import okhttp3.ResponseBody; - -public class MSBatchRequestContentTest { - - String testurl = "http://graph.microsoft.com/me"; - - @Test - public void testMSBatchRequestContentCreation() { - MSBatchRequestContent requestContent = new MSBatchRequestContent(); - for (int i = 0; i < 5; i++) { - Request request = new Request.Builder().url(testurl).build(); - MSBatchRequestStep requestStep = new MSBatchRequestStep("" + i, request); - requestContent.addBatchRequestStep(requestStep); - } - assertTrue(requestContent.getBatchRequestContent() != null); - } - - @Test - public void testGetBatchRequestContent() { - Request request = new Request.Builder().url(testurl).build(); - MSBatchRequestStep requestStep = new MSBatchRequestStep("1", request); - MSBatchRequestContent requestContent = new MSBatchRequestContent(); - requestContent.addBatchRequestStep(requestStep); - String content = requestContent.getBatchRequestContent(); - String expectedContent = "{\"requests\":[{\"id\":\"1\",\"url\":\"http://graph.microsoft.com/me\",\"method\":\"GET\",\"dependsOn\":[]}]}"; - assertTrue(content.compareTo(expectedContent) == 0); - } - - @Test - public void testGetBatchRequestContentWithHeader() { - Request request = new Request.Builder().url(testurl).header("testkey", "testvalue").build(); - MSBatchRequestStep requestStep = new MSBatchRequestStep("1", request); - MSBatchRequestContent requestContent = new MSBatchRequestContent(); - requestContent.addBatchRequestStep(requestStep); - String content = requestContent.getBatchRequestContent(); - System.out.println(content); - String expectedContent = "{\"requests\":[{\"id\":\"1\",\"url\":\"http://graph.microsoft.com/me\",\"method\":\"GET\",\"headers\":{\"testkey\":\"testvalue\"},\"dependsOn\":[]}]}"; - assertTrue(content.compareTo(expectedContent) == 0); - } - - @Test - public void testRemoveBatchRequesStepWithId() { - Request request = new Request.Builder().url(testurl).build(); - MSBatchRequestStep requestStep = new MSBatchRequestStep("1", request); - MSBatchRequestContent requestContent = new MSBatchRequestContent(); - requestContent.addBatchRequestStep(requestStep); - requestContent.removeBatchRequestStepWithId("1"); - String content = requestContent.getBatchRequestContent(); - String expectedContent = "{\"requests\":[]}"; - assertTrue(content.compareTo(expectedContent) == 0); - } - - @Test - public void testRemoveBatchRequesStepWithIdByAddingMultipleBatchSteps() { - Request request = new Request.Builder().url(testurl).build(); - MSBatchRequestStep requestStep = new MSBatchRequestStep("1", request); - - Request request1 = new Request.Builder().url(testurl).build(); - MSBatchRequestStep requestStep1 = new MSBatchRequestStep("2", request1, "1"); - - MSBatchRequestContent requestContent = new MSBatchRequestContent(); - requestContent.addBatchRequestStep(requestStep); - requestContent.addBatchRequestStep(requestStep1); - - requestContent.removeBatchRequestStepWithId("1"); - String content = requestContent.getBatchRequestContent(); - String expectedContent = "{\"requests\":[{\"id\":\"2\",\"url\":\"http://graph.microsoft.com/me\",\"method\":\"GET\",\"dependsOn\":[]}]}"; - assertTrue(content.compareTo(expectedContent) == 0); - } - - @Test - public void defensiveProgrammingTests() { - final MSBatchRequestStep mockStep = mock(MSBatchRequestStep.class); - final HashSet reservedIds = new HashSet<>(); - when(mockStep.getRequestId()).thenAnswer(new Answer() { - @Override - public String answer(InvocationOnMock invocation) throws Throwable { - return getNewId(reservedIds); - } - }); - - assertThrows(IllegalArgumentException.class, () -> { - new MSBatchRequestContent(mock(ILogger.class), null, null, null, null, null, null, null, null, null, null, null, null, null, - null, null, null, null, null, null, null, null, null); - }, "the number of steps cannot exceed 20"); - assertThrows(NullPointerException.class, () -> { - new MSBatchRequestContent((ILogger) null, (MSBatchRequestStep)null); - }, "the logger cannot be null"); - - new MSBatchRequestContent(mockStep, null); // addind a null step doesn't throw - assertThrows(NullPointerException.class, () -> { - new MSBatchRequestContent().addBatchRequestStep(null); - }, "should throw argument exception"); - assertThrows(NullPointerException.class, () -> { - new MSBatchRequestContent().addBatchRequestStep((Request) null); - }, "should throw argument exception"); - - assertThrows(IllegalArgumentException.class, () -> { - final MSBatchRequestContent batchContent = new MSBatchRequestContent(); - for (int i = 0; i < MSBatchRequestContent.MAX_NUMBER_OF_REQUESTS; i++) { - assertNotNull(batchContent.addBatchRequestStep(mock(Request.class))); - } - batchContent.addBatchRequestStep(mock(Request.class)); - }, "the number of steps cannot exceed 20"); - { - final MSBatchRequestContent batchContent = new MSBatchRequestContent(); - reservedIds.clear(); - for (int i = 0; i < MSBatchRequestContent.MAX_NUMBER_OF_REQUESTS; i++) { - assertTrue(batchContent.addBatchRequestStep(mockStep), "item number " + i + " should be added successfully"); - } - assertFalse(batchContent.addBatchRequestStep(mockStep)); - } - } - - private String getNewId(final HashSet reserved) { - String requestId; - do { - requestId = Integer.toString(ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE)); - } while (reserved.contains(requestId)); - reserved.add(requestId); - return requestId; - } - - @Test - @SuppressWarnings("unchecked") - public void executeBatchTest() throws Throwable { - final MSBatchRequestStep step = new MSBatchRequestStep("1", new Request - .Builder() - .url(testurl) - .method("GET", null) - .build()); - final MSBatchRequestContent content = new MSBatchRequestContent(step); - final OkHttpClient mHttpClient = mock(OkHttpClient.class); - final Call mCall = mock(Call.class); - when(mHttpClient.newCall(any(Request.class))).thenReturn(mCall); - - final CoreHttpProvider mHttpProvider = new CoreHttpProvider(new DefaultSerializer(mock(ILogger.class)), mock(ILogger.class), mHttpClient); - final IBaseClient mClient = BaseClient.builder() - .authenticationProvider(mock(IAuthenticationProvider.class)) - .httpProvider(mHttpProvider) - .buildClient(); - final Response mResponse = new Response - .Builder() - .request(new Request - .Builder() - .url("https://graph.microsoft.com/v1.0/$batch") - .build()) - .code(200) - .protocol(Protocol.HTTP_1_1) - .message("OK") - .addHeader("Content-type", "application/json") - .body(ResponseBody.create("{\"responses\": [{\"id\": \"1\",\"status\": 200,\"body\": null}]}", - MediaType.parse("application/json"))) - .build(); - when(mCall.execute()).thenReturn(mResponse); - final MSBatchResponseContent batchResponse = content.execute(mClient); - final Response response = batchResponse.getResponseById("1"); - assertNotNull(response); - } - @Test - public void responseParsing() { - final MSBatchResponseContent batchResponse = new MSBatchResponseContent(mock(ILogger.class), "https://graph.microsoft.com/v1.0", - JsonParser.parseString("{\"requests\":[{\"id\":\"1\",\"method\":\"GET\",\"url\":\"/me\"}]}") - .getAsJsonObject(), - JsonParser.parseString("{\"responses\": [{\"id\": \"1\",\"status\": 200,\"body\": null}]}") - .getAsJsonObject()); - final Response response = batchResponse.getResponseById("1"); - assertNotNull(response); - } -} diff --git a/src/test/java/com/microsoft/graph/content/MSBatchRequestStepTest.java b/src/test/java/com/microsoft/graph/content/MSBatchRequestStepTest.java deleted file mode 100644 index 1e6d7bbf0..000000000 --- a/src/test/java/com/microsoft/graph/content/MSBatchRequestStepTest.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.microsoft.graph.content; - -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.junit.jupiter.api.Assertions.assertTrue; -import static org.mockito.Mockito.mock; - -import org.junit.jupiter.api.Test; - -import okhttp3.Request; - -public class MSBatchRequestStepTest { - - @Test - public void testMSBatchRequestStepCreation() { - Request request = new Request.Builder().url("http://graph.microsoft.com").build(); - MSBatchRequestStep requestStep = new MSBatchRequestStep("1", request); - assertTrue(requestStep != null, "Test BatchRequestStep creation"); - assertTrue(requestStep.getRequestId().compareTo("1") == 0, "Test Request id"); - assertTrue(requestStep.getRequest() == request, "Test Request object"); - assertTrue(requestStep.getDependsOnIds() != null, "Test Array of depends on Ids"); - } - - @Test - public void defensiveProgrammingTests() { - assertThrows(NullPointerException.class, () -> { - new MSBatchRequestStep(null, null); - }, "should throw argument exception"); - assertThrows(NullPointerException.class, () -> { - new MSBatchRequestStep("id", null); - }, "should throw argument exception"); - assertThrows(IllegalArgumentException.class, () -> { - new MSBatchRequestStep("", null); - }, "should throw argument exception"); - new MSBatchRequestStep("id", mock(Request.class)); - } - -}