@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
173 lines (150 loc) • 4.81 kB
JavaScript
;
const utils = require('./../utils');
const batchObjects = require('./batchObjects');
exports.getBoundary = function (headerValue) {
const l = headerValue.split(';');
for (const element of l) {
const ll = element.split('=');
if (ll[0].trim() === 'boundary') {
return ll[1];
}
}
};
function readAppHttp(batchContent, boundary) {
const headers = {};
const payload = [];
let state = 0; //read url
const url = batchContent.readLine();
state = 1; //read header
let line = batchContent.lookLine();
while (
line !== null &&
line !== undefined &&
line.indexOf(boundary) !== 0
) {
if (state === 1) {
if (line.length === 0) {
state = 2; //read body
batchContent.inc();
} else {
const h = readHeader(line);
headers[h.name] = h.value;
batchContent.inc();
}
} else if (state === 2) {
payload.push(line);
batchContent.inc();
}
line = batchContent.lookLine();
}
if (line === undefined) {
throw new Error(
'Invalid boundary while parsing batch request. Expect boundary ' +
boundary
);
}
return new batchObjects.AppHttp(url, headers, payload);
}
function parsePart(batchContent, boundary) {
const boundaryNext = boundary;
const boundaryEnd = boundary + '--';
const headers = {};
let content;
let state = 1; //read header
let line = batchContent.lookLine();
while (
line !== null &&
line !== undefined &&
line !== boundaryNext &&
line !== boundaryEnd
) {
if (state === 1) {
if (line.length === 0) {
state = 2; //read body
batchContent.inc();
} else {
const h = readHeader(line);
headers[h.name] = h.value;
batchContent.inc();
}
} else if (state === 2) {
if (!headers['content-type']) {
throw new Error('Missing header "content-type" in batch part');
} else if (headers['content-type'] === 'application/http') {
content = readAppHttp(batchContent, boundary);
} else if (
utils.startsWith(headers['content-type'], 'multipart/mixed;')
) {
const changeSetBoundary = exports.getBoundary(
headers['content-type']
);
content = parseBatch(
batchContent,
changeSetBoundary,
'changeset'
);
} else {
//TODO not supported
}
}
line = batchContent.lookLine();
}
if (line === undefined) {
throw new Error(
'Invalid boundary while parsing batch request. Expect boundary ' +
boundary
);
}
return content;
}
function readHeader(line) {
const colPos = line.indexOf(':');
if (colPos === -1) {
throw new Error('Invalid header "content-type" in batch part');
}
const s0 = line.substr(0, colPos);
const s1 = line.substr(colPos + 1);
return {
name: s0.toLowerCase(),
value: s1.trim(),
};
}
function parseBatch(content, boundary, type) {
const boundaryNext = '--' + boundary;
const boundaryEnd = '--' + boundary + '--';
const batch = new batchObjects.Batch(type);
let part;
let line = content.readLine();
while (line !== null && line !== undefined && line !== boundaryNext) {
//read lines before first boundary
line = content.readLine();
}
if (line === undefined) {
throw new Error('Invalid boundary while parsing batch request');
}
//line is now read boundary
line = content.lookLine(); //read line behind
while (line !== null && line !== undefined && line !== boundaryEnd) {
part = parsePart(content, boundaryNext);
batch.parts.push(part);
line = content.lookLine(); //now on boundary
if (line === boundaryNext) {
line = content.readLine(); //consume boundaryNext
}
}
content.readLine(); //consume boundaryEnd
line = content.lookLine(); //read line behind
while (line !== null && line !== undefined && line.length === 0) {
//read empty lines after first boundaryend
content.readLine();
line = content.lookLine();
}
return batch;
}
exports.convertBatch = function (payload, boundary) {
return parseBatch(
new batchObjects.BatchContent(payload),
boundary,
'batch'
);
};