UNPKG

@sap/cds

Version:

SAP Cloud Application Programming Model - CDS for Node.js

97 lines (83 loc) 3.85 kB
const cds = require('../cds') // async const get_handle_authorization = require('./generic/auth') const get_handle_validations = require('./generic/input') const get_handle_etags = require('./generic/etag') // sync const get_handle_paging = require('./generic/paging') const get_handle_sorting = require('./generic/sorting') const get_handle_media_type = require('./generic/media') const get_handle_temporal_data = require('./generic/temporal') const postProcess = require('./utils/postProcess') /** * Generic Application Service Provider */ class ApplicationService extends cds.Service { init() { const { generics } = this.constructor for (let each of generics) this.constructor[each].call(this) if (!generics.has('handle_authorization')) this.handle_authorization ??= get_handle_authorization.call(this) if (!generics.has('handle_etags')) this.handle_etags ??= get_handle_etags.call(this) if (!generics.has('handle_validations')) this.handle_validations ??= get_handle_validations.call(this) if (!generics.has('handle_media_type')) this.handle_media_type ??= get_handle_media_type.call(this) if (!generics.has('handle_temporal_data')) this.handle_temporal_data ??= get_handle_temporal_data.call(this) if (!generics.has('handle_paging')) this.handle_paging ??= get_handle_paging.call(this) if (!generics.has('handle_sorting')) this.handle_sorting ??= get_handle_sorting.call(this) return super.init() } static get generics() { return (this._generics ??= new Set([ ...(this.__proto__.generics || []), ...Reflect.ownKeys(this).filter(p => p.startsWith?.('handle_')) ])) } static get handle_fiori() { return (this._handle_fiori ??= require('../fiori/lean-draft')) } static get handle_crud() { return (this._handle_crud ??= require('./generic/crud')) } // NOTE: undocumented static get handle_flows() { return (this._handle_flows ??= require('./generic/flows')) } // NOTE: undocumented static get handle_assert() { return (this._handle_assert ??= require('./generic/assert')) } // Overload .handle in order to resolve projections up to a definition that is known by the remote service instance. // Result is post processed according to the inverse projection in order to reflect the correct result of the original query. async handle(req) { // REVISIT: We must not allow arbitrary CQNs, so this shouldn't be here! if (this._requires_resolving(req)) { // rewrite the query to a target entity served by this service... const query = this.resolve.query(req.query) if (!query) cds.error`Target ${req.target.name} cannot be resolved for service ${this.name}` // we need to provide target explicitly because it's cached within ensure_target const target = cds.infer.target(query, this) const _req = new cds.Request({ query, target, _resolved: true }) return this.handle(_req).then(result => postProcess(_req, result, this)) } // async if (this.handle_authorization) await this.handle_authorization(req) if (this.handle_etags) await this.handle_etags(req) if (this.handle_validations) await this.handle_validations(req) // sync if (req.event === 'READ') { this.handle_temporal_data?.(req) this.handle_paging?.(req) this.handle_sorting?.(req) } else if (req.event === 'UPDATE') { this.handle_media_type?.(req) } return super.handle(req) } } // NOTE: getRestrictions is VERY INOFFICIAL!!! //> only kept temporary because of an ill-devised usage in AMS plugin const { getRestrictions } = require('./generic/auth/restrictions') ApplicationService.prototype.getRestrictions = function (..._) { return getRestrictions.call(this, ..._) } ApplicationService.prototype.isAppService = true module.exports = ApplicationService