Skip to content

Commit e3ac12d

Browse files
authored
Merge pull request #108 from microsoftgraph/feature/batches-execution
feature/batches execution
2 parents f849b69 + b26bcdd commit e3ac12d

File tree

11 files changed

+360
-125
lines changed

11 files changed

+360
-125
lines changed

src/main/java/com/microsoft/graph/content/MSBatchRequestContent.java

Lines changed: 58 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
11
package com.microsoft.graph.content;
22

33
import java.io.IOException;
4-
import java.util.Arrays;
4+
import java.util.HashSet;
55
import java.util.LinkedHashMap;
66
import java.util.List;
77
import java.util.Map;
8+
import java.util.concurrent.CompletableFuture;
89
import java.util.concurrent.ThreadLocalRandom;
910
import java.util.regex.Matcher;
1011
import java.util.regex.Pattern;
@@ -17,6 +18,8 @@
1718
import com.google.gson.JsonObject;
1819
import com.google.gson.JsonParser;
1920
import com.google.gson.JsonPrimitive;
21+
import com.microsoft.graph.core.ClientException;
22+
import com.microsoft.graph.core.IBaseClient;
2023
import com.google.gson.JsonParseException;
2124

2225
import okhttp3.Headers;
@@ -40,20 +43,14 @@ public class MSBatchRequestContent {
4043
*
4144
* @param batchRequestStepsArray List of batch steps for batching
4245
*/
43-
public MSBatchRequestContent(@Nonnull final List<MSBatchRequestStep> batchRequestStepsArray) {
44-
if (batchRequestStepsArray.size() > MAX_NUMBER_OF_REQUESTS)
46+
public MSBatchRequestContent(@Nonnull final MSBatchRequestStep... batchRequestStepsArray) {
47+
if (batchRequestStepsArray.length > MAX_NUMBER_OF_REQUESTS)
4548
throw new IllegalArgumentException("Number of batch request steps cannot exceed " + MAX_NUMBER_OF_REQUESTS);
4649

4750
this.batchRequestStepsHashMap = new LinkedHashMap<>();
4851
for (final MSBatchRequestStep requestStep : batchRequestStepsArray)
49-
addBatchRequestStep(requestStep);
50-
}
51-
52-
/**
53-
* Creates empty batch request content
54-
*/
55-
public MSBatchRequestContent() {
56-
this.batchRequestStepsHashMap = new LinkedHashMap<>();
52+
if(requestStep != null)
53+
addBatchRequestStep(requestStep);
5754
}
5855

5956
/**
@@ -63,6 +60,8 @@ public MSBatchRequestContent() {
6360
* given
6461
*/
6562
public boolean addBatchRequestStep(@Nonnull final MSBatchRequestStep batchRequestStep) {
63+
if(batchRequestStep == null)
64+
throw new IllegalArgumentException("batchRequestStep parameter cannot be null");
6665
if (batchRequestStepsHashMap.containsKey(batchRequestStep.getRequestId()) ||
6766
batchRequestStepsHashMap.size() >= MAX_NUMBER_OF_REQUESTS)
6867
return false;
@@ -78,11 +77,13 @@ public boolean addBatchRequestStep(@Nonnull final MSBatchRequestStep batchReques
7877
*/
7978
@Nonnull
8079
public String addBatchRequestStep(@Nonnull final Request request, @Nullable final String... arrayOfDependsOnIds) {
80+
if(request == null)
81+
throw new IllegalArgumentException("request parameter cannot be null");
8182
String requestId;
8283
do {
8384
requestId = Integer.toString(ThreadLocalRandom.current().nextInt(1, Integer.MAX_VALUE));
8485
} while(batchRequestStepsHashMap.keySet().contains(requestId));
85-
if(addBatchRequestStep(new MSBatchRequestStep(requestId, request, Arrays.asList(arrayOfDependsOnIds))))
86+
if(addBatchRequestStep(new MSBatchRequestStep(requestId, request, arrayOfDependsOnIds)))
8687
return requestId;
8788
else
8889
throw new IllegalArgumentException("unable to add step to batch. Number of batch request steps cannot exceed " + MAX_NUMBER_OF_REQUESTS);
@@ -100,29 +101,63 @@ public boolean removeBatchRequestStepWithId(@Nonnull final String requestId) {
100101
batchRequestStepsHashMap.remove(requestId);
101102
removed = true;
102103
for (final Map.Entry<String, MSBatchRequestStep> steps : batchRequestStepsHashMap.entrySet()) {
103-
if (steps.getValue() != null && steps.getValue().getArrayOfDependsOnIds() != null) {
104-
while (steps.getValue().getArrayOfDependsOnIds().remove(requestId))
104+
if (steps.getValue() != null && steps.getValue().getDependsOnIds() != null) {
105+
while (steps.getValue().getDependsOnIds().remove(requestId))
105106
;
106107
}
107108
}
108109
}
109110
return removed;
110111
}
111112

112-
/**
113-
* @return Batch request content's json as String
114-
*/
115-
@Nonnull
116-
public String getBatchRequestContent() {
113+
private JsonObject getBatchRequestContentAsJson() {
117114
final JsonObject batchRequestContentMap = new JsonObject();
118115
final JsonArray batchContentArray = new JsonArray();
119116
for (final Map.Entry<String, MSBatchRequestStep> requestStep : batchRequestStepsHashMap.entrySet()) {
120117
batchContentArray.add(getBatchRequestObjectFromRequestStep(requestStep.getValue()));
121118
}
122119
batchRequestContentMap.add("requests", batchContentArray);
120+
return batchRequestContentMap;
121+
}
122+
/**
123+
* @return Batch request content's json as String
124+
*/
125+
@Nonnull
126+
public String getBatchRequestContent() {
127+
return getBatchRequestContentAsJson().toString();
128+
}
123129

124-
final String content = batchRequestContentMap.toString();
125-
return content;
130+
/**
131+
* Executes the batch requests and returns the response
132+
* @param client client to use for the request
133+
* @return the batch response
134+
* @throws ClientException when the batch couldn't be executed because of client issues.
135+
*/
136+
@Nonnull
137+
public MSBatchResponseContent execute(@Nonnull final IBaseClient client) {
138+
final JsonObject content = getBatchRequestContentAsJson();
139+
return new MSBatchResponseContent(client.getServiceRoot() + "/",
140+
content,
141+
client.customRequest("/$batch")
142+
.buildRequest()
143+
.post(content)
144+
.getAsJsonObject());
145+
}
146+
/**
147+
* Executes the batch requests asynchronously and returns the response
148+
* @param client client to use for the request
149+
* @return a future with the batch response
150+
*/
151+
@Nonnull
152+
public CompletableFuture<MSBatchResponseContent> executeAsync(@Nonnull final IBaseClient client) {
153+
if(client == null) {
154+
throw new IllegalArgumentException("client parameter cannot be null");
155+
}
156+
final JsonObject content = getBatchRequestContentAsJson();
157+
return client.customRequest("/$batch")
158+
.buildRequest()
159+
.postAsync(content)
160+
.thenApply(resp -> new MSBatchResponseContent(client.getServiceRoot() + "/", content, resp.getAsJsonObject()));
126161
}
127162

128163
private static final Pattern protocolAndHostReplacementPattern = Pattern.compile("(?i)^http[s]?:\\/\\/graph\\.microsoft\\.com\\/(?>v1\\.0|beta)\\/?"); // (?i) case insensitive
@@ -146,9 +181,9 @@ private JsonObject getBatchRequestObjectFromRequestStep(final MSBatchRequestStep
146181
contentmap.add("headers", headerMap);
147182
}
148183

149-
final List<String> arrayOfDependsOnIds = batchRequestStep.getArrayOfDependsOnIds();
184+
final HashSet<String> arrayOfDependsOnIds = batchRequestStep.getDependsOnIds();
150185
if (arrayOfDependsOnIds != null) {
151-
final JsonArray array = new JsonArray();
186+
final JsonArray array = new JsonArray(arrayOfDependsOnIds.size());
152187
for (final String dependsOnId : arrayOfDependsOnIds)
153188
array.add(dependsOnId);
154189
contentmap.add("dependsOn", array);

src/main/java/com/microsoft/graph/content/MSBatchRequestStep.java

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
package com.microsoft.graph.content;
22

3-
import java.util.List;
3+
import java.util.Arrays;
4+
import java.util.HashSet;
45

56
import javax.annotation.Nullable;
67
import javax.annotation.Nonnull;
@@ -13,25 +14,25 @@
1314
public class MSBatchRequestStep {
1415
private String requestId;
1516
private Request request;
16-
private List<String> arrayOfDependsOnIds;
17+
private HashSet<String> dependsOnIds;
1718

1819
/**
1920
* Initializes a batch step from a raw HTTP request
2021
* @param requestId the id to assign to this step
2122
* @param request the request to send in the batch
2223
* @param arrayOfDependsOnIds the ids of steps this step depends on
2324
*/
24-
public MSBatchRequestStep(@Nonnull final String requestId, @Nonnull final Request request, @Nullable final List<String> arrayOfDependsOnIds) {
25+
public MSBatchRequestStep(@Nonnull final String requestId, @Nonnull final Request request, @Nullable final String... arrayOfDependsOnIds) {
2526
if(requestId == null)
2627
throw new IllegalArgumentException("Request Id cannot be null.");
2728
if(requestId.length() == 0)
2829
throw new IllegalArgumentException("Request Id cannot be empty.");
2930
if(request == null)
30-
new IllegalArgumentException("Request cannot be null.");
31+
throw new IllegalArgumentException("Request cannot be null.");
3132

3233
this.requestId = requestId;
3334
this.request = request;
34-
this.arrayOfDependsOnIds = arrayOfDependsOnIds;
35+
this.dependsOnIds = new HashSet<>(Arrays.asList(arrayOfDependsOnIds));
3536
}
3637

3738
/**
@@ -57,7 +58,7 @@ public Request getRequest() {
5758
* @return the list of steps this step depends on
5859
*/
5960
@Nullable
60-
public List<String> getArrayOfDependsOnIds(){
61-
return arrayOfDependsOnIds;
61+
public HashSet<String> getDependsOnIds(){
62+
return dependsOnIds;
6263
}
6364
}

src/main/java/com/microsoft/graph/content/MSBatchResponseContent.java

Lines changed: 58 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
import com.google.gson.JsonParseException;
1616

1717
import okhttp3.MediaType;
18+
import okhttp3.Protocol;
1819
import okhttp3.Request;
1920
import okhttp3.RequestBody;
2021
import okhttp3.Response;
@@ -26,17 +27,34 @@
2627
*/
2728
public class MSBatchResponseContent {
2829

29-
private final Response batchResponse;
30-
private LinkedHashMap<String, Request> batchRequestsHashMap;
30+
private final Protocol protocol;
31+
private final String message;
32+
private LinkedHashMap<String, Request> batchRequestsHashMap = new LinkedHashMap<>();
3133
private JsonArray batchResponseArray;
3234
private String nextLink;
3335

3436
/**
3537
* @param batchResponse OkHttp batch response on execution of batch requests
3638
*/
3739
public MSBatchResponseContent(@Nullable final Response batchResponse) {
38-
this.batchResponse = batchResponse;
3940
update(batchResponse);
41+
this.message = batchResponse.message();
42+
this.protocol = batchResponse.protocol();
43+
}
44+
/**
45+
* instantiates a new response
46+
* internal only, used when the content executes the requests
47+
* @param baseUrl the base service URL without a trailing slash
48+
* @param batchRequestData the batch request payload data as a JSON string
49+
* @param batchResponseData the batch response body as a JSON string
50+
*/
51+
protected MSBatchResponseContent(@Nonnull final String baseUrl, @Nonnull final JsonObject batchRequestData, @Nonnull final JsonObject batchResponseData) {
52+
this.protocol = Protocol.HTTP_1_1;
53+
this.message = "OK";
54+
final Map<String, Request> requestMap = createBatchRequestsHashMap(baseUrl, batchRequestData);
55+
if (requestMap != null)
56+
batchRequestsHashMap.putAll(requestMap);
57+
updateFromResponseBody(batchResponseData);
4058
}
4159

4260
/**
@@ -66,14 +84,13 @@ public Response getResponseById(@Nonnull final String requestId) {
6684
// Put corresponding request into the constructed response
6785
builder.request(batchRequestsHashMap.get(requestId));
6886
// copy protocol and message same as of batch response
69-
builder.protocol(batchResponse.protocol());
70-
builder.message(batchResponse.message());
87+
builder.protocol(protocol);
88+
builder.message(message);
7189

7290
// Put status code of the corresponding request in JsonArray
7391
final JsonElement statusElement = jsonresponse.get("status");
7492
if (statusElement != null && statusElement.isJsonPrimitive()) {
75-
final Long status = statusElement.getAsLong();
76-
builder.code(status.intValue());
93+
builder.code(statusElement.getAsInt());
7794
}
7895

7996
// Put body from response array for corresponding id into constructing response
@@ -82,7 +99,7 @@ public Response getResponseById(@Nonnull final String requestId) {
8299
final JsonObject JsonObject = jsonBodyElement.getAsJsonObject();
83100
final String bodyAsString = JsonObject.toString();
84101
final ResponseBody responseBody = ResponseBody
85-
.create(MediaType.parse("application/json; charset=utf-8"), bodyAsString);
102+
.create(bodyAsString, MediaType.parse("application/json; charset=utf-8"));
86103
builder.body(responseBody);
87104
}
88105

@@ -144,37 +161,36 @@ public void update(@Nonnull final Response batchResponse) {
144161
throw new IllegalArgumentException("Batch Response cannot be null");
145162

146163
final Map<String, Request> requestMap = createBatchRequestsHashMap(batchResponse);
147-
if (batchRequestsHashMap == null)
148-
batchRequestsHashMap = new LinkedHashMap<>();
149164
if (requestMap != null)
150165
batchRequestsHashMap.putAll(requestMap);
151166

152167
if (batchResponse.body() != null) {
153168
try {
154169
final String batchResponseData = batchResponse.body().string();
155170
if (batchResponseData != null) {
156-
final JsonObject batchResponseObj = stringToJSONObject(batchResponseData);
157-
if (batchResponseObj != null) {
158-
159-
final JsonElement nextLinkElement = batchResponseObj.get("@odata.nextLink");
160-
if (nextLinkElement != null && nextLinkElement.isJsonPrimitive())
161-
nextLink = nextLinkElement.getAsString();
162-
163-
if (batchResponseArray == null)
164-
batchResponseArray = new JsonArray();
165-
166-
final JsonElement responseArrayElement = batchResponseObj.get("responses");
167-
if (responseArrayElement != null && responseArrayElement.isJsonArray()) {
168-
final JsonArray responseArray = responseArrayElement.getAsJsonArray();
169-
batchResponseArray.addAll(responseArray);
170-
}
171-
}
171+
updateFromResponseBody(stringToJSONObject(batchResponseData));
172172
}
173173
} catch (final IOException e) {
174174
e.printStackTrace();
175175
}
176176
}
177177
}
178+
private void updateFromResponseBody(@Nonnull final JsonObject batchResponseObj) {
179+
if (batchResponseObj != null) {
180+
final JsonElement nextLinkElement = batchResponseObj.get("@odata.nextLink");
181+
if (nextLinkElement != null && nextLinkElement.isJsonPrimitive())
182+
nextLink = nextLinkElement.getAsString();
183+
184+
if (batchResponseArray == null)
185+
batchResponseArray = new JsonArray();
186+
187+
final JsonElement responseArrayElement = batchResponseObj.get("responses");
188+
if (responseArrayElement != null && responseArrayElement.isJsonArray()) {
189+
final JsonArray responseArray = responseArrayElement.getAsJsonArray();
190+
batchResponseArray.addAll(responseArray);
191+
}
192+
}
193+
}
178194

179195
/**
180196
* @return nextLink of batch response
@@ -188,8 +204,20 @@ private Map<String, Request> createBatchRequestsHashMap(final Response batchResp
188204
if (batchResponse == null)
189205
return null;
190206
try {
191-
final Map<String, Request> batchRequestsHashMap = new LinkedHashMap<>();
192207
final JsonObject requestJSONObject = requestBodyToJSONObject(batchResponse.request());
208+
final String baseUrl = batchResponse.request().url().toString().replace("$batch", "");
209+
return createBatchRequestsHashMap(baseUrl, requestJSONObject);
210+
} catch (IOException ex) {
211+
ex.printStackTrace();
212+
return null;
213+
}
214+
}
215+
private Map<String, Request> createBatchRequestsHashMap(@Nonnull final String baseUrl, @Nonnull final JsonObject requestJSONObject) {
216+
if(baseUrl == null || baseUrl == "" || requestJSONObject == null) {
217+
return null;
218+
}
219+
try {
220+
final Map<String, Request> batchRequestsHashMap = new LinkedHashMap<>();
193221
final JsonElement requestArrayElement = requestJSONObject.get("requests");
194222
if (requestArrayElement != null && requestArrayElement.isJsonArray()) {
195223
final JsonArray requestArray = requestArrayElement.getAsJsonArray();
@@ -202,8 +230,7 @@ private Map<String, Request> createBatchRequestsHashMap(final Response batchResp
202230

203231
final JsonElement urlElement = requestObject.get("url");
204232
if (urlElement != null && urlElement.isJsonPrimitive()) {
205-
final StringBuilder fullUrl = new StringBuilder(
206-
batchResponse.request().url().toString().replace("$batch", ""));
233+
final StringBuilder fullUrl = new StringBuilder(baseUrl);
207234
fullUrl.append(urlElement.getAsString());
208235
builder.url(fullUrl.toString());
209236
}
@@ -227,7 +254,7 @@ private Map<String, Request> createBatchRequestsHashMap(final Response batchResp
227254
final JsonObject JsonObject = jsonBodyElement.getAsJsonObject();
228255
final String bodyAsString = JsonObject.toString();
229256
final RequestBody requestBody = RequestBody
230-
.create(MediaType.parse("application/json; charset=utf-8"), bodyAsString);
257+
.create(bodyAsString, MediaType.parse("application/json; charset=utf-8"));
231258
builder.method(jsonMethodElement.getAsString(), requestBody);
232259
} else if (jsonMethodElement != null) {
233260
builder.method(jsonMethodElement.getAsString(), null);
@@ -240,7 +267,7 @@ private Map<String, Request> createBatchRequestsHashMap(final Response batchResp
240267
}
241268
return batchRequestsHashMap;
242269

243-
} catch (IOException | JsonParseException e) {
270+
} catch (JsonParseException e) {
244271
e.printStackTrace();
245272
}
246273
return null;

0 commit comments

Comments
 (0)