UNPKG

@sap/cds

Version:

SAP Cloud Application Programming Model - CDS for Node.js

83 lines (70 loc) 3.7 kB
const cds = require('../../cds') const { SELECT } = cds.ql module.exports = exports = cds.service.impl(function () { // prettier-ignore this.on(['CREATE', 'READ', 'UPDATE', 'UPSERT', 'DELETE'], '*', async function handle_crud_requests(req) { if (!cds.db) return req.reject('NO_DATABASE_CONNECTION') // REVISIT: error message if (!req.query) return req.reject(501, 'The request has no query and cannot be served generically.') if (req.target['@cds.persistence.skip'] === true && req.target['@cds.persistence.table'] !== true) return req.reject(501, `Entity "${req.target.name}" is annotated with "@cds.persistence.skip" and cannot be served generically.`) const { ref } = req.query.INSERT?.into || req.query.SELECT?.from || {} const check_parent = ref?.length > 1 ? SELECT.one(1).from({ ref: ref.slice(0, -1) }) : null if (check_parent && req.event === 'CREATE') (await check_parent) || req.reject(404) else if (req.target?._isSingleton) await handle_singleton_request(req) else if (req.query?.SELECT && req.locale) req.query.SELECT.localized ??= true // REVISIT for cds^10: can we always use cds.db.dispatch(req)? const result = req.iterator && !req.objectMode ? await cds.db.dispatch(req) : await cds.run(req.query, req.data) return await exports._handle_result(req, result, check_parent) }) }) async function handle_singleton_request(req) { if (req.event !== 'DELETE' && req.event !== 'UPDATE') return if (req.event === 'DELETE') req.target['@odata.singleton.nullable'] || req.reject(400, 'SINGLETON_NOT_NULLABLE') const fetch_singleton = SELECT.one(req.target) const keys = [...(req.target.keys || [])].filter(e => !e.isAssociation).map(e => e.name) // if no keys available, select all columns so we can delete the singleton with same content if (keys.length) fetch_singleton.columns(keys) const singleton = await fetch_singleton if (!singleton) throw req.reject(404) // REVISIT: Workaround for singleton, to get keys into singleton for (const k in singleton) if (keys.includes(k)) req.data[k] = singleton[k] req.query.where(singleton) } async function new_behavior(req, result, parent) { const n = result?.affected ?? result?.length ?? result // prettier-ignore switch (req.event) { case 'READ': if (!n && req._etagValidationType === 'if-match') req.reject(412) if (!n) !parent || (await parent) || req.reject(404) return result case 'INSERT': case 'CREATE': if (result.affected != null) return result //> db service returned [affected] else return Object.assign([...result], { affected: result.affectedRows }) case 'UPSERT': case 'UPDATE': case 'DELETE': if (!n && req._etagValidationType) return req.reject(412) if (!n) (await cds.db.exists(req.subject)) || req.reject(404) if (result.affected != null) return result //> db service returned [affected] else return Object.assign([], { affected: n }) } } async function legacy_behavior(req, result, parent) { if (req.event !== 'READ' && req.protocol?.match(/odata/)) req._.readAfterWrite = true const x = await new_behavior(req, result, parent) // prettier-ignore switch (req.event) { case 'READ': return x //> array of results or single result or null case 'DELETE': return x.affected //> number of deleted rows default: return req.data } } exports._handle_result = legacy_behavior exports._always_return_arrays = on_off => { if (on_off === undefined) return exports._handle_result === new_behavior exports._handle_result = on_off ? new_behavior : legacy_behavior } exports._always_return_arrays(!cds.env.features.legacy_srv_results)