UNPKG

@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
'use strict'; 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); } ); };