UNPKG

@sap/xsodata

Version:

Expose data from a HANA database as OData V2 service with help of .xsodata files.

340 lines (266 loc) 11.5 kB
'use strict'; var async = require('async'); var connect = require('../db/connect'); var contentIdHelper = require('./contentIdHelper'); var contentTypeCheck = require('./../utils/checkContentType'); var dataCollectorGet = require('./../sql/dataCollectorGet'); var dataCollectorPut = require('./../sql/dataCollectorPut'); var exitProcessor = require('./exitProcessor'); var serializer = require('./../serializer/serializer'); var sqlPut = require('./../sql/createPutStatements'); var utils = require('./../utils/utils'); var AtomXmlSerializer = require('./../serializer/atomXmlToJsonSerializer'); var BadRequestError = require('./../utils/errors/http/badRequest'); var InternalError = require('./../utils/errors/internalError'); var UnsupportedMediaType = require('./../utils/errors/http/unsupportedMediaType'); function movePayloadToDbSegment_RecordNV(context, asyncDone) { var req = context.request; var contentType = req.headers['content-type']; try { context.logger.silly('resourceProcessorPut', 'movePayloadToDbSegment_RecordNV'); if (contentType) { var 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 { var dbSeg = context.oData.dbSegmentLast; var data = buffer.toString('utf-8'); if (contentTypeCheck.isContentTypeXml(contentType) === true && utils.isXml(data) === true) { var typeModel = dbSeg.entityType.propertiesMap; var serializer = new AtomXmlSerializer(data, typeModel); return serializer.serialize(function (err, innerContext) { if (err) { return asyncDone(err, context); } if (!Array.isArray(innerContext.result)) { var err1 = InternalError("Serialization result is not an array", context); return asyncDone(err1, context); } var len = innerContext.result.length; if (len !== 1) { var err2 = InternalError("Serialization result length must be 1 but is " + len, context); return asyncDone(err2, context); } var result = innerContext.result[0]; dbSeg.setRecordFromPutPayload(context, result); return asyncDone(null, context); }); } if (contentTypeCheck.isContentTypeJson(contentType) === true) { var 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); } return asyncDone(null, context); } } function eventBefore(context, asyncDone) { context.logger.info('update', 'event start before'); var eventFunction = exitProcessor.eventHandler('before', 'update'); 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('update', 'event start after'); var eventFunction = exitProcessor.eventHandler('after', 'update'); 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('update', 'event start precommit'); var eventFunction = exitProcessor.eventHandler('precommit', 'update'); 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('update', 'event start postcommit'); var eventFunction = exitProcessor.eventHandler('postcommit', 'update'); 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 insertTmpTableToRealTables(context, asyncDone) { var dbSeg = context.oData.dbSegmentLast; var entityType = dbSeg.entityType; var 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) { 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('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); } ); };