@sap-cloud-sdk/core
Version:
SAP Cloud SDK for JavaScript core
208 lines • 8.38 kB
JavaScript
var __assign = (this && this.__assign) || function () {
__assign = Object.assign || function(t) {
for (var s, i = 1, n = arguments.length; i < n; i++) {
s = arguments[i];
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
t[p] = s[p];
}
return t;
};
return __assign.apply(this, arguments);
};
Object.defineProperty(exports, "__esModule", { value: true });
exports.isHttpSuccessCode = exports.parseBatchResponse = exports.parseResponseData = exports.parseHttpCode = exports.splitResponse = exports.splitChangeSetResponse = exports.splitBatchResponse = exports.getResponseBody = exports.detectNewLineSymbol = void 0;
var util_1 = require("@sap-cloud-sdk/util");
var logger = (0, util_1.createLogger)({
package: 'core',
messageContext: 'batch-response-parser'
});
/**
* Detects the system dependent line break in a string.
* @param str - The string to check for line breaks. Should have at least two lines, otherwise an error will be thrown.
* @returns The system dependent line break
*/
function detectNewLineSymbol(str) {
if (str.includes(util_1.webEOL)) {
return util_1.webEOL;
}
if (str.includes(util_1.unixEOL)) {
return util_1.unixEOL;
}
throw new Error('Cannot detect line breaks in the batch response body.');
}
exports.detectNewLineSymbol = detectNewLineSymbol;
/**
* Get the response body from the string representation of a response.
* @param response - String representation of a response.
* @returns The response body as a one line string.
*/
function getResponseBody(response) {
var newLineSymbol = detectNewLineSymbol(response);
var lines = response.split(newLineSymbol);
// A valid response should contain at least three lines, part id, empty line and response body.
if (lines.length >= 3) {
return lines[lines.length - 1];
}
throw Error("Cannot parse batch subrequest response body. Expected at least three lines in the response, got ".concat(lines.length, "."));
}
exports.getResponseBody = getResponseBody;
/**
* Parse the headers in the string representation of a response headers into an object. This will only look at the highest level of headers.
* @param response - String representation of a response
* @returns The headers as an object.
*/
function parseHeaders(response) {
var newLineSymbol = detectNewLineSymbol(response);
// split on the first empty line
var responseHeaders = response.split(newLineSymbol + newLineSymbol)[0];
return responseHeaders.split(newLineSymbol).reduce(function (headers, line) {
var _a;
var _b = line.split(':'), key = _b[0], value = _b[1];
return __assign(__assign({}, headers), (_a = {}, _a[key] = value === null || value === void 0 ? void 0 : value.trim(), _a));
}, {});
}
/**
* Get the boundary from the content type header value. Throws an error if no boundary can be found.
* @param contentType - Value of the content type header
* @returns The boundary.
*/
function getBoundary(contentType) {
var boundary = (contentType === null || contentType === void 0 ? void 0 : contentType.match(/.*boundary=.+/))
? (0, util_1.last)(contentType.split('boundary='))
: undefined;
if (!boundary) {
throw new Error('No boundary found.');
}
return boundary;
}
/**
* Split a batch response into an array of sub responses for the retrieve requests and changesets.
* @param response - The raw HTTP response.
* @returns A list of sub responses represented as strings.
*/
function splitBatchResponse(response) {
var body = response.data.trim();
if (!body) {
return [];
}
try {
var boundary = getBoundary((0, util_1.pickValueIgnoreCase)(response.headers, 'content-type'));
return splitResponse(body, boundary);
}
catch (err) {
throw new util_1.ErrorWithCause('Could not parse batch response.', err);
}
}
exports.splitBatchResponse = splitBatchResponse;
/**
* Split a changeset (sub) response into an array of sub responses.
* @param changeSetResponse - The string representation of a change set response.
* @returns A list of sub responses represented as strings.
*/
function splitChangeSetResponse(changeSetResponse) {
var headers = parseHeaders(changeSetResponse);
try {
var boundary = getBoundary((0, util_1.pickValueIgnoreCase)(headers, 'content-type'));
return splitResponse(changeSetResponse, boundary);
}
catch (err) {
throw new util_1.ErrorWithCause('Could not parse change set response.', err);
}
}
exports.splitChangeSetResponse = splitChangeSetResponse;
/**
* Split a string representation of a response into sub responses given its boundary.
* @param response - The string representation of the response to split.
* @param boundary - The boundary to split by.
* @returns A list of sub responses represented as strings.
*/
function splitResponse(response, boundary) {
var newLineSymbol = detectNewLineSymbol(response);
var parts = response.split("--".concat(boundary)).map(function (part) {
var trimmedPart = part.trim();
return trimmedPart.includes('204 No Content') ||
(trimmedPart.includes('200 OK') &&
trimmedPart.includes('Content-Length: 0'))
? "".concat(trimmedPart).concat(newLineSymbol)
: trimmedPart;
});
if (parts.length >= 3) {
return parts.slice(1, parts.length - 1);
}
throw new Error('Could not parse batch response body. Expected at least two response boundaries.');
}
exports.splitResponse = splitResponse;
/**
* Parse the HTTP code of response.
* @param response - String representation of the response.
* @returns The HTTP code.
*/
function parseHttpCode(response) {
var group = response.match(/HTTP\/\d\.\d (\d{3}).*?/);
if (group) {
return parseInt(group[1].toString());
}
throw new Error('Cannot parse http code of the response.');
}
exports.parseHttpCode = parseHttpCode;
/**
* Get the body from the given response and parse it to JSON.
* @param response - The string representation of a single response.
* @returns The parsed JSON representation of the response body.
*/
function parseResponseBody(response) {
var responseBody = getResponseBody(response);
if (responseBody) {
try {
return JSON.parse(responseBody);
}
catch (err) {
logger.error("Could not parse response body. Invalid JSON. Original Error: ".concat(err));
}
}
return {};
}
/**
* Parse the body and http code of a batch sub response.
* @param response - A batch sub response.
* @returns The parsed response.s
*/
function parseResponseData(response) {
return {
body: parseResponseBody(response),
httpCode: parseHttpCode(response)
};
}
exports.parseResponseData = parseResponseData;
/**
* Parse the complete batch HTTP response.
* @param batchResponse - HTTP response of a batch request.
* @returns An array of parsed sub responses of the batch response.
*/
function parseBatchResponse(batchResponse) {
return splitBatchResponse(batchResponse).map(function (response) {
var contentType = (0, util_1.pickValueIgnoreCase)(parseHeaders(response), 'content-type');
if (isChangeSetContentType(contentType)) {
return splitChangeSetResponse(response).map(function (subResponse) {
return parseResponseData(subResponse);
});
}
if (isRetrieveOrErrorContentType(contentType)) {
return parseResponseData(response);
}
throw Error("Cannot parse batch response. Unknown subresponse 'Content-Type' header '".concat(contentType, "'."));
});
}
exports.parseBatchResponse = parseBatchResponse;
function isChangeSetContentType(contentType) {
return contentType === null || contentType === void 0 ? void 0 : contentType.trim().startsWith('multipart/mixed');
}
function isRetrieveOrErrorContentType(contentType) {
return contentType === null || contentType === void 0 ? void 0 : contentType.trim().startsWith('application/http');
}
function isHttpSuccessCode(httpCode) {
return httpCode >= 200 && httpCode < 300;
}
exports.isHttpSuccessCode = isHttpSuccessCode;
//# sourceMappingURL=batch-response-parser.js.map
;