@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
449 lines (365 loc) • 15.6 kB
JavaScript
;
var async = require('async');
var connect = require('../db/connect');
var sqlPutPostLinks = require('./../sql/createPutPostLinksStatements');
var dataCollectorLinks = require('./../sql/dataCollectorLinks');
var dataCollectorPutPostLinks = require('./../sql/dataCollectorPutPostLinks');
var DBSegment = require('./../db/dbSegment');
var serializer = require('./../serializer/serializer');
var exitProcessor = require('./exitProcessor');
var utils = require('./../utils/utils');
var contenTypeCheck = require('./../utils/checkContentType');
var oDataSegmentParser = require('../parsers/jison_segment_parser');
var UnsupportedMediaType = require('./../utils/errors/http/unsupportedMediaType');
var BadRequest = require('./../utils/errors/http/badRequest');
function movePayloadToDbSegment(context, asyncDone) {
var req = context.request;
var contentType = req.headers['content-type'];
try {
context.logger.silly('resourceProcessorPutPostLinks', 'movePayloadToDbSegment');
if (contentType) {
if (!contenTypeCheck.isSupportedContentType(contentType)) {
throw new UnsupportedMediaType('Content-Type ' + contentType + ' not supported.');
}
}
if (req.body) {
//SAPINFO This IF covers the Express case. The Express module sets the body in the request object
onData(null, req.body);
} else {
req.getBodyAsString(onData);
}
} catch (err) {
return asyncDone(err, context);
}
function onData(err, buffer) {
let data;
let json;
if (err) {
return asyncDone(err, context);
}
try {
data = buffer.toString('utf-8');
if (contenTypeCheck.isContentTypeJson(contentType) === true) {
try {
json = JSON.parse(data);
} catch (e) {
return asyncDone(new BadRequest("Request payload is not a valid JSON object", context), context);
}
const keys = getKeyFromPostPutDeleteLinksUrl(json, context.oData.dbSegmentLast.entityType.name, context.uriTree.baseUrl);
context.oData.dbSegmentLast.setKeyValues(keys);
if (!context.oData.dbSegmentLast.m2n) {
DBSegment.DbSegment.setRecordPutPostLinks(context);
}
return asyncDone(null, context);
}
// If we reach this code no valid content type could be found
// or the payload is not valid
throw new UnsupportedMediaType('Content-Type ' + contentType + ' does not match payload: ' + data);
} catch (err) {
return asyncDone(err, context);
}
return asyncDone(null, context);
}
}
/**
* Move key information from uri into key maps if $links is used
* @param linksPayload
* @param refEntity
* @param baseUrl
*/
function getKeyFromPostPutDeleteLinksUrl(linksPayload, refEntity, baseUrl) {
if (!linksPayload.uri) {
throw new BadRequest('Error in Payload: Missing URI parameter');
}
var count = 0;
for (var p in linksPayload) {
if (linksPayload.hasOwnProperty(p)) {
count++;
}
}
if (count > 1) {
throw new BadRequest('Error in Payload: Too many parameters');
}
linksPayload.uri = decodeURIComponent(linksPayload.uri);
var lastSegIndexBegin = linksPayload.uri.lastIndexOf("/");
if (lastSegIndexBegin > 0) { // Absolute URL
var payloadBaseUrl = linksPayload.uri.substring(0, lastSegIndexBegin + 1);
if (payloadBaseUrl !== baseUrl) {
throw new BadRequest('Error in Payload: The base URL does not match the request base URL.');
}
}
let segment = linksPayload.uri.substring(lastSegIndexBegin + 1, linksPayload.uri.length);
let parsed;
try {
parsed = oDataSegmentParser.parse(segment);
} catch (e) {
throw new BadRequest("Invalid entity URI", context);
}
if (parsed.identifier !== refEntity) {
throw new BadRequest('Error in Payload: Entity name does not match the last DBSegment in URI');
}
return parsed.keys;
}
function eventBefore(context, asyncDone) {
context.logger.info(context.oData.links.sOperation, 'event start before');
var eventFunction = exitProcessor.eventHandler('before', context.oData.links.sOperation);
eventFunction(context, function (err, context) {
context.logger.info('modify event end', 'before');
if (err) {
context.logger.info('modify event end', 'error occured');
}
return asyncDone(err, context);
});
}
function eventAfter(context, asyncDone) {
context.logger.info(context.oData.links.sOperation, 'event start after');
var eventFunction = exitProcessor.eventHandler('after', context.oData.links.sOperation);
eventFunction(context, function (err, context) {
context.logger.info('modify event end', 'after');
if (err) {
context.logger.info('modify event end', 'error occured');
}
return asyncDone(err, context);
});
}
function eventPrecommit(context, asyncDone) {
context.logger.info(context.oData.links.sOperation, 'event start precommit');
var eventFunction = exitProcessor.eventHandler('precommit', context.oData.links.sOperation);
eventFunction(context, function (err, context) {
context.logger.info('modify event end', 'precommit');
if (err) {
context.logger.info('modify event end', 'error occured');
}
return asyncDone(err, context);
});
}
function eventPostCommit(context, asyncDone) {
context.logger.info(context.oData.links.sOperation, 'event start postcommit');
var eventFunction = exitProcessor.eventHandler('postcommit', context.oData.links.sOperation);
eventFunction(context, function (err, context) {
context.logger.info('modify event end', 'postcommit');
if (err) {
context.logger.info('modify event end', 'error occured');
}
return asyncDone(err, context);
});
}
function insertTmpTableToRealTable(context, asyncDone) {
var dbSeg = context.oData.dbSegmentLast;
var entityType = dbSeg.entityType;
var create = (entityType.modifications || {}).update || {};
if (create.using) {
exitProcessor.executeExit(create.using, 'using', context.oData.links.sOperation, context, asyncDone);
} else {
dataCollectorLinks.insertTmpTableToRealTable(context, asyncDone);
}
}
function insertTmpTableToRealMNTable(context, asyncDone) {
var dbSeg = context.oData.links.toBeUpdated;
var entityType = dbSeg.entityType;
var create = (entityType.modifications || {}).create || {};
if (create.using) {
exitProcessor.executeExit(create.using, 'using', 'create', context, asyncDone);
} else {
dataCollectorPutPostLinks.insertTmpTableToRealMNTable(context, asyncDone);
}
}
function setStatus(context, asyncDone) {
context.response.status(204);
return asyncDone(null, context);
}
exports.process = function (context, asyncDone) {
context.logger.silly('resourceProcessorPutPostLinks', 'process');
// Adapt the operation that is passed to the custom exit handler
if (context.request.method === "POST") {
context.oData.links.sOperation = 'create';
} else if (context.request.method === "PUT") {
context.oData.links.sOperation = 'update';
}
var execArr;
var m2n = (context.oData.dbSegmentLast.getOver() !== undefined);
if (m2n) { // INSERT into 3rd table
context.oData.dbSegmentLast.m2n = true;
execArr = [
utils.injectContext(context),
//preparation
utils.try(sqlPutPostLinks.createPutPostLinksMNStatementsCreateTmpTables),
utils.try(dataCollectorLinks.createTmpTableMN), //create //NEW ORL
//utils.try(contentIdHelper.createNewContentIdFromRecordMapFromPayload),
utils.try(movePayloadToDbSegment),
utils.try(sqlPutPostLinks.createPutPostLinksMNStatementsInsert),
utils.try(dataCollectorLinks.movePayloadFromMNSegmentToSelectStm), //no change
// no auto key gen usage here
utils.try(dataCollectorLinks.insertOldDataToPrincipalDependentTables),
utils.try(dataCollectorLinks.insertPayloadIntoTempTable), //insert NEW
//execution
utils.try(eventBefore),
utils.try(insertTmpTableToRealMNTable), //insert real
utils.try(eventAfter),
//post processing
//utils.try(dataCollectorLinks.selectData), //select
//commit handling
utils.try(eventPrecommit),
utils.try(dataCollectorLinks.commit),
utils.try(eventPostCommit),
// cleanup
utils.try(dataCollectorLinks.truncateTempTablesMN),
utils.try(dataCollectorLinks.dropTempTablesMN),
utils.try(dataCollectorLinks.commit),
//utils.try(modifyDbSegment),
//utils.try(writeLocationHeader),
//utils.try(serializer.serializeData),
utils.try(setStatus)
];
} else {
execArr = [
utils.injectContext(context),
//preparation
utils.try(sqlPutPostLinks.createPutPostLinksStatementsCreateTmpTables),
utils.try(dataCollectorLinks.createTmpTables), //create //NEW ORL
//utils.try(contentIdHelper.createNewContentIdFromRecordMapFromPayload),
utils.try(movePayloadToDbSegment),
utils.try(sqlPutPostLinks.createPutPostLinksStatementsInsert),
utils.try(dataCollectorLinks.moveRecordNV_ToSelectStm), //no change
// no auto key gen usage here
utils.try(dataCollectorLinks.insertOldDataToOldTable),
utils.try(dataCollectorLinks.insertOldDataToPrincipalDependentTables),
utils.try(dataCollectorLinks.insertPayloadIntoTempTable), //insert NEW
//execution
utils.try(eventBefore),
utils.try(insertTmpTableToRealTable),
utils.try(eventAfter),
//commit handling
utils.try(eventPrecommit),
utils.try(dataCollectorLinks.commit),
utils.try(eventPostCommit),
// cleanup
utils.try(dataCollectorLinks.truncateTempTables),
utils.try(dataCollectorLinks.dropTempTables),
utils.try(dataCollectorLinks.commit),
//post processing
utils.try(serializer.serializeNoContent)
];
}
async.waterfall(
execArr,
function (err, context) {
if (err) {
var dbClient = context.db.client;
if (dbClient) {
//ROLLBack the changes
return connect.dbRollback(context, dbClient, function (errDB) {
if (errDB) {
return asyncDone(errDB, context);
}
return asyncDone(err, context);
});
}
}
return asyncDone(err, context);
}
);
};
exports.processInBatchCreateTables = function (context, asyncDone) {
context.logger.silly('resourceProcessorPutPostLinks', 'processInBatchCreateTables');
var execArr;
var m2n = (context.oData.dbSegmentLast.getOver() !== undefined);
if (m2n) { // INSERT into 3rd table
context.oData.dbSegmentLast.m2n = true;
execArr = [
utils.injectContext(context),
utils.try(sqlPutPostLinks.createPutPostLinksMNStatementsCreateTmpTables),
utils.try(dataCollectorLinks.createTmpTableMN) //create //NEW ORL
//utils.try(contentIdHelper.createNewContentIdFromRecordMapFromPayload),
];
} else {
execArr = [
utils.injectContext(context),
//preparation
utils.try(sqlPutPostLinks.createPutPostLinksStatementsCreateTmpTables),
utils.try(dataCollectorLinks.createTmpTables) //create //NEW ORL
//utils.try(contentIdHelper.createNewContentIdFromRecordMapFromPayload),
];
}
async.waterfall(
execArr,
function (err, context) {
return asyncDone(err, context);
}
);
};
exports.processInBatch = function (context, asyncDone) {
context.logger.silly('resourceProcessorPutPostLinks', 'processInBatch');
var execArr;
var m2n = (context.oData.dbSegmentLast.getOver() !== undefined);
if (m2n) { // INSERT into 3rd table
context.oData.dbSegmentLast.m2n = true;
execArr = [
utils.injectContext(context),
utils.try(movePayloadToDbSegment),
utils.try(sqlPutPostLinks.createPutPostLinksMNStatementsInsert),
utils.try(dataCollectorLinks.movePayloadFromMNSegmentToSelectStm), //no change
// no auto key gen usage here
utils.try(dataCollectorLinks.insertOldDataToPrincipalDependentTables),
utils.try(dataCollectorLinks.insertPayloadIntoTempTable), //insert NEW
//execution
utils.try(eventBefore),
utils.try(insertTmpTableToRealMNTable), //insert real
utils.try(eventAfter)
//post processing
//utils.try(dataCollectorLinks.selectData), //select
];
} else {
execArr = [
utils.injectContext(context),
utils.try(movePayloadToDbSegment),
utils.try(sqlPutPostLinks.createPutPostLinksStatementsInsert),
//utils.try(contentIdHelper.createNewContentIdFromRecordMapFromPayload),
utils.try(dataCollectorLinks.moveRecordNV_ToSelectStm), //no change
utils.try(dataCollectorLinks.insertOldDataToOldTable),
utils.try(dataCollectorLinks.insertOldDataToPrincipalDependentTables),
utils.try(dataCollectorLinks.insertPayloadIntoTempTable), //insert NEW
//execution
utils.try(eventBefore),
utils.try(insertTmpTableToRealTable),
utils.try(eventAfter)
];
}
async.waterfall(
execArr,
function (err, context) {
return asyncDone(err, context);
}
);
};
exports.processInBatchPreCommitRun = function (context, asyncDone) {
context.logger.silly('resourceProcessorPutPostLinks', 'processInBatchPreCommitRun');
async.waterfall(
[
utils.injectContext(context),
//commit handling
utils.try(eventPrecommit)
],
function (err, context) {
return asyncDone(err, context);
}
);
};
exports.processInBatchPostCommitRun = function (context, asyncDone) {
context.logger.silly('resourceProcessorPutPostLinks', 'processInBatchPostCommitRun');
async.waterfall(
[
utils.injectContext(context),
utils.try(eventPostCommit),
// truncate temporary tables to clean the session
utils.try(dataCollectorLinks.truncateTempTables),
// drop temporary tables to clean the session
utils.try(dataCollectorLinks.dropTempTables),
//post processing
utils.try(serializer.serializeNoContent)
],
function (err, context) {
return asyncDone(err, context);
}
);
};