Compare commits

..

2 Commits

Author SHA1 Message Date
Daniel Hutzel
dec9ec2aba ES Modules 2023-11-18 11:13:58 +01:00
Daniel Hutzel
789faeb7c4 ES Modules 2023-11-18 11:13:25 +01:00
27 changed files with 3850 additions and 3558 deletions

34
.eslintrc Normal file
View File

@@ -0,0 +1,34 @@
{
"extends": [
"plugin:@sap/cds/recommended",
"eslint:recommended"
],
"env": {
"browser": true,
"es2022": true,
"node": true,
"jest": true,
"mocha": true
},
"parserOptions": {
"sourceType": "module"
},
"globals": {
"SELECT": true,
"INSERT": true,
"UPSERT": true,
"UPDATE": true,
"DELETE": true,
"CREATE": true,
"DROP": true,
"CDL": true,
"CQL": true,
"cds": true
},
"rules": {
"no-console": "off",
"require-atomic-updates": "off",
"require-await":"warn",
"no-unused-vars": ["warn", { "argsIgnorePattern": "_" }]
}
}

View File

@@ -5,13 +5,4 @@ updates:
directory: /
versioning-strategy: increase-if-necessary
schedule:
interval: weekly
groups:
production-dependencies:
dependency-type: "production"
development-dependencies:
dependency-type: "development"
ignore:
- dependency-name: "chai"
# chai 5 doesn't work atm w/ cds.test, TODO fix that in cds.test
versions: ["5.x"]
interval: daily

View File

@@ -11,6 +11,7 @@ on:
jobs:
build:
runs-on: ubuntu-latest
strategy:
@@ -18,21 +19,10 @@ jobs:
node-version: [20.x, 18.x]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 20.x
- run: npm ci
- run: npm run lint

View File

@@ -4,11 +4,13 @@
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"qwtel.sqlite-viewer",
"sapse.vscode-cds",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"mechatroner.rainbow-csv",
"humao.rest-client",
"alexcvzz.vscode-sqlite",
"hbenl.vscode-mocha-test-adapter",
"sdras.night-owl",
"vsls-contrib.codetour"
],

View File

