blob: 84739601a6673dc77f6429a1bdb96d2d8db8752e [file] [log] [blame]
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>JSDoc: Source: odata/batch.js</title>
<script src="scripts/prettify/prettify.js"> </script>
<script src="scripts/prettify/lang-css.js"> </script>
<link type="text/css" rel="stylesheet" href="styles/prettify-tomorrow.css">
<link type="text/css" rel="stylesheet" href="styles/jsdoc-default.css">
</head>
<body>
<div id="main">
<h1 class="page-title">Source: odata/batch.js</h1>
<section>
<article>
<pre class="prettyprint source"><code>/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
'use strict';
/** @module odata/batch */
var utils = require('./../utils.js');
var odataUtils = require('./odatautils.js');
var odataHandler = require('./handler.js');
var extend = utils.extend;
var isArray = utils.isArray;
var trimString = utils.trimString;
var contentType = odataHandler.contentType;
var handler = odataHandler.handler;
var isBatch = odataUtils.isBatch;
var MAX_DATA_SERVICE_VERSION = odataHandler.MAX_DATA_SERVICE_VERSION;
var normalizeHeaders = odataUtils.normalizeHeaders;
//TODO var payloadTypeOf = odata.payloadTypeOf;
var prepareRequest = odataUtils.prepareRequest;
// Imports
// CONTENT START
var batchMediaType = "multipart/mixed";
var responseStatusRegex = /^HTTP\/1\.\d (\d{3}) (.*)$/i;
var responseHeaderRegex = /^([^()&lt;>@,;:\\"\/[\]?={} \t]+)\s?:\s?(.*)/;
/** Calculates a random 16 bit number and returns it in hexadecimal format.
* @returns {String} A 16-bit number in hex format.
*/
function hex16() {
return Math.floor((1 + Math.random()) * 0x10000).toString(16).substr(1);
}
/** Creates a string that can be used as a multipart request boundary.
* @param {String} [prefix] -
* @returns {String} Boundary string of the format: &lt;prefix>&lt;hex16>-&lt;hex16>-&lt;hex16>
*/
function createBoundary(prefix) {
return prefix + hex16() + "-" + hex16() + "-" + hex16();
}
/** Gets the handler for data serialization of individual requests / responses in a batch.
* @param context - Context used for data serialization.
* @returns Handler object
*/
function partHandler(context) {
return context.handler.partHandler;
}
/** Gets the current boundary used for parsing the body of a multipart response.
* @param context - Context used for parsing a multipart response.
* @returns {String} Boundary string.
*/
function currentBoundary(context) {
var boundaries = context.boundaries;
return boundaries[boundaries.length - 1];
}
/** Parses a batch response.
* @param handler - This handler.
* @param {String} text - Batch text.
* @param {Object} context - Object with parsing context.
* @return An object representation of the batch.
*/
function batchParser(handler, text, context) {
var boundary = context.contentType.properties["boundary"];
return { __batchResponses: readBatch(text, { boundaries: [boundary], handlerContext: context }) };
}
/** Serializes a batch object representation into text.
* @param handler - This handler.
* @param {Object} data - Representation of a batch.
* @param {Object} context - Object with parsing context.
* @return An text representation of the batch object; undefined if not applicable.#
*/
function batchSerializer(handler, data, context) {
var cType = context.contentType = context.contentType || contentType(batchMediaType);
if (cType.mediaType === batchMediaType) {
return writeBatch(data, context);
}
}
/** Parses a multipart/mixed response body from from the position defined by the context.
* @param {String} text - Body of the multipart/mixed response.
* @param context - Context used for parsing.
* @return Array of objects representing the individual responses.
*/
function readBatch(text, context) {
var delimiter = "--" + currentBoundary(context);
// Move beyond the delimiter and read the complete batch
readTo(text, context, delimiter);
// Ignore the incoming line
readLine(text, context);
// Read the batch parts
var responses = [];
var partEnd = null;
while (partEnd !== "--" && context.position &lt; text.length) {
var partHeaders = readHeaders(text, context);
var partContentType = contentType(partHeaders["Content-Type"]);
var changeResponses;
if (partContentType && partContentType.mediaType === batchMediaType) {
context.boundaries.push(partContentType.properties.boundary);
try {
changeResponses = readBatch(text, context);
} catch (e) {
e.response = readResponse(text, context, delimiter);
changeResponses = [e];
}
responses.push({ __changeResponses: changeResponses });
context.boundaries.pop();
readTo(text, context, "--" + currentBoundary(context));
} else {
if (!partContentType || partContentType.mediaType !== "application/http") {
throw { message: "invalid MIME part type " };
}
// Skip empty line
readLine(text, context);
// Read the response
var response = readResponse(text, context, delimiter);
try {
if (response.statusCode >= 200 && response.statusCode &lt;= 299) {
partHandler(context.handlerContext).read(response, context.handlerContext);
} else {
// Keep track of failed responses and continue processing the batch.
response = { message: "HTTP request failed", response: response };
}
} catch (e) {
response = e;
}
responses.push(response);
}
partEnd = text.substr(context.position, 2);
// Ignore the incoming line.
readLine(text, context);
}
return responses;
}
/** Parses the http headers in the text from the position defined by the context.
* @param {String} text - Text containing an http response's headers
* @param context - Context used for parsing.
* @returns Object containing the headers as key value pairs.
* This function doesn't support split headers and it will stop reading when it hits two consecutive line breaks.
*/
function readHeaders(text, context) {
var headers = {};
var parts;
var line;
var pos;
do {
pos = context.position;
line = readLine(text, context);
parts = responseHeaderRegex.exec(line);
if (parts !== null) {
headers[parts[1]] = parts[2];
} else {
// Whatever was found is not a header, so reset the context position.
context.position = pos;
}
} while (line && parts);
normalizeHeaders(headers);
return headers;
}
/** Parses an HTTP response.
* @param {String} text -Text representing the http response.
* @param context optional - Context used for parsing.
* @param {String} delimiter -String used as delimiter of the multipart response parts.
* @return Object representing the http response.
*/
function readResponse(text, context, delimiter) {
// Read the status line.
var pos = context.position;
var match = responseStatusRegex.exec(readLine(text, context));
var statusCode;
var statusText;
var headers;
if (match) {
statusCode = match[1];
statusText = match[2];
headers = readHeaders(text, context);
readLine(text, context);
} else {
context.position = pos;
}
return {
statusCode: statusCode,
statusText: statusText,
headers: headers,
body: readTo(text, context, "\r\n" + delimiter)
};
}
/** Returns a substring from the position defined by the context up to the next line break (CRLF).
* @param {String} text - Input string.
* @param context - Context used for reading the input string.
* @returns {String} Substring to the first ocurrence of a line break or null if none can be found.
*/
function readLine(text, context) {
return readTo(text, context, "\r\n");
}
/** Returns a substring from the position given by the context up to value defined by the str parameter and increments the position in the context.
* @param {String} text - Input string.
* @param context - Context used for reading the input string.
* @param {String} [str] - Substring to read up to.
* @returns {String} Substring to the first ocurrence of str or the end of the input string if str is not specified. Null if the marker is not found.
*/
function readTo(text, context, str) {
var start = context.position || 0;
var end = text.length;
if (str) {
end = text.indexOf(str, start);
if (end === -1) {
return null;
}
context.position = end + str.length;
} else {
context.position = end;
}
return text.substring(start, end);
}
/** Serializes a batch request object to a string.
* @param data - Batch request object in payload representation format
* @param context - Context used for the serialization
* @returns {String} String representing the batch request
*/
function writeBatch(data, context) {
if (!isBatch(data)) {
throw { message: "Data is not a batch object." };
}
var batchBoundary = createBoundary("batch_");
var batchParts = data.__batchRequests;
var batch = "";
var i, len;
for (i = 0, len = batchParts.length; i &lt; len; i++) {
batch += writeBatchPartDelimiter(batchBoundary, false) +
writeBatchPart(batchParts[i], context);
}
batch += writeBatchPartDelimiter(batchBoundary, true);
// Register the boundary with the request content type.
var contentTypeProperties = context.contentType.properties;
contentTypeProperties.boundary = batchBoundary;
return batch;
}
/** Creates the delimiter that indicates that start or end of an individual request.
* @param {String} boundary Boundary string used to indicate the start of the request
* @param {Boolean} close - Flag indicating that a close delimiter string should be generated
* @returns {String} Delimiter string
*/
function writeBatchPartDelimiter(boundary, close) {
var result = "\r\n--" + boundary;
if (close) {
result += "--";
}
return result + "\r\n";
}
/** Serializes a part of a batch request to a string. A part can be either a GET request or
* a change set grouping several CUD (create, update, delete) requests.
* @param part - Request or change set object in payload representation format
* @param context - Object containing context information used for the serialization
* @param {boolean} [nested] -
* @returns {String} String representing the serialized part
* A change set is an array of request objects and they cannot be nested inside other change sets.
*/
function writeBatchPart(part, context, nested) {
var changeSet = part.__changeRequests;
var result;
if (isArray(changeSet)) {
if (nested) {
throw { message: "Not Supported: change set nested in other change set" };
}
var changeSetBoundary = createBoundary("changeset_");
result = "Content-Type: " + batchMediaType + "; boundary=" + changeSetBoundary + "\r\n";
var i, len;
for (i = 0, len = changeSet.length; i &lt; len; i++) {
result += writeBatchPartDelimiter(changeSetBoundary, false) +
writeBatchPart(changeSet[i], context, true);
}
result += writeBatchPartDelimiter(changeSetBoundary, true);
} else {
result = "Content-Type: application/http\r\nContent-Transfer-Encoding: binary\r\n\r\n";
var partContext = extend({}, context);
partContext.handler = handler;
partContext.request = part;
partContext.contentType = null;
prepareRequest(part, partHandler(context), partContext);
result += writeRequest(part);
}
return result;
}
/** Serializes a request object to a string.
* @param request - Request object to serialize
* @returns {String} String representing the serialized request
*/
function writeRequest(request) {
var result = (request.method ? request.method : "GET") + " " + request.requestUri + " HTTP/1.1\r\n";
for (var name in request.headers) {
if (request.headers[name]) {
result = result + name + ": " + request.headers[name] + "\r\n";
}
}
result += "\r\n";
if (request.body) {
result += request.body;
}
return result;
}
/** batchHandler (see {@link module:odata/batch~batchParser}) */
exports.batchHandler = handler(batchParser, batchSerializer, batchMediaType, MAX_DATA_SERVICE_VERSION);
/** batchSerializer (see {@link module:odata/batch~batchSerializer}) */
exports.batchSerializer = batchSerializer;
/** writeRequest (see {@link module:odata/batch~writeRequest}) */
exports.writeRequest = writeRequest;</code></pre>
</article>
</section>
</div>
<nav>
<h2><a href="index.html">Index</a></h2><h3>Modules</h3><ul><li><a href="module-cache.html">cache</a></li><li><a href="source.html">cache/source</a></li><li><a href="module-odata.html">odata</a></li><li><a href="batch.html">odata/batch</a></li><li><a href="handler.html">odata/handler</a></li><li><a href="json.html">odata/json</a></li><li><a href="metadata.html">odata/metadata</a></li><li><a href="net.html">odata/net</a></li><li><a href="utils.html">odata/utils</a></li><li><a href="deferred.html">odatajs/deferred</a></li><li><a href="utils_.html">odatajs/utils</a></li><li><a href="xml.html">odatajs/xml</a></li><li><a href="module-store.html">store</a></li><li><a href="dom.html">store/dom</a></li><li><a href="indexeddb.html">store/indexeddb</a></li><li><a href="memory.html">store/memory</a></li></ul><h3>Classes</h3><ul><li><a href="DataCache.html">DataCache</a></li><li><a href="DataCacheOperation.html">DataCacheOperation</a></li><li><a href="DjsDeferred.html">DjsDeferred</a></li><li><a href="dom-DomStore.html">DomStore</a></li><li><a href="indexeddb-IndexedDBStore.html">IndexedDBStore</a></li><li><a href="memory-MemoryStore.html">MemoryStore</a></li><li><a href="ODataCacheSource.html">ODataCacheSource</a></li></ul><h3><a href="global.html">Global</a></h3>
</nav>
<br clear="both">
<footer>
Documentation generated by <a href="https://github.com/jsdoc3/jsdoc">JSDoc 3.2.2</a> on Thu Apr 09 2015 08:31:26 GMT+0200 (MESZ)
</footer>
<script> prettyPrint(); </script>
<script src="scripts/linenumber.js"> </script>
</body>
</html>