@sap/cds
Version:
SAP Cloud Application Programming Model - CDS for Node.js
97 lines (83 loc) • 3.85 kB
JavaScript
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