diff --git a/package.json b/package.json index f625843f..2543c7c8 100644 --- a/package.json +++ b/package.json @@ -12,8 +12,7 @@ "@capire/media": "./media", "@capire/orders": "./orders", "@capire/reviews": "./reviews", - "@sap/cds": "^5", - "express": "^4" + "@sap/cds": "^5.1.5" }, "devDependencies": { "cds-swagger-ui-express": "^0.2.0", diff --git a/test/cds.ql.test.js b/test/cds.ql.test.js index 1a6fc861..85b80c96 100644 --- a/test/cds.ql.test.js +++ b/test/cds.ql.test.js @@ -1,17 +1,20 @@ const cds = require('@sap/cds/lib') const { expect } = cds.test -const CQL = ([cql]) => cds.parse.cql(cql) +const { cdr } = cds.ql const Foo = { name: 'Foo' } const Books = { name: 'capire.bookshop.Books' } -const { cdr } = cds.ql -// while jest has 'test' as alias to 'it', mocha doesn't -if (!global.test) global.test = it +const STAR = cdr ? '*' : { ref: ['*'] } +const skip = {to:{eql:()=>skip}} +const srv = new cds.Service +let cqn + +expect.plain = (cqn) => !cqn.SELECT.one && !cqn.SELECT.distinct ? expect(cqn) : skip +expect.one = (cqn) => !cqn.SELECT.distinct ? expect(cqn) : skip describe('cds.ql → cqn', () => { // - let cqn describe(`SELECT...`, () => { @@ -26,170 +29,192 @@ describe('cds.ql → cqn', () => { .to.eql(SELECT.from(Foo,['*'])) }) - test('from ( Foo )', () => { - expect({ + + it('should consistently handle lists', () => { + const ID = 11, args = [{ref:['foo']}, 'bar', 3] + const cqn = CQL`SELECT from Foo where ID=11 and x in (foo,'bar',3)` + expect(SELECT.from`Foo`.where `ID=${ID} and x in ${args}`).to.eql(cqn) + expect(SELECT.from(Foo).where(`ID=`, ID, `and x in`, args)).to.eql(cqn) + expect(SELECT.from(Foo).where({ ID, x:args })).to.eql(cqn) + }) + + }) + + + for (let each of ['SELECT', 'SELECT one', 'SELECT distinct']) { + let SELECT; beforeEach(()=> SELECT = ( + each === 'SELECT distinct' ? cds.ql.SELECT.distinct : + each === 'SELECT one' ? cds.ql.SELECT.one : + cds.ql.SELECT + )) + describe(`${each}...`, () => { + + test(`from Foo`, () => { + expect(cqn = SELECT `from Foo`) + .to.eql(SELECT.from `Foo`) + .to.eql(SELECT.from('Foo')) + .to.eql(SELECT.from(Foo)) + .to.eql(SELECT`Foo`) + .to.eql(SELECT('Foo')) + .to.eql(SELECT(Foo)) + expect.plain(cqn) + .to.eql(CQL`SELECT from Foo`) + .to.eql(srv.read `Foo`) + .to.eql(srv.read('Foo')) + .to.eql(srv.read(Foo)) + .to.eql({ SELECT: { from: { ref: ['Foo'] } }, }) - .to.eql(CQL`SELECT from Foo`) - .to.eql(SELECT.from(Foo)) }) - test('from ( ..., )', () => { - // Compiler - expect(CQL`SELECT from Foo[11]`).to.eql({ - SELECT: { - // REVISIT: add one:true? - from: { ref: [{ id: 'Foo', where: [{ val: 11 }] }] }, - }, + + test('from Foo []', () => { + + expect(cqn = SELECT`from Foo[11]`) + .to.eql(SELECT`from Foo[${11}]`) + .to.eql(SELECT.from `Foo[11]`) + .to.eql(SELECT.from `Foo[${11}]`) + .to.eql(SELECT`Foo[11]`) + expect.plain(cqn) + .to.eql(CQL`SELECT from Foo[11]`) + .to.eql(srv.read`Foo[11]`) + .to.eql({ + SELECT: { from: { + ref: [{ id: 'Foo', where: [{ val: 11 }] }] + }}, }) - expect(CQL`SELECT from Foo[ID=11]`).to.eql({ - SELECT: { - // REVISIT: add one:true - from: { - ref: [{ id: 'Foo', where: [{ ref: ['ID'] }, '=', { val: 11 }] }], - }, - }, + if (cdr) expect.plain (cqn) + .to.eql(srv.read`Foo[${11}]`) + .to.eql(SELECT`Foo[${11}]`) + + expect((cqn = SELECT`from Foo[ID=11]`)) + .to.eql(SELECT`from Foo[ID=${11}]`) + .to.eql(SELECT.from `Foo[ID=11]`) + .to.eql(SELECT.from `Foo[ID=${11}]`) + .to.eql(SELECT`Foo[ID=11]`) + expect.plain(cqn) + .to.eql(CQL`SELECT from Foo[ID=11]`) + .to.eql(srv.read`Foo[ID=11]`) + .to.eql({ + SELECT: { from: { + ref: [{ id: 'Foo', where: [{ ref: ['ID'] }, '=', { val: 11 }] }], + }}, }) - // Runtime ds.ql - expect(SELECT.from(Foo, 11)) - .to.eql(SELECT.from(Foo, { ID: 11 })) - .to.eql(SELECT.from(Foo).byKey(11)) - .to.eql(SELECT.from(Foo).byKey({ ID: 11 })) - .to.eql(SELECT.one.from(Foo).where({ ID: 11 })) - .to.eql({ - // REVISIT: should produce CQN as the ones above? - SELECT: { - one: true, - from: { ref: ['Foo'] }, - where: [{ ref: ['ID'] }, '=', { val: 11 }], - }, - }) + if (cdr) expect.plain (cqn) + .to.eql(SELECT`Foo[ID=${11}]`) + .to.eql(srv.read`Foo[ID=${11}]`) - expect(CQL`SELECT from Foo[11]{a}`).to.eql({ - SELECT: { - // REVISIT: add one:true? - from: { ref: [{ id: 'Foo', where: [{ val: 11 }] }] }, - columns: [{ ref: ['a'] }], - }, - }) - - expect(SELECT.from(Foo, 11, ['a'])) - .to.eql(SELECT.from(Foo, 11, (foo) => foo.a)) - .to.eql({ - // REVISIT: should produce CQN as the ones above? - SELECT: { - one: true, - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }], - where: [{ ref: ['ID'] }, '=', { val: 11 }], - }, - }) - }) - - test('from ( ..., => {...})', () => { - // single *, prefix and postfix, as array and function - let parsed, fluid - expect((parsed = CQL`SELECT * from Foo`)).to.eql(CQL`SELECT from Foo{*}`) - //> .to.eql... FIXME: see skipped 'should handle * correctly' below - expect((fluid = SELECT('*').from(Foo))) - .to.eql(SELECT.from(Foo, ['*'])) - .to.eql(SELECT.from(Foo, (foo) => foo('*'))) - .to.eql(SELECT.from(Foo).columns('*')) - .to.eql(SELECT.from(Foo).columns((foo) => foo('*'))) - .to.eql({ - SELECT: { from: { ref: ['Foo'] }, columns: [cdr ? '*' : { ref: ['*'] }] }, - }) - - if (cdr === 'all') expect(parsed).to.eql(fluid) - - // single column, prefix and postfix, as array and function - expect(CQL`SELECT a from Foo`) - expect(CQL`SELECT from Foo {a}`) - .to.eql(SELECT.from(Foo, ['a'])) - .to.eql(SELECT.from(Foo, (foo) => foo.a)) - .to.eql({ - SELECT: { from: { ref: ['Foo'] }, columns: [{ ref: ['a'] }] }, - }) - - // multiple columns, prefix and postfix, as array and function - expect(CQL`SELECT a,b as c from Foo`) - - expect (CQL`SELECT from Foo {a,b as c}`).to.eql(cqn = { + // Following implicitly resolve to SELECT.one + expect(cqn = SELECT.from(Foo,11)) + .to.eql(SELECT.from(Foo,{ID:11})) + .to.eql(SELECT.from(Foo).byKey(11)) + .to.eql(SELECT.from(Foo).byKey({ID:11})) + expect.one(cqn) + .to.eql({ SELECT: { + one: true, from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'], as: 'c' }], + where: [{ ref: ['ID'] }, '=', { val: 11 }], }, }) - expect(SELECT.from(Foo, ['a', { b: 'c' }])).to.eql(cqn) - expect( - SELECT.from(Foo, (foo) => { - foo.a, foo.b.as('c') - }) - ).to.eql(cqn) - expect(SELECT.from(Foo).columns('a', { b: 'c' })).to.eql(cqn) - expect(SELECT.from(Foo).columns(['a', { b: 'c' }])).to.eql(cqn) - expect( - SELECT.from(Foo).columns((foo) => { - foo.a, foo.b.as('c') - }) - ).to.eql(cqn) - // multiple columns and *, prefix and postfix, as array and function - expect(CQL`SELECT *,a,b from Foo`).to.eql(CQL`SELECT from Foo{*,a,b}`) - //> .to.eql... FIXME: see skipped 'should handle * correctly' below - expect(SELECT.from(Foo, ['a', 'b', '*'])) - .to.eql(SELECT.from(Foo).columns('a', 'b', '*')) - .to.eql(SELECT.from(Foo).columns(['a', 'b', '*'])) - .to.eql( - SELECT.from(Foo, (foo) => { - foo.a, foo.b, foo('*') - }) - ) + }) + + test('from Foo {...}', () => { + + expect(cqn = SELECT `*,a,b as c` .from `Foo`) + .to.eql(SELECT `*,a,b as c`. from(Foo)) + .to.eql(SELECT('*','a',{b:'c'}).from`Foo`) + .to.eql(SELECT('*','a',{b:'c'}).from(Foo)) + .to.eql(SELECT(['*','a',{b:'c'}]).from(Foo)) + .to.eql(SELECT.columns('*','a',{b:'c'}).from(Foo)) + .to.eql(SELECT.columns(['*','a',{b:'c'}]).from(Foo)) + .to.eql(SELECT.columns((foo) => { foo`.*`, foo.a, foo.b`as c` }).from(Foo)) + .to.eql(SELECT.columns((foo) => { foo('*'), foo.a, foo.b.as('c') }).from(Foo)) + .to.eql(SELECT.from(Foo).columns('*','a',{b:'c'})) + .to.eql(SELECT.from(Foo).columns(['*','a',{b:'c'}])) + .to.eql(SELECT.from(Foo).columns((foo) => { foo`.*`, foo.a, foo.b`as c` })) + .to.eql(SELECT.from(Foo).columns((foo) => { foo('*'), foo.a, foo.b.as('c') })) + .to.eql(SELECT.from(Foo,['*','a',{b:'c'}])) + .to.eql(SELECT.from(Foo, (foo) => { foo`.*`, foo.a, foo.b`as c` })) + .to.eql(SELECT.from(Foo, (foo) => { foo('*'), foo.a, foo.b.as('c') })) + + expect.plain(cqn) .to.eql({ SELECT: { from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }, { ref: ['b'] }, cdr ? '*' : { ref: ['*'] }], + columns: [ STAR, { ref: ['a'] }, { ref: ['b'], as: 'c' }], }, }) + + cdr && expect.plain(cqn) + .to.eql(CQL`SELECT *,a,b as c from Foo`) + .to.eql(CQL`SELECT from Foo {*,a,b as c}`) + + // Test combination with key as second argument to .from + expect(cqn = SELECT.from(Foo, 11, ['a'])) + .to.eql(SELECT.from(Foo, 11, foo => foo.a)) + expect.one(cqn) + .to.eql({ + SELECT: { + one: true, + from: { ref: ['Foo'] }, + columns: [{ ref: ['a'] }], + where: [{ ref: ['ID'] }, '=', { val: 11 }], + }, + }) + }) - test('from ( ..., => _.expand ( x=>{...}))', () => { + test('with nested expands', () => { // SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } } - expect( - SELECT.from(Foo, (foo) => { - foo('*'), - foo.x, - foo.car('*'), - foo.boo((b) => { - b('*'), b.moo.zoo((x) => x.y.z) - }) + expect(cqn = + SELECT.from (Foo, foo => { + foo`*`, foo.x, foo.car`*`, foo.boo (b => { + b`*`, b.moo.zoo( + x => x.y.z + ) + }) }) - ).to.eql({ + ).to.eql( + SELECT.from (Foo, foo => { + foo('*'), foo.x, foo.car('*'), foo.boo (b => { + b('*'), b.moo.zoo( + x => x.y.z + ) + }) + }) + ) + + expect.plain(cqn) + .to.eql({ SELECT: { from: { ref: ['Foo'] }, columns: [ - cdr ? '*' : { ref: ['*'] }, + STAR, { ref: ['x'] }, { ref: ['car'], expand: ['*'] }, { ref: ['boo'], - expand: ['*', { ref: ['moo', 'zoo'], expand: [{ ref: ['y', 'z'] }] }], + expand: [ '*', { ref: ['moo', 'zoo'], expand: [{ ref: ['y', 'z'] }] }], }, ], }, }) }) - test('from ( ..., => _.inline ( _=>{...}))', () => { + if (each !== 'SELECT') return + + test('with nested inlines', () => { // SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } } expect( - SELECT.from(Foo, (foo) => { - foo.bar('*'), - foo.bar('.*'), //> leading dot indicates inline - foo.boo((x) => x.moo.zoo), - foo.boo((_) => _.moo.zoo) //> underscore arg name indicates inline + SELECT.from (Foo, foo => { + foo.bar `*`, + foo.bar `.*`, //> leading dot indicates inline + foo.boo(_ => _.moo.zoo), //> underscore arg name indicates inline + foo.boo(x => x.moo.zoo) }) ).to.eql({ SELECT: { @@ -197,39 +222,13 @@ describe('cds.ql → cqn', () => { columns: [ { ref: ['bar'], expand: ['*'] }, { ref: ['bar'], inline: ['*'] }, - { ref: ['boo'], expand: [{ ref: ['moo', 'zoo'] }] }, { ref: ['boo'], inline: [{ ref: ['moo', 'zoo'] }] }, + { ref: ['boo'], expand: [{ ref: ['moo', 'zoo'] }] }, ], }, }) }) - test('one / distinct ...', () => { - expect(SELECT.distinct.from(Foo).SELECT) - // .to.eql(CQL(`SELECT distinct from Foo`).SELECT) - .to.eql(SELECT.distinct(Foo).SELECT) - .to.eql({ distinct: true, from: { ref: ['Foo'] } }) - - expect(SELECT.one.from(Foo).SELECT) - // .to.eql(CQL(`SELECT one from Foo`).SELECT) - .to.eql(SELECT.one(Foo).SELECT) - .to.eql({ one: true, from: { ref: ['Foo'] } }) - - expect(SELECT.one('a').from(Foo).SELECT) - // .to.eql(CQL(`SELECT distinct a from Foo`).SELECT) - .to.eql(SELECT.one(['a']).from(Foo).SELECT) - .to.eql(SELECT.one(Foo, ['a']).SELECT) - .to.eql(SELECT.one(Foo, (foo) => foo.a).SELECT) - .to.eql(SELECT.one.from(Foo, (foo) => foo.a).SELECT) - .to.eql(SELECT.one.from(Foo, ['a']).SELECT) - .to.eql({ - one: true, - from: { ref: ['Foo'] }, - columns: [{ ref: ['a'] }], - }) - // same for works distinct - }) - it('should correctly handle { ... and:{...} }', () => { expect(SELECT.from(Foo).where({ x: 1, and: { y: 2, or: { z: 3 } } })).to.eql({ SELECT: { @@ -427,6 +426,7 @@ describe('cds.ql → cqn', () => { // }) +} describe(`INSERT...`, () => { test('entries ({a,b}, ...)', () => { diff --git a/test/consuming-services.test.js b/test/consuming-services.test.js index 97c9db24..936425fe 100644 --- a/test/consuming-services.test.js +++ b/test/consuming-services.test.js @@ -15,17 +15,17 @@ describe('Consuming Services locally', () => { it('supports targets as strings or reflected defs', async () => { const AdminService = await cds.connect.to('AdminService') const { Authors } = AdminService.entities - const _ = expect (await AdminService.read(Authors)) + expect (await SELECT.from(Authors)) + .to.eql(await SELECT.from('Authors')) + .to.eql(await AdminService.read(Authors)) .to.eql(await AdminService.read('Authors')) .to.eql(await AdminService.run(SELECT.from(Authors))) - // temporary workaround - if (cds.version >= '4.2.0') - _.to.eql(await AdminService.run(SELECT.from('Authors'))) + .to.eql(await AdminService.run(SELECT.from('Authors'))) }) it('allows reading from local services using cds.ql', async () => { const AdminService = await cds.connect.to('AdminService') - const query = SELECT.from('Authors', (a) => { + const authors = await AdminService.read (`Authors`, a => { a.name, a.books((b) => { b.title, @@ -34,10 +34,6 @@ describe('Consuming Services locally', () => { }) }) }).where(`name like`, 'E%') - // temporary workaround - if (cds.version < '4.2.0') - query.SELECT.from.ref[0] = 'AdminService.Authors' - const authors = await AdminService.run(query) expect(authors).to.containSubset([ { name: 'Emily Brontë',