UNPKG

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