001package com.box.sdk;
002
003import java.util.ArrayList;
004import java.util.HashMap;
005import java.util.Iterator;
006import java.util.List;
007import java.util.Map;
008
009import com.box.sdk.http.HttpHeaders;
010import com.box.sdk.http.HttpMethod;
011import com.eclipsesource.json.JsonArray;
012import com.eclipsesource.json.JsonObject;
013import com.eclipsesource.json.JsonValue;
014
015/**
016 * Used to make a bunch of HTTP Requests as a batch. Currently the number of requests that can be batched at once
017 * is capped at <b>20</b> by the API layer. Also there are certain requests which <b>cannot</b> be performed
018 * by Batch API. Check API documentation for more information.
019 *
020 * <p>The request itself is a BoxJSONRequest but extends it to provide additional functionality for aggregating
021 *  a bunch of requests and responding with multiple responses as if the requests were called individually</p>
022 */
023public class BatchAPIRequest extends BoxJSONRequest {
024    /**
025     * Batch URL Template.
026     */
027    public static final URLTemplate BATCH_URL_TEMPLATE = new URLTemplate("batch");
028    private final BoxAPIConnection api;
029
030    /**
031     * Constructs an authenticated BatchRequest using a provided BoxAPIConnection.
032     * @param  api    an API connection for authenticating the request.
033     */
034    public BatchAPIRequest(BoxAPIConnection api) {
035        super(api, BATCH_URL_TEMPLATE.build(api.getBaseURL()), HttpMethod.GET);
036        this.api = api;
037    }
038
039    /**
040     * Execute a set of API calls as batch request.
041     * @param requests list of api requests that has to be executed in batch.
042     * @return list of BoxAPIResponses
043     */
044    public List<BoxAPIResponse> execute(List<BoxAPIRequest> requests) {
045        this.prepareRequest(requests);
046        BoxJSONResponse batchResponse = (BoxJSONResponse) send();
047        return this.parseResponse(batchResponse);
048    }
049
050    /**
051     * Prepare a batch api request using list of individual reuests.
052     * @param requests list of api requests that has to be executed in batch.
053     */
054    protected void prepareRequest(List<BoxAPIRequest> requests) {
055        JsonObject body = new JsonObject();
056        JsonArray requestsJSONArray = new JsonArray();
057        for (BoxAPIRequest request: requests) {
058            JsonObject batchRequest = new JsonObject();
059            batchRequest.add("method", request.getMethod());
060            batchRequest.add("relative_url", request.getUrl().toString().substring(this.api.getBaseURL().length() - 1));
061            //If the actual request has a JSON body then add it to vatch request
062            if (request instanceof BoxJSONRequest) {
063                BoxJSONRequest jsonRequest = (BoxJSONRequest) request;
064                batchRequest.add("body", jsonRequest.getBodyAsJsonObject());
065            }
066            //Add any headers that are in the request, except Authorization
067            if (request.getHeaders() != null) {
068                JsonObject batchRequestHeaders = new JsonObject();
069                for (RequestHeader header: request.getHeaders()) {
070                    if (header.getKey() != null && !header.getKey().isEmpty()
071                        && HttpHeaders.AUTHORIZATION.equals(header.getKey())) {
072                        batchRequestHeaders.add(header.getKey(), header.getValue());
073                    }
074                }
075                batchRequest.add("headers", batchRequestHeaders);
076            }
077
078            //Add the request to array
079            requestsJSONArray.add(batchRequest);
080        }
081        //Add the requests array to body
082        body.add("requests", requestsJSONArray);
083        super.setBody(body);
084    }
085
086    /**
087     * Parses btch api response to create a list of BoxAPIResponse objects.
088     * @param batchResponse response of a batch api request
089     * @return list of BoxAPIResponses
090     */
091    protected List<BoxAPIResponse> parseResponse(BoxJSONResponse batchResponse) {
092        JsonObject responseJSON = JsonObject.readFrom(batchResponse.getJSON());
093        List<BoxAPIResponse> responses = new ArrayList<BoxAPIResponse>();
094        Iterator<JsonValue> responseIterator = responseJSON.get("responses").asArray().iterator();
095        while (responseIterator.hasNext()) {
096            JsonObject jsonResponse = responseIterator.next().asObject();
097            BoxAPIResponse response = null;
098
099            //Gather headers
100            Map<String, String> responseHeaders = new HashMap<String, String>();
101
102            if (jsonResponse.get("headers") != null) {
103                JsonObject batchResponseHeadersObject = jsonResponse.get("headers").asObject();
104                for (JsonObject.Member member : batchResponseHeadersObject) {
105                    String headerName = member.getName();
106                    String headerValue = member.getValue().asString();
107                    responseHeaders.put(headerName, headerValue);
108                }
109            }
110
111            // Construct a BoxAPIResponse when response is null, or a BoxJSONResponse when there's a response
112            // (not anticipating any other response as per current APIs.
113            // Ideally we should do it based on response header)
114            if (jsonResponse.get("response") == null) {
115                response =
116                    new BoxAPIResponse(jsonResponse.get("status").asInt(), responseHeaders);
117            } else {
118                response =
119                    new BoxJSONResponse(jsonResponse.get("status").asInt(), responseHeaders,
120                        jsonResponse.get("response").asObject());
121            }
122            responses.add(response);
123        }
124        return responses;
125    }
126}