UNPKG

@sap/cds

Version:

SAP Cloud Application Programming Model - CDS for Node.js

133 lines (115 loc) 4.4 kB
const { LinkedDefinitions, struct, type } = require ('./classes') const _is_cached = Symbol('is cached') const _derived = Symbol('derived') class entity extends struct { is (kind) { return kind === 'entity' || kind === 'view' && !!this.query || super.is(kind) } /** * Experimental API to write: * ```js * SELECT.from(Foo.as('bar')) // ... as alternative to: * SELECT.from(Foo).alias('bar') * ``` */ as (alias) { return { ntt:this, ref:[this.name], as:alias } } get keys() { return this._elements ('key') } get associations() { return this._elements ('isAssociation') } get compositions() { return this._elements ('isComposition') } _elements (kind, _default = null) { const derived = this.own(_derived, ()=>({})) if (kind in derived) return derived[kind] const {elements} = this, dict = new LinkedDefinitions; let any = _default for (let e in elements) if (elements[e][kind]) (any = dict)[e] = elements[e] return derived[kind] = any } toJSON() { return this.own('query') ? {...this} : super.toJSON(this) } toString() { return this.name.replace(/\./g,'_') } } class Association extends type { is(kind) { return kind === 'Association' || super.is(kind) } get is2many() { return !this.is2one } get is2one() { let c = this.cardinality return !c || c.max === 1 || (!c.max && !c.targetMax) || c.targetMax === 1 } set foreignKeys(k) { this.set('foreignKeys', k) } get foreignKeys() { const keys = this.keys; if (!keys) return this.foreignKeys = undefined if (!Object.keys(keys).length) return this.foreignKeys = null const foreignKeys = new LinkedDefinitions for (const k of keys) { const el = k.ref.reduce((target,n)=> target.elements[n], this._target) const as = k.as || k.ref.at(-1) foreignKeys[as] = Object.create (el, { name:{value:as}, notNull: {value:true} }) //, parent:{value:this} }) // For structures, foreign keys must be provided, (autogenerated) UUIDs are skipped for validation // REVISIT: We should change the line above to set parent correctly, as in the commented tail part. // But that breaks code in _runtime which expects it to be the wrong parent. } return this.foreignKeys = foreignKeys } set keys(k) { super.keys = k } get keys() { if (this.on || this.is2many || !this._target) return this.set('_keys', undefined) const keys=[], tks = this._target.keys for (let k in tks) keys.push({ ref: [tks[k].name] }) return this.keys = keys } /** * Gets the foreign key data for a given managed association from inbound data * converted to an object that can be used as a where clause filter to fetch * from the assoc's target entity. The function correctly handles renamed * foreign keys. * * @example * entity Books { //... * author : Association to Authors { ID:id } // renamed foreign key * } * * @example * let { Books, Authors } = srv.entities * let { author } = Books.elements * let data = { // inbound data, e.g. from req.data * title: 'Foo', * author_id: 111 * } * let entry = author.refIn(data) * //> { ref: [ {id:'Authors', where:[ {ref:['ID']}, '=', {val:111} ]} ]} * await SELECT.one(entry) * await db.exists(entry) */ refIn (d) { if (!this.keys) throw new Error (`Can't collect filter data for unmanaged associations`) const value = this.dataIn(d); if (!value) return const where={}; for (let {ref:[r],as} of this.keys) where[r] = value[as||r] const q = SELECT.from (this._target, where) return q.SELECT.from } /** * Overriding implementation in base class to handle data for to-many Associations. */ dataIn (d, prefix='') { if (this.is2many) { const key = this.name, _key = '_'+key; if (_key in d) return d[_key] const input = d[key]; if (input[_is_cached]) return input if (Array.isArray(input)) { const value = d[key].map (each => this._target.dataIn (each,'',true)) if (!prefix && d._hull) d._hull[key] = Object.defineProperty(value, _is_cached, {value:true}) return value } } else return struct.prototype.dataIn.call (this, d, prefix) } } class Composition extends Association { is(kind) { return kind === 'Composition' || super.is(kind) } } module.exports = { entity, Association, Composition }