@@ -14,6 +14,8 @@
"**/odata-v4/okra/**"
]
},
"mochaExplorer.debuggerConfig": "Debug Mocha Tests",
"mochaExplorer.parallel": true,
"eslint.probe": [
"cds",
"csn",

2
bookshop/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
@cds-models
@cds

View File

@@ -6,7 +6,7 @@
// NOTE: We use cds.on('served') to delay the UPSERTs after the db init
// to run after all INSERTs from .csv files happened.
module.exports = cds.on('served', ()=> cds.run(
cds.on('served', ()=> cds.run(
UPSERT.into ('sap.common.Currencies') .columns (
[ 'code', 'symbol', 'name' ]
) .rows (

View File

@@ -1,2 +1 @@
const { CatalogService } = require('./srv/cat-service')
module.exports = { CatalogService }
export { CatalogService } from './srv/cat-service'

11
bookshop/jsconfig.json Normal file
View File

@@ -0,0 +1,11 @@
{
"compilerOptions": {
"module": "NodeNext",
"moduleResolution": "nodenext",
"paths": {
"#cds/*": [
"./@cds/*"
]
}
}
}

View File

@@ -2,6 +2,7 @@
"name": "@capire/bookshop",
"version": "1.0.0",
"description": "A simple self-contained bookshop service.",
"type": "module",
"files": [
"app",
"srv",
@@ -10,15 +11,20 @@
"index.js"
],
"devDependencies": {
"@cap-js/sqlite": "*"
"@cap-js/sqlite": "*",
"@cap-js/cds-typer": ">=0.1"
},
"dependencies": {
"@sap/cds": ">=7",
"express": "^4.17.1"
"@sap/cds": "^7",
"express": "^4.17.1",
"passport": ">=0.4.1"
},
"scripts": {
"genres": "cds serve test/genres.cds",
"start": "cds-serve",
"watch": "cds watch"
},
"imports": {
"#cds/*": "./@cds/*/index.js"
}
}

View File

@@ -1,6 +1,6 @@
const cds = require('@sap/cds/lib')
import cds from '@sap/cds'
module.exports = class AdminService extends cds.ApplicationService { init(){
export class AdminService extends cds.ApplicationService { init(){
this.before ('NEW','Authors', genid)
this.before ('NEW','Books', genid)
return super.init()

View File

@@ -1,11 +1,13 @@
module.exports = class CatalogService extends cds.ApplicationService { init() {
import cds from '@sap/cds'
export class CatalogService extends cds.ApplicationService { init() {
const { Books } = cds.entities('sap.capire.bookshop')
const { ListOfBooks } = this.entities
// Add some discount for overstocked books
this.after('each', ListOfBooks, book => {
if (book.stock > 111) book.title += ` -- 11% discount!`
this.after('READ', ListOfBooks, each => {
if (each.stock > 111) each.title += ` -- 11% discount!`
})
// Reduce stock of ordered books if available stock suffices

View File

@@ -1,5 +1,5 @@
const cds = require('@sap/cds')
module.exports = class UserService extends cds.Service { init(){
import cds from '@sap/cds'
export class UserService extends cds.Service { init(){
this.on('READ', 'me', ({ tenant, user, locale }) => ({ id: user.id, locale, tenant }))
this.on('login', (req) => {
if (req.user._is_anonymous)

View File

@@ -1,27 +0,0 @@
const eslintCds = require('@sap/eslint-plugin-cds')
const eslintJs = require('@eslint/js')
const globals = require('globals')
module.exports = [
eslintJs.configs.recommended,
eslintCds.configs.recommended,
{
languageOptions: {
globals: {
sap: true,
...globals.es2022,
...globals.browser,
...globals.node,
...globals.jest,
...globals.mocha,
...eslintCds.configs.recommended.languageOptions.globals
}
},
rules: {
'no-console': 'off',
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
'require-atomic-updates': 'off',
'require-await': 'warn'
}
}
]

View File

@@ -2,10 +2,10 @@
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
# JCI app descriptor contains lower case TITLE
appTitle=Manage Authors
appTitle=Bookshop Authors
# JCI app descriptor contains lower case DESCRIPTION
appSubTitle=CAP Sample Application
appSubTitle=Bookshop Authors
# JCI app descriptor contains lower case DESCRIPTION
appDescription=Bookshop Authors

View File

@@ -35,7 +35,7 @@
"additionalParameters": "ignored"
},
"semanticObject": "Authors",
"action": "manage",
"action": "display",
"title": "{{appTitle}}",
"info": "{{appInfo}}",
"subTitle": "{{appSubTitle}}",

View File

@@ -2,7 +2,7 @@
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
# JCI app descriptor contains lower case TITLE
appTitle=Manage Books
appTitle=Bookshop Sample
# JCI app descriptor contains lower case DESCRIPTION
appSubTitle=CAP Sample Application

View File

@@ -19,22 +19,6 @@
"id": "ui5template.basicSAPUI5ApplicationProject",
"-id": "ui5template.smartTemplate",
"-version": "1.40.12"
},
"crossNavigation": {
"inbounds": {
"intent1": {
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"semanticObject": "Books",
"action": "manage",
"title": "{{appTitle}}",
"info": "{{appInfo}}",
"subTitle": "{{appSubTitle}}",
"icon": "sap-icon://course-book"
}
}
}
},
"sap.ui5": {

View File

@@ -50,7 +50,7 @@
"tileType": "sap.ushell.ui.tile.StaticTile",
"properties": {
"title": "Manage Authors",
"targetURL": "#Authors-manage"
"targetURL": "#Authors-display"
}
},
{
@@ -99,7 +99,7 @@
},
"BrowseAuthors": {
"semanticObject": "Authors",
"action": "manage",
"action": "display",
"title": "Browse Authors",
"signature": {
"parameters": {

View File

@@ -2,7 +2,7 @@
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
# JCI app descriptor contains lower case TITLE
appTitle=Browse Books
appTitle=Bookshop Sample
# JCI app descriptor contains lower case DESCRIPTION
appSubTitle=CAP Sample Application

View File

@@ -5,7 +5,8 @@
"@capire/bookstore": "*",
"@sap/cds": ">=5",
"@cap-js-community/odata-v2-adapter": "^1",
"express": "^4.17.1"
"express": "^4.17.1",
"passport": ">=0.4.1"
},
"devDependencies": {
"@cap-js/sqlite": "^1"
@@ -50,10 +51,5 @@
"hana": {
"deploy-format": "hdbtable"
}
},
"sapux": [
"app/admin-authors",
"app/admin-books",
"app/browse"
]
}
}

View File

@@ -1,4 +1,4 @@
import { Request } from "@sap/cds"
import type { Request } from "@sap/cds/apis/services"
module.exports = class say {
hello(req: Request) {

View File

@@ -2,7 +2,7 @@
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
# JCI app descriptor contains lower case TITLE
appTitle=Manage Orders
appTitle=Bookshop Sample
# JCI app descriptor contains lower case DESCRIPTION
appSubTitle=CAP Sample Application

962
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -21,12 +21,11 @@
],
"devDependencies": {
"@cap-js/sqlite": "^1",
"@sap/eslint-plugin-cds": "^3",
"@sap/eslint-plugin-cds": "^2.6.1",
"axios": "^1",
"chai": "^4.3.4",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"eslint": "^9",
"semver": "^7"
},
"scripts": {
@@ -39,8 +38,7 @@
"jest": "npx jest --silent",
"start": "cds watch fiori",
"test": "npm run jest -- --silent",
"test:hello": "cd hello && npm test",
"lint": "eslint ."
"test:hello": "cd hello && npm test"
},
"jest": {
"testTimeout": 20000,

View File

@@ -676,7 +676,7 @@ describe('cds.ql → cqn', () => {
.to.eql(INSERT.into(Foo).entries(...entries))
.to.eql(INSERT.into(Foo).entries(entries))
.to.eql({
INSERT: { into: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] }, entries },
INSERT: { into: 'Foo', entries },
})
})
@@ -692,7 +692,7 @@ describe('cds.ql → cqn', () => {
.to.eql(INSERT.into(Foo).columns('a', 'b').rows([1, 2], [3, 4]))
.to.eql({
INSERT: {
into: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] },
into: 'Foo',
columns: ['a', 'b'],
rows: [
[1, 2],
@@ -706,7 +706,7 @@ describe('cds.ql → cqn', () => {
expect(INSERT.into(Foo).columns('a', 'b').values([1, 2]))
.to.eql(INSERT.into(Foo).columns('a', 'b').values(1, 2))
.to.eql({
INSERT: { into: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] }, columns: ['a', 'b'], values: [1, 2] },
INSERT: { into: 'Foo', columns: ['a', 'b'], values: [1, 2] },
})
})
@@ -721,7 +721,7 @@ describe('cds.ql → cqn', () => {
test('entity (..., <key>)', () => {
const cqnWhere = {
UPDATE: {
entity: cds.env.ql.quirks_mode ? 'capire.bookshop.Books' : { ref: ['capire.bookshop.Books'] },
entity: 'capire.bookshop.Books',
where: [{ ref: ['ID'] }, '=', { val: 4711 }],
},
}
@@ -765,7 +765,7 @@ describe('cds.ql → cqn', () => {
.to.eql(UPDATE(Foo).with({ foo: 11, bar: { '-=': 22 } }))
.to.eql({
UPDATE: {
entity: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] },
entity: 'Foo',
data: { foo: 11 },
with: {
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
@@ -776,7 +776,7 @@ describe('cds.ql → cqn', () => {
// some more
expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
UPDATE: {
entity: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] },
entity: 'Foo',
data: {
car: "foo's bar, car",
},
@@ -796,7 +796,7 @@ describe('cds.ql → cqn', () => {
test('from (..., <key>)', () => {
const cqnWhere = {
DELETE: {
from: cds.env.ql.quirks_mode ? 'capire.bookshop.Books' : { ref: ['capire.bookshop.Books'] },
from: 'capire.bookshop.Books',
where: [{ ref: ['ID'] }, '=', { val: 4711 }],
},
}

View File

@@ -33,51 +33,8 @@ describe('cap/samples - Hierarchical Data', ()=>{
]}
))
it ('should generate correct queries for expands', ()=>{
let q = SELECT.from (Cats, c => { c.ID, c.name, c.children (c => c.name) })
expect (q) .to.eql ({
SELECT: {
from: { ref:[ "Categories" ] },
columns: [
{ ref: [ "ID" ] },
{ ref: [ "name" ] },
{ ref: [ "children" ], expand: [ {ref:['name']} ] },
]
}
})
if (q.forSQL) expect (q.forSQL()) .to.eql ({
SELECT: {
from: { ref:[ "Categories" ], as: "Categories" },
columns: [
{ ref: [ "Categories", "ID" ] },
{ ref: [ "Categories", "name" ] },
{ as: "children", SELECT: { expand: true,
one: false,
columns: [{ ref: [ "children", "name" ]}],
from: { ref:["Categories"], as: "children" },
where: [
{ref:[ "Categories", "ID" ]}, "=", {ref:[ "children", "parent_ID" ]}
],
}},
],
}
})
if (q.toSql) expect (q.toSql()) .to.eql (
`SELECT json_insert('{}',` +
`'$."ID"',ID,` +
`'$."name"',name,` +
`'$."children"',children->'$'` +
`) as _json_ FROM (` +
`SELECT Categories.ID,Categories.name,(` +
`SELECT jsonb_group_array(jsonb_insert('{}','$."name"',name)) as _json_ FROM (` +
`SELECT children.name FROM Categories as children WHERE Categories.ID = children.parent_ID` +
`)` +
`) as children FROM Categories as Categories` +
`)`
)
})
it ('supports nested reads', async()=>{
if (require('semver').gte(cds.version, '5.9.0')) {
expect (await
SELECT.one.from (Cats, c=>{
c.ID, c.name.as('parent'), c.children (c=>{
@@ -90,9 +47,24 @@ describe('cap/samples - Hierarchical Data', ()=>{
{ child:'Catwoman' },
]}
)
return
}
expect (await
SELECT.one.from (Cats, c=>{
c.ID, c.name.as('parent'), c.children (c=>{
c.name.as('child')
})
}) .where ({name:'Cat'})
) .to.eql (
{ ID:101, parent:'Cat', children:[
{ ID:102, child:'Kitty' },
{ ID:106, child:'Catwoman' },
]}
)
})
it ('supports deeply nested reads', async()=>{
if (require('semver').gte(cds.version, '5.9.0')) {
expect (await SELECT.one.from (Cats, c=>{
c.ID, c.name, c.children (
c => { c.name },
@@ -109,6 +81,24 @@ describe('cap/samples - Hierarchical Data', ()=>{
{ name:'Catalina', children:[] } ]},
]}
)
return
}
expect (await SELECT.one.from (Cats, c=>{
c.ID, c.name, c.children (
c => { c.name },
{levels:3}
)
}) .where ({name:'Cat'})
) .to.eql (
{ ID:101, name:'Cat', children:[
{ ID:102, name:'Kitty', children:[
{ ID:103, name:'Kitty Cat', children:[
{ ID:104, name:'Aristocat' }, ]}, // level 3
{ ID:105, name:'Kitty Bat', children:[] }, ]},
{ ID:106, name:'Catwoman', children:[
{ ID:107, name:'Catalina', children:[] } ]},
]}
)
})
it ('supports cascaded deletes', async()=>{