UNPKG

@sap/cds

Version:

SAP Cloud Application Programming Model - CDS for Node.js

132 lines (116 loc) 4.43 kB
module.exports = exports = adapter => exports.parse.bind(adapter) /** * @type {import('express').Handler} * @this import('../RestAdapter') */ exports.parse = function (req, res, next) { const { service } = this // REVISIT: Once we don't display the error message location in terms of an offset, but instead a copy of the // original request including a marker, we don't need to provide the baseUrl here. let query = cds.odata.parse(req.url, { service, baseUrl: req.baseUrl, protocol: 'rest' }) // parser always produces selects const _target = (req._target = query.SELECT && query.SELECT.from) if (!_target) return next() // REVISIT: __target is the csn target definition let { __target: definition, SELECT: { from, one } } = query if (typeof definition === 'string') { definition = service.model.definitions[definition] || service.model.definitions[definition.split(':$:')[0]].actions[definition.split(':$:')[1]] } delete query.__target // req.__proto__.method is set in case of upsert const isUpsert = req.__proto__.method in { PUT: 1, PATCH: 1 } // REVISIT: hack for actions and functions let operation, args const last = _target.ref[_target.ref.length - 1] if (last.operation) { operation = last.operation if (last.args) args = last.args _target.ref.pop() } const unbound = _target.ref.length === 0 // query based on method switch (req.method) { case 'HEAD': case 'GET': if (operation) { req._operation = operation = definition if (operation.kind === 'action') cds.error('Action must be called by POST', { code: 400 }) if (!unbound) query = one ? SELECT.one(_target) : SELECT.from(_target) else query = undefined } else { // read (nothing to do) } break case 'POST': if (operation) { req._operation = operation = definition if (operation.kind === 'function') cds.error('Function must be called by GET', { code: 400 }) if (!unbound) query = one ? SELECT.one(_target) : SELECT.from(_target) else query = undefined } else { // create if (one && !isUpsert) cds.error('POST not allowed on entity', { code: 400 }) query = INSERT.into(_target) } break case 'PUT': case 'PATCH': if (operation) { let errorMsg if (definition) { errorMsg = `${definition.kind.charAt(0).toUpperCase() + definition.kind.slice(1)} must be called by ${ definition.kind === 'action' ? 'POST' : 'GET' }` } else { errorMsg = `That action/function must be called by POST/GET` } cds.error(errorMsg, { code: 400 }) } if (!one) throw { statusCode: 400, code: '400', message: `INVALID_${req.method}` } query = UPDATE(_target) break case 'DELETE': if (operation) { let errorMsg if (definition) { errorMsg = `${definition.kind.charAt(0).toUpperCase() + definition.kind.slice(1)} must be called by ${ definition.kind === 'action' ? 'POST' : 'GET' }` } else { errorMsg = `That action/function must be called by POST/GET` } cds.error(errorMsg, { code: 400 }) } if (!one) cds.error('DELETE not allowed on collection', { code: 400 }) query = DELETE.from(_target) break default: // anything to do? } req._query = query // REVISIT: req._query should not be a standard API if (!definition) return next() const { keys, params } = getKeysAndParamsFromPath(from, service) req._data = operation?.kind === 'function' ? {} : keys if (params) req._params = params // REVISIT: query._data hack if ((query && (query.INSERT || query.UPDATE || query.DELETE)) || (operation && operation.kind === 'action') || args) { if (operation && (operation.kind === 'action' || operation.kind === 'function') && !operation.params) { req._data = {} } else { const payload = args || req.body if (!operation) Object.assign(payload, keys) preProcessData(payload, service, definition) req._data = payload } } next() } const cds = require('../../_runtime/cds') const { INSERT, SELECT, UPDATE, DELETE } = cds.ql const { getKeysAndParamsFromPath } = require('../../common/utils/path') const { preProcessData } = require('../pre-processing')