diff --git a/lib/compile/unfold/assocs.js b/lib/compile/unfold/assocs.js new file mode 100644 index 00000000..2aaca2d5 --- /dev/null +++ b/lib/compile/unfold/assocs.js @@ -0,0 +1,10 @@ +module.exports = (d) => { + if (d.kind === 'element' && !d.on && d.keys) { + // return console.log ('unfold assoc:', d.parent.name, d.name) + // Managed Associations become unmanaged ones + d.on = d.keys.reduce((on,k) => { + return on.concat([ {ref:[ d.name, ...k.ref ]}, '=', {ref:[ k.as||k.ref[0] ]} ]) + }, []) + Object.defineProperty (d, 'keys', {value:d.keys, configurable:true, enumerable:false}) + } +} diff --git a/lib/compile/unfold/index.js b/lib/compile/unfold/index.js new file mode 100644 index 00000000..9dd689b0 --- /dev/null +++ b/lib/compile/unfold/index.js @@ -0,0 +1,21 @@ +// https://github.tools.sap/cap/sflight-ea/blob/main/doc/CSN.md +const cds = require ('@sap/cds/lib') + +class Unfolds { + get structs() { return super.structs = require('./structs.js') } + get assocs() { return super.assocs = require('./assocs') } +} + +/** + * Use this function to unfold compact CSN models like that: + * @example cds.unfold(m,'assocs','structs') + * @type Unfolds & (csn:T,unfolds[]:string) => T + */ +const unfold = Object.setPrototypeOf((csn,...unfolds)=>{ + const done = (csn.meta ??= {}).unfolded ??= [] + const todo = unfolds.filter(u => !done.includes(u)) + try { return cds.linked(csn).forall(d => todo.forEach(t => unfold[t](d))) } + finally { done.push(...todo) } +}, new Unfolds) + +module.exports = unfold diff --git a/lib/compile/unfold/structs.js b/lib/compile/unfold/structs.js new file mode 100644 index 00000000..a762a575 --- /dev/null +++ b/lib/compile/unfold/structs.js @@ -0,0 +1,43 @@ +module.exports = (d) => { + if (d.kind === 'element' && (d.elements || d.foreignKeys)) { + // return console.log ('unfold struct', d.parent.name, d.name) + // add flattened elements for structs + for (let e in d.elements) { + let name = d.name+'_'+e + let flat = d.parent.elements[name] = { + __proto__: d.elements[e], key: d.key, + ... d.elements[e] // REVISIT: remove that for productive code + } + _add_derived (flat,'name',name) + } + // structs stay in model, bot become non-enumerable elements + if (!d.isAssociation) _add_derived (d.parent.elements, d.name, d) + // the flattended elements became keys -> structs associations aren't + delete d.key + // console.log (d.parent.name, d.name, d) + } +} + +const _add_derived = (o,p,v,enumerable=false) => { + Object.defineProperty (o,p, { value:v, enumerable, writable:true, configurable:true }) + return v +} + +;()=>{ + + let payload1 ={ + struct_a: 6, + struct_b: 6 + } + + let payload2 ={ + struct: { a: 6, b: 6 } + } + + let payload3 ={ + struct: { a: 6, b: 6 }, + struct_a: 7, + struct_b: 8 + } + +} diff --git a/lib/monkey-patch-cds.js b/lib/monkey-patch-cds.js new file mode 100644 index 00000000..37a0d3c3 --- /dev/null +++ b/lib/monkey-patch-cds.js @@ -0,0 +1,15 @@ +const cds = require("@sap/cds/lib") + +const unfold = require("./compile/unfold") +cds.compile.for.nodejs +cds.compile.for.nodejs = csn => { + _plain_csn ??= cds.compile(cds.clone(csn)) + return unfold(csn,'assocs','structs') +} + +const _2edm = cds.compile.to.edm +const _2sql = cds.compile.to.sql +cds.compile.to.edm = (csn,o) => _2edm(_plain_csn||csn,o) +cds.compile.to.sql = (csn,o) => _2sql(_plain_csn||csn,o) + +let _plain_csn diff --git a/server.js b/server.js new file mode 100644 index 00000000..a81708a6 --- /dev/null +++ b/server.js @@ -0,0 +1 @@ +require('./lib/monkey-patch-cds.js') \ No newline at end of file