Fixed cds.ql in latest release (#232)

* Fixed cds.ql in latest release

* Requires @sap/cds ^5.1.5

* fixed fixes

* More cdr tests

Co-authored-by: Christian Georgi <chgeo@users.noreply.github.com>
This commit is contained in:
Daniel Hutzel
2021-08-31 09:59:06 +02:00
committed by GitHub
parent 2f5d159428
commit f599206bf4
3 changed files with 164 additions and 169 deletions

View File

@@ -12,8 +12,7 @@
"@capire/media": "./media", "@capire/media": "./media",
"@capire/orders": "./orders", "@capire/orders": "./orders",
"@capire/reviews": "./reviews", "@capire/reviews": "./reviews",
"@sap/cds": "^5", "@sap/cds": "^5.1.5"
"express": "^4"
}, },
"devDependencies": { "devDependencies": {
"cds-swagger-ui-express": "^0.2.0", "cds-swagger-ui-express": "^0.2.0",

View File

@@ -1,17 +1,20 @@
const cds = require('@sap/cds/lib') const cds = require('@sap/cds/lib')
const { expect } = cds.test const { expect } = cds.test
const CQL = ([cql]) => cds.parse.cql(cql) const { cdr } = cds.ql
const Foo = { name: 'Foo' } const Foo = { name: 'Foo' }
const Books = { name: 'capire.bookshop.Books' } const Books = { name: 'capire.bookshop.Books' }
const { cdr } = cds.ql
// while jest has 'test' as alias to 'it', mocha doesn't const STAR = cdr ? '*' : { ref: ['*'] }
if (!global.test) global.test = it 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', () => { describe('cds.ql → cqn', () => {
// //
let cqn
describe(`SELECT...`, () => { describe(`SELECT...`, () => {
@@ -26,170 +29,192 @@ describe('cds.ql → cqn', () => {
.to.eql(SELECT.from(Foo,['*'])) .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'] } }, SELECT: { from: { ref: ['Foo'] } },
}) })
.to.eql(CQL`SELECT from Foo`)
.to.eql(SELECT.from(Foo))
}) })
test('from ( ..., <key>)', () => {
// Compiler test('from Foo [<key>]', () => {
expect(CQL`SELECT from Foo[11]`).to.eql({
SELECT: { expect(cqn = SELECT`from Foo[11]`)
// REVISIT: add one:true? .to.eql(SELECT`from Foo[${11}]`)
from: { ref: [{ id: 'Foo', where: [{ val: 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({ if (cdr) expect.plain (cqn)
SELECT: { .to.eql(srv.read`Foo[${11}]`)
// REVISIT: add one:true .to.eql(SELECT`Foo[${11}]`)
from: {
ref: [{ id: 'Foo', where: [{ ref: ['ID'] }, '=', { val: 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 if (cdr) expect.plain (cqn)
expect(SELECT.from(Foo, 11)) .to.eql(SELECT`Foo[ID=${11}]`)
.to.eql(SELECT.from(Foo, { ID: 11 })) .to.eql(srv.read`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 }],
},
})
expect(CQL`SELECT from Foo[11]{a}`).to.eql({ // Following implicitly resolve to SELECT.one
SELECT: { expect(cqn = SELECT.from(Foo,11))
// REVISIT: add one:true? .to.eql(SELECT.from(Foo,{ID:11}))
from: { ref: [{ id: 'Foo', where: [{ val: 11 }] }] }, .to.eql(SELECT.from(Foo).byKey(11))
columns: [{ ref: ['a'] }], .to.eql(SELECT.from(Foo).byKey({ID:11}))
}, expect.one(cqn)
}) .to.eql({
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 = {
SELECT: { SELECT: {
one: true,
from: { ref: ['Foo'] }, 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 test('from Foo {...}', () => {
expect(SELECT.from(Foo, ['a', 'b', '*']))
.to.eql(SELECT.from(Foo).columns('a', 'b', '*')) expect(cqn = SELECT `*,a,b as c` .from `Foo`)
.to.eql(SELECT.from(Foo).columns(['a', 'b', '*'])) .to.eql(SELECT `*,a,b as c`. from(Foo))
.to.eql( .to.eql(SELECT('*','a',{b:'c'}).from`Foo`)
SELECT.from(Foo, (foo) => { .to.eql(SELECT('*','a',{b:'c'}).from(Foo))
foo.a, foo.b, 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({ .to.eql({
SELECT: { SELECT: {
from: { ref: ['Foo'] }, 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 } } // SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } }
expect( expect(cqn =
SELECT.from(Foo, (foo) => { SELECT.from (Foo, foo => {
foo('*'), foo`*`, foo.x, foo.car`*`, foo.boo (b => {
foo.x, b`*`, b.moo.zoo(
foo.car('*'), x => x.y.z
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: { SELECT: {
from: { ref: ['Foo'] }, from: { ref: ['Foo'] },
columns: [ columns: [
cdr ? '*' : { ref: ['*'] }, STAR,
{ ref: ['x'] }, { ref: ['x'] },
{ ref: ['car'], expand: ['*'] }, { ref: ['car'], expand: ['*'] },
{ {
ref: ['boo'], 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 } } // SELECT from Foo { *, x, bar.*, car{*}, boo { *, moo.zoo } }
expect( expect(
SELECT.from(Foo, (foo) => { SELECT.from (Foo, foo => {
foo.bar('*'), foo.bar `*`,
foo.bar('.*'), //> leading dot indicates inline foo.bar `.*`, //> leading dot indicates inline
foo.boo((x) => x.moo.zoo), foo.boo(_ => _.moo.zoo), //> underscore arg name indicates inline
foo.boo((_) => _.moo.zoo) //> underscore arg name indicates inline foo.boo(x => x.moo.zoo)
}) })
).to.eql({ ).to.eql({
SELECT: { SELECT: {
@@ -197,39 +222,13 @@ describe('cds.ql → cqn', () => {
columns: [ columns: [
{ ref: ['bar'], expand: ['*'] }, { ref: ['bar'], expand: ['*'] },
{ ref: ['bar'], inline: ['*'] }, { ref: ['bar'], inline: ['*'] },
{ ref: ['boo'], expand: [{ ref: ['moo', 'zoo'] }] },
{ ref: ['boo'], inline: [{ 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:{...} }', () => { it('should correctly handle { ... and:{...} }', () => {
expect(SELECT.from(Foo).where({ x: 1, and: { y: 2, or: { z: 3 } } })).to.eql({ expect(SELECT.from(Foo).where({ x: 1, and: { y: 2, or: { z: 3 } } })).to.eql({
SELECT: { SELECT: {
@@ -427,6 +426,7 @@ describe('cds.ql → cqn', () => {
// //
}) })
}
describe(`INSERT...`, () => { describe(`INSERT...`, () => {
test('entries ({a,b}, ...)', () => { test('entries ({a,b}, ...)', () => {

View File

@@ -15,17 +15,17 @@ describe('Consuming Services locally', () => {
it('supports targets as strings or reflected defs', async () => { it('supports targets as strings or reflected defs', async () => {
const AdminService = await cds.connect.to('AdminService') const AdminService = await cds.connect.to('AdminService')
const { Authors } = AdminService.entities 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.read('Authors'))
.to.eql(await AdminService.run(SELECT.from(Authors))) .to.eql(await AdminService.run(SELECT.from(Authors)))
// temporary workaround .to.eql(await AdminService.run(SELECT.from('Authors')))
if (cds.version >= '4.2.0')
_.to.eql(await AdminService.run(SELECT.from('Authors')))
}) })
it('allows reading from local services using cds.ql', async () => { it('allows reading from local services using cds.ql', async () => {
const AdminService = await cds.connect.to('AdminService') const AdminService = await cds.connect.to('AdminService')
const query = SELECT.from('Authors', (a) => { const authors = await AdminService.read (`Authors`, a => {
a.name, a.name,
a.books((b) => { a.books((b) => {
b.title, b.title,
@@ -34,10 +34,6 @@ describe('Consuming Services locally', () => {
}) })
}) })
}).where(`name like`, 'E%') }).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([ expect(authors).to.containSubset([
{ {
name: 'Emily Brontë', name: 'Emily Brontë',