@sap/xsodata
Version:
Expose data from a HANA database as OData V2 service with help of .xsodata files.
325 lines (270 loc) • 10.8 kB
JavaScript
;
const async = require('async');
const connect = require('../db/connect');
const contentIdHelper = require('./contentIdHelper');
const contentTypeCheck = require('./../utils/checkContentType');
const dataCollectorGet = require('./../sql/dataCollectorGet');
const dataCollectorPut = require('./../sql/dataCollectorPut');
const exitProcessor = require('./exitProcessor');
const serializer = require('./../serializer/serializer');
const sqlPut = require('./../sql/createPutStatements');
const utils = require('./../utils/utils');
const AtomXmlSerializer = require('./../serializer/atomXmlToJsonSerializer');
const BadRequestError = require('./../utils/errors/http/badRequest');
const InternalError = require('./../utils/errors/internalError');
const UnsupportedMediaType = require('./../utils/errors/http/unsupportedMediaType');
const { eventPut } = require('./eventActionHandler');
const eventBefore = eventPut.eventBefore;
const eventAfter = eventPut.eventAfter;
const eventPreCommit = eventPut.eventPrecommit;
const eventPostCommit = eventPut.eventPostCommit;
function movePayloadToDbSegment_RecordNV(context, asyncDone) {
const req = context.request;
const contentType = req.headers['content-type'];
try {
context.logger.silly(
'resourceProcessorPut',
'movePayloadToDbSegment_RecordNV'
);
if (contentType) {
const isSupportedContentType =
contentTypeCheck.isSupportedContentType(contentType);
if (isSupportedContentType === false) {
throw new UnsupportedMediaType(
'Content-Type ' + contentType + ' not supported.'
);
}
} else {
throw new UnsupportedMediaType(
'The server is refusing to process the request because' +
' the entity has an unsupported format',
context
);
}
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) {
if (err) {
return asyncDone(err, context);
}
try {
const dbSeg = context.oData.dbSegmentLast;
const data = buffer.toString('utf-8');
if (
contentTypeCheck.isContentTypeXml(contentType) === true &&
utils.isXml(data) === true
) {
const typeModel = dbSeg.entityType.propertiesMap;
const serializer = new AtomXmlSerializer(data, typeModel);
return serializer.serialize(function (err, innerContext) {
if (err) {
return asyncDone(err, context);
}
if (!Array.isArray(innerContext.result)) {
const err1 = InternalError(
'Serialization result is not an array',
context
);
return asyncDone(err1, context);
}
const len = innerContext.result.length;
if (len !== 1) {
const err2 = InternalError(
'Serialization result length must be 1 but is ' +
len,
context
);
return asyncDone(err2, context);
}
const result = innerContext.result[0];
dbSeg.setRecordFromPutPayload(context, result);
return asyncDone(null, context);
});
}
if (contentTypeCheck.isContentTypeJson(contentType) === true) {
let json;
try {
json = JSON.parse(data);
} catch (e) {
return asyncDone(
new BadRequestError(
'Request payload is not a valid json object',
context
),
context
);
}
try {
dbSeg.setRecordFromPutPayload(context, json);
return asyncDone(null, context);
} catch (err) {
return asyncDone(err, 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);
}
}
}
function insertTmpTableToRealTables(context, asyncDone) {
const dbSeg = context.oData.dbSegmentLast;
const entityType = dbSeg.entityType;
const create = (entityType.modifications || {}).update || {};
if (create.using) {
exitProcessor.executeExit(
create.using,
'using',
'update',
context,
asyncDone
);
} else {
dataCollectorPut.insertTmpTableToRealTable(context, asyncDone);
}
}
exports.process = function (context, asyncDone) {
context.logger.silly('resourceProcessorPut', 'process');
async.waterfall(
[
utils.injectContext(context),
//preparation
utils.try(sqlPut.createPutStatementsCreateTmpTables),
utils.try(dataCollectorPut.createTmpTables),
utils.try(
contentIdHelper.createNewContentIdFromRecordMapFromPayload
),
utils.try(movePayloadToDbSegment_RecordNV),
utils.try(sqlPut.createPutStatementsInsert),
utils.try(dataCollectorPut.moveRecordNV_ToInsertTmpStm), //no change
utils.try(dataCollectorPut.insertOldDataToOldTable),
utils.try(dataCollectorPut.checkForAutoKeyGenUsage),
utils.try(dataCollectorPut.insertPayloadIntoTempTable), //insert NEW
//utils.try(dataCollectorPut.moveCreatedInfoToNewTempTable), //insert NEW
//execution
utils.try(eventBefore),
utils.try(insertTmpTableToRealTables),
utils.try(eventAfter),
utils.try(dataCollectorPut.selectData), // for context id
utils.try(contentIdHelper.createNewContentIdFromDbSegSelectedRows),
//commit handling
utils.try(eventPreCommit),
utils.try(dataCollectorPut.selectData), // for response
utils.try(dataCollectorPut.commit),
utils.try(eventPostCommit),
// cleanup
utils.try(dataCollectorPut.truncateTempTables),
utils.try(dataCollectorPut.dropTempTables),
utils.try(dataCollectorPut.commit),
// select ETag
utils.try(dataCollectorGet.select),
//post processing
utils.try(serializer.serializeNoContent),
],
function (err, context) {
if (err) {
const 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('resourceProcessorPut', 'processInBatchCreateTables');
async.waterfall(
[
utils.injectContext(context),
utils.try(sqlPut.createPutStatementsCreateTmpTables),
utils.try(dataCollectorPut.createTmpTables),
utils.try(
contentIdHelper.createNewContentIdFromRecordMapFromPayload
),
],
function (err, context) {
return asyncDone(err, context);
}
);
};
exports.processInBatch = function (context, asyncDone) {
context.logger.silly('resourceProcessorPut', 'processInBatch');
async.waterfall(
[
utils.injectContext(context),
//preparation
utils.try(movePayloadToDbSegment_RecordNV),
utils.try(sqlPut.createPutStatementsInsert),
utils.try(dataCollectorPut.moveRecordNV_ToInsertTmpStm), //no change
utils.try(dataCollectorPut.insertOldDataToOldTable),
utils.try(dataCollectorPut.checkForAutoKeyGenUsage),
utils.try(dataCollectorPut.insertPayloadIntoTempTable), //insert NEW
//utils.try(dataCollectorPut.moveCreatedInfoToNewTempTable), //insert NEW
//execution
utils.try(eventBefore),
utils.try(insertTmpTableToRealTables),
utils.try(eventAfter),
//post processing
utils.try(dataCollectorPut.selectData), // for context id
utils.try(contentIdHelper.createNewContentIdFromDbSegSelectedRows),
],
function (err, context) {
return asyncDone(err, context);
}
);
};
exports.processInBatchPreCommitRun = function (context, asyncDone) {
context.logger.silly('resourceProcessorPut', 'processInBatchPreCommitRun');
async.waterfall(
[
utils.injectContext(context),
utils.try(eventPreCommit),
utils.try(dataCollectorPut.selectData), // for response
],
function (err, context) {
return asyncDone(err, context);
}
);
};
exports.processInBatchPostCommitRun = function (context, asyncDone) {
context.logger.silly('resourceProcessorPut', 'processInBatchPostCommitRun');
async.waterfall(
[
utils.injectContext(context),
utils.try(eventPostCommit),
// cleanup
utils.try(dataCollectorPut.truncateTempTables),
utils.try(dataCollectorPut.dropTempTables),
//post processing
utils.try(serializer.serializeNoContent),
],
function (err, context) {
return asyncDone(err, context);
}
);
};