Compare commits
96 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ea79264e9d | ||
|
|
c2d1217e4a | ||
|
|
d739680180 | ||
|
|
326729e03d | ||
|
|
6e56eb9c01 | ||
|
|
1a0d3e60cf | ||
|
|
402a5816e3 | ||
|
|
cdb9ae6436 | ||
|
|
4ba262f02a | ||
|
|
187014d98b | ||
|
|
85f24970de | ||
|
|
a191c4fd50 | ||
|
|
ac3cfa81c7 | ||
|
|
710dd7ac32 | ||
|
|
bbe2bae087 | ||
|
|
ec8608faab | ||
|
|
4c46f115f5 | ||
|
|
c6b88f6b66 | ||
|
|
aaa94e2d5f | ||
|
|
0f6809a45b | ||
|
|
6249fa2270 | ||
|
|
9e02e2c796 | ||
|
|
d9f50d635f | ||
|
|
546f374b08 | ||
|
|
bb127bcc58 | ||
|
|
d08749fd21 | ||
|
|
be5696b14e | ||
|
|
9263f747c7 | ||
|
|
ead660bfe4 | ||
|
|
e2712acaec | ||
|
|
426b64caaf | ||
|
|
ac6d87dc03 | ||
|
|
b716b1e0f8 | ||
|
|
de8fd4b86a | ||
|
|
464e7c40dc | ||
|
|
cabaf29c63 | ||
|
|
68a50148fe | ||
|
|
71ff72cdd8 | ||
|
|
74fe9fb76f | ||
|
|
fd770b4a91 | ||
|
|
8eb42d1f98 | ||
|
|
4d3cd6be90 | ||
|
|
4f505e4cfe | ||
|
|
e23b04fffd | ||
|
|
0fb8dfd5c2 | ||
|
|
8d01b05034 | ||
|
|
809b96bc96 | ||
|
|
41a9e494ce | ||
|
|
c4432d8dd7 | ||
|
|
167a39790f | ||
|
|
8acd671f57 | ||
|
|
5d7af63ec1 | ||
|
|
ee1c30d464 | ||
|
|
8988bc8bc6 | ||
|
|
2e0945a10b | ||
|
|
38e19a0cc3 | ||
|
|
0e9d900d49 | ||
|
|
687c789ed5 | ||
|
|
41e431a119 | ||
|
|
c4515bef51 | ||
|
|
43d3512627 | ||
|
|
cf956bfc45 | ||
|
|
3a5a177796 | ||
|
|
8b10d8e054 | ||
|
|
ea42ad6ab3 | ||
|
|
8bef766860 | ||
|
|
ba1a8dd989 | ||
|
|
0364cf68bb | ||
|
|
ab500b36a8 | ||
|
|
708539fc21 | ||
|
|
816057f005 | ||
|
|
6f523089cd | ||
|
|
a3af15bc4f | ||
|
|
d4133e7def | ||
|
|
bb8cfd31c4 | ||
|
|
a0b1fa6df4 | ||
|
|
143c72a5e1 | ||
|
|
4c3c49260a | ||
|
|
ed788d4834 | ||
|
|
675e966c4b | ||
|
|
8228800c80 | ||
|
|
d5424f100d | ||
|
|
e186237100 | ||
|
|
27800aa2b1 | ||
|
|
d0b0a954bd | ||
|
|
e3c639763b | ||
|
|
d24e44e8f0 | ||
|
|
b5cc148499 | ||
|
|
283660aff3 | ||
|
|
06d605518d | ||
|
|
aad19ab2c9 | ||
|
|
5ffbd2b330 | ||
|
|
e08e5e0d3d | ||
|
|
f2bcc6c021 | ||
|
|
cf1ff4d61f | ||
|
|
2144e09c47 |
31
.eslintrc
31
.eslintrc
@@ -1,31 +0,0 @@
|
|||||||
{
|
|
||||||
"extends": [
|
|
||||||
"plugin:@sap/cds/recommended",
|
|
||||||
"eslint:recommended"
|
|
||||||
],
|
|
||||||
"env": {
|
|
||||||
"browser": true,
|
|
||||||
"es2022": true,
|
|
||||||
"node": true,
|
|
||||||
"jest": true,
|
|
||||||
"mocha": true
|
|
||||||
},
|
|
||||||
"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": "_" }]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
14
.github/dependabot.yml
vendored
14
.github/dependabot.yml
vendored
@@ -5,4 +5,16 @@ updates:
|
|||||||
directory: /
|
directory: /
|
||||||
versioning-strategy: increase-if-necessary
|
versioning-strategy: increase-if-necessary
|
||||||
schedule:
|
schedule:
|
||||||
interval: daily
|
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"]
|
||||||
|
- dependency-name: "chai-as-promised"
|
||||||
|
# chai-as-promised 8 doesn't work atm w/ cds.test, TODO fix that in cds.test
|
||||||
|
versions: ["8.x"]
|
||||||
|
|||||||
16
.github/workflows/node.js.yml
vendored
16
.github/workflows/node.js.yml
vendored
@@ -11,7 +11,6 @@ on:
|
|||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
build:
|
build:
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
@@ -19,10 +18,21 @@ jobs:
|
|||||||
node-version: [20.x, 18.x]
|
node-version: [20.x, 18.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v4
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
- name: Use Node.js ${{ matrix.node-version }}
|
||||||
uses: actions/setup-node@v1
|
uses: actions/setup-node@v4
|
||||||
with:
|
with:
|
||||||
node-version: ${{ matrix.node-version }}
|
node-version: ${{ matrix.node-version }}
|
||||||
- run: npm ci
|
- run: npm ci
|
||||||
- run: npm test
|
- 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
|
||||||
|
|||||||
4
.vscode/extensions.json
vendored
4
.vscode/extensions.json
vendored
@@ -4,13 +4,11 @@
|
|||||||
|
|
||||||
// List of extensions which should be recommended for users of this workspace.
|
// List of extensions which should be recommended for users of this workspace.
|
||||||
"recommendations": [
|
"recommendations": [
|
||||||
|
"qwtel.sqlite-viewer",
|
||||||
"sapse.vscode-cds",
|
"sapse.vscode-cds",
|
||||||
"dbaeumer.vscode-eslint",
|
"dbaeumer.vscode-eslint",
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"mechatroner.rainbow-csv",
|
"mechatroner.rainbow-csv",
|
||||||
"humao.rest-client",
|
"humao.rest-client",
|
||||||
"alexcvzz.vscode-sqlite",
|
|
||||||
"hbenl.vscode-mocha-test-adapter",
|
|
||||||
"sdras.night-owl",
|
"sdras.night-owl",
|
||||||
"vsls-contrib.codetour"
|
"vsls-contrib.codetour"
|
||||||
],
|
],
|
||||||
|
|||||||
2
.vscode/settings.json
vendored
2
.vscode/settings.json
vendored
@@ -14,8 +14,6 @@
|
|||||||
"**/odata-v4/okra/**"
|
"**/odata-v4/okra/**"
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
"mochaExplorer.debuggerConfig": "Debug Mocha Tests",
|
|
||||||
"mochaExplorer.parallel": true,
|
|
||||||
"eslint.probe": [
|
"eslint.probe": [
|
||||||
"cds",
|
"cds",
|
||||||
"csn",
|
"csn",
|
||||||
|
|||||||
@@ -13,9 +13,8 @@
|
|||||||
"@cap-js/sqlite": "*"
|
"@cap-js/sqlite": "*"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sap/cds": "^7",
|
"@sap/cds": ">=7",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1"
|
||||||
"passport": ">=0.4.1"
|
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"genres": "cds serve test/genres.cds",
|
"genres": "cds serve test/genres.cds",
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
module.exports = class AdminService extends cds.ApplicationService { init(){
|
module.exports = class AdminService extends cds.ApplicationService { init(){
|
||||||
this.before ('NEW','Authors', genid)
|
this.before ('NEW','Authors', genid)
|
||||||
|
|||||||
@@ -1,29 +1,35 @@
|
|||||||
const cds = require('@sap/cds')
|
const cds = require('@sap/cds')
|
||||||
|
module.exports = class CatalogService extends cds.ApplicationService { init() {
|
||||||
|
|
||||||
class CatalogService extends cds.ApplicationService { init(){
|
const { Books } = cds.entities('sap.capire.bookshop')
|
||||||
|
|
||||||
const { Books } = cds.entities ('sap.capire.bookshop')
|
|
||||||
const { ListOfBooks } = this.entities
|
const { ListOfBooks } = this.entities
|
||||||
|
|
||||||
// Reduce stock of ordered books if available stock suffices
|
|
||||||
this.on ('submitOrder', async req => {
|
|
||||||
const {book,quantity} = req.data
|
|
||||||
if (quantity < 1) return req.reject (400,`quantity has to be 1 or more`)
|
|
||||||
let b = await SELECT `stock` .from (Books,book)
|
|
||||||
if (!b) return req.error (404,`Book #${book} doesn't exist`)
|
|
||||||
let {stock} = b
|
|
||||||
if (quantity > stock) return req.reject (409,`${quantity} exceeds stock for book #${book}`)
|
|
||||||
await UPDATE (Books,book) .with ({ stock: stock -= quantity })
|
|
||||||
await this.emit ('OrderedBook', { book, quantity, buyer:req.user.id })
|
|
||||||
return { stock }
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add some discount for overstocked books
|
// Add some discount for overstocked books
|
||||||
this.after ('READ', ListOfBooks, each => {
|
this.after('each', ListOfBooks, book => {
|
||||||
if (each.stock > 111) each.title += ` -- 11% discount!`
|
if (book.stock > 111) book.title += ` -- 11% discount!`
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Reduce stock of ordered books if available stock suffices
|
||||||
|
this.on('submitOrder', async req => {
|
||||||
|
let { book:id, quantity } = req.data
|
||||||
|
let book = await SELECT.from (Books, id, b => b.stock)
|
||||||
|
|
||||||
|
// Validate input data
|
||||||
|
if (!book) return req.error (404, `Book #${id} doesn't exist`)
|
||||||
|
if (quantity < 1) return req.error (400, `quantity has to be 1 or more`)
|
||||||
|
if (quantity > book.stock) return req.error (409, `${quantity} exceeds stock for book #${id}`)
|
||||||
|
|
||||||
|
// Reduce stock in database and return updated stock value
|
||||||
|
await UPDATE (Books, id) .with ({ stock: book.stock -= quantity })
|
||||||
|
return book
|
||||||
|
})
|
||||||
|
|
||||||
|
// Emit event when an order has been submitted
|
||||||
|
this.after('submitOrder', async (_,req) => {
|
||||||
|
let { book, quantity } = req.data
|
||||||
|
await this.emit('OrderedBook', { book, quantity, buyer: req.user.id })
|
||||||
|
})
|
||||||
|
|
||||||
|
// Delegate requests to the underlying generic service
|
||||||
return super.init()
|
return super.init()
|
||||||
}}
|
}}
|
||||||
|
|
||||||
module.exports = { CatalogService }
|
|
||||||
|
|||||||
36
eslint.config.js
Normal file
36
eslint.config.js
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
const cds = require('@sap/eslint-plugin-cds')
|
||||||
|
const globals = require('globals')
|
||||||
|
const js = require('@eslint/js')
|
||||||
|
|
||||||
|
module.exports = [
|
||||||
|
cds.configs.recommended,
|
||||||
|
js.configs.recommended,
|
||||||
|
{
|
||||||
|
languageOptions: {
|
||||||
|
globals: {
|
||||||
|
es2022: true,
|
||||||
|
...globals.browser,
|
||||||
|
...globals.node,
|
||||||
|
...globals.jest,
|
||||||
|
...globals.mocha,
|
||||||
|
cds: true,
|
||||||
|
sap: true,
|
||||||
|
CDL: true,
|
||||||
|
CQL: true,
|
||||||
|
CREATE: true,
|
||||||
|
DELETE: true,
|
||||||
|
DROP: true,
|
||||||
|
INSERT: true,
|
||||||
|
SELECT: true,
|
||||||
|
UPDATE: true,
|
||||||
|
UPSERT: true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
rules: {
|
||||||
|
'no-console': 'off',
|
||||||
|
'no-unused-vars': ['warn', { argsIgnorePattern: '^_' }],
|
||||||
|
'require-atomic-updates': 'off',
|
||||||
|
'require-await': 'warn'
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
@@ -2,10 +2,10 @@
|
|||||||
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
||||||
|
|
||||||
# JCI app descriptor contains lower case TITLE
|
# JCI app descriptor contains lower case TITLE
|
||||||
appTitle=Bookshop Authors
|
appTitle=Manage Authors
|
||||||
|
|
||||||
# JCI app descriptor contains lower case DESCRIPTION
|
# JCI app descriptor contains lower case DESCRIPTION
|
||||||
appSubTitle=Bookshop Authors
|
appSubTitle=CAP Sample Application
|
||||||
|
|
||||||
# JCI app descriptor contains lower case DESCRIPTION
|
# JCI app descriptor contains lower case DESCRIPTION
|
||||||
appDescription=Bookshop Authors
|
appDescription=Bookshop Authors
|
||||||
|
|||||||
@@ -35,7 +35,7 @@
|
|||||||
"additionalParameters": "ignored"
|
"additionalParameters": "ignored"
|
||||||
},
|
},
|
||||||
"semanticObject": "Authors",
|
"semanticObject": "Authors",
|
||||||
"action": "display",
|
"action": "manage",
|
||||||
"title": "{{appTitle}}",
|
"title": "{{appTitle}}",
|
||||||
"info": "{{appInfo}}",
|
"info": "{{appInfo}}",
|
||||||
"subTitle": "{{appSubTitle}}",
|
"subTitle": "{{appSubTitle}}",
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
||||||
|
|
||||||
# JCI app descriptor contains lower case TITLE
|
# JCI app descriptor contains lower case TITLE
|
||||||
appTitle=Bookshop Sample
|
appTitle=Manage Books
|
||||||
|
|
||||||
# JCI app descriptor contains lower case DESCRIPTION
|
# JCI app descriptor contains lower case DESCRIPTION
|
||||||
appSubTitle=CAP Sample Application
|
appSubTitle=CAP Sample Application
|
||||||
|
|||||||
@@ -19,6 +19,22 @@
|
|||||||
"id": "ui5template.basicSAPUI5ApplicationProject",
|
"id": "ui5template.basicSAPUI5ApplicationProject",
|
||||||
"-id": "ui5template.smartTemplate",
|
"-id": "ui5template.smartTemplate",
|
||||||
"-version": "1.40.12"
|
"-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": {
|
"sap.ui5": {
|
||||||
|
|||||||
@@ -50,7 +50,7 @@
|
|||||||
"tileType": "sap.ushell.ui.tile.StaticTile",
|
"tileType": "sap.ushell.ui.tile.StaticTile",
|
||||||
"properties": {
|
"properties": {
|
||||||
"title": "Manage Authors",
|
"title": "Manage Authors",
|
||||||
"targetURL": "#Authors-display"
|
"targetURL": "#Authors-manage"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
@@ -99,7 +99,7 @@
|
|||||||
},
|
},
|
||||||
"BrowseAuthors": {
|
"BrowseAuthors": {
|
||||||
"semanticObject": "Authors",
|
"semanticObject": "Authors",
|
||||||
"action": "display",
|
"action": "manage",
|
||||||
"title": "Browse Authors",
|
"title": "Browse Authors",
|
||||||
"signature": {
|
"signature": {
|
||||||
"parameters": {
|
"parameters": {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
||||||
|
|
||||||
# JCI app descriptor contains lower case TITLE
|
# JCI app descriptor contains lower case TITLE
|
||||||
appTitle=Bookshop Sample
|
appTitle=Browse Books
|
||||||
|
|
||||||
# JCI app descriptor contains lower case DESCRIPTION
|
# JCI app descriptor contains lower case DESCRIPTION
|
||||||
appSubTitle=CAP Sample Application
|
appSubTitle=CAP Sample Application
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<!DOCTYPE html>
|
<!doctype html>
|
||||||
<html>
|
<html>
|
||||||
<head>
|
|
||||||
|
|
||||||
|
<head>
|
||||||
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
|
||||||
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
|
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
|
||||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||||
@@ -10,21 +10,22 @@
|
|||||||
<script>
|
<script>
|
||||||
window["sap-ushell-config"] = {
|
window["sap-ushell-config"] = {
|
||||||
defaultRenderer: "fiori2",
|
defaultRenderer: "fiori2",
|
||||||
applications: {}
|
applications: {},
|
||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
<script id="sap-ushell-bootstrap" src="https://ui5.sap.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
||||||
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
|
<script id="sap-ui-bootstrap" src="https://ui5.sap.com/resources/sap-ui-core.js"
|
||||||
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
|
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout" data-sap-ui-compatVersion="edge"
|
||||||
data-sap-ui-compatVersion="edge"
|
data-sap-ui-theme="sap_horizon"></script>
|
||||||
data-sap-ui-theme="sap_horizon"
|
|
||||||
data-sap-ui-frameOptions="allow"
|
|
||||||
></script>
|
|
||||||
<script>
|
<script>
|
||||||
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
|
sap.ui.getCore().attachInit(() =>
|
||||||
|
sap.ushell.Container.createRenderer().placeAt("content")
|
||||||
|
);
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="sapUiBody" id="content"></body>
|
|
||||||
|
<body class="sapUiBody" id="content">
|
||||||
|
</body>
|
||||||
|
|
||||||
</html>
|
</html>
|
||||||
@@ -5,8 +5,7 @@
|
|||||||
"@capire/bookstore": "*",
|
"@capire/bookstore": "*",
|
||||||
"@sap/cds": ">=5",
|
"@sap/cds": ">=5",
|
||||||
"@cap-js-community/odata-v2-adapter": "^1",
|
"@cap-js-community/odata-v2-adapter": "^1",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1"
|
||||||
"passport": ">=0.4.1"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cap-js/sqlite": "^1"
|
"@cap-js/sqlite": "^1"
|
||||||
@@ -51,5 +50,10 @@
|
|||||||
"hana": {
|
"hana": {
|
||||||
"deploy-format": "hdbtable"
|
"deploy-format": "hdbtable"
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
|
"sapux": [
|
||||||
|
"app/admin-authors",
|
||||||
|
"app/admin-books",
|
||||||
|
"app/browse"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
// install OData v2 adapter
|
|
||||||
const cds = require("@sap/cds")
|
|
||||||
const proxy = require('@cap-js-community/odata-v2-adapter')
|
|
||||||
const opts = global.it ? { target:'auto' } : {} // for tests, set 'auto' to detect port dynamically
|
|
||||||
cds.on('bootstrap', app => app.use(proxy(opts))) // install proxy
|
|
||||||
// cds.log('cov2ap','silent') // suppress anoying log outpout, e.g. for `npm run mocha -- --reporter nyan`
|
|
||||||
|
|
||||||
module.exports = require('@capire/bookstore/server.js')
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
import type { Request } from "@sap/cds/apis/services"
|
import { Request } from "@sap/cds"
|
||||||
|
|
||||||
module.exports = class say {
|
module.exports = class say {
|
||||||
hello(req: Request) {
|
hello(req: Request) {
|
||||||
|
|||||||
@@ -1,13 +1,10 @@
|
|||||||
const cds = require ('@sap/cds')
|
const cds = require ('@sap/cds')
|
||||||
describe('Hello world!', () => {
|
describe('Hello world!', () => {
|
||||||
|
|
||||||
beforeAll (()=> process.env.CDS_TYPESCRIPT = true)
|
|
||||||
afterAll (()=> delete process.env.CDS_TYPESCRIPT)
|
|
||||||
const {GET} = cds.test.in(__dirname,'../srv').run('serve', 'world.cds')
|
const {GET} = cds.test.in(__dirname,'../srv').run('serve', 'world.cds')
|
||||||
|
|
||||||
it('should say hello with class impl', async () => {
|
it('should say hello with class impl', async () => {
|
||||||
const {data} = await GET`/say/hello(to='world')`
|
const {data} = await GET`/say/hello(to='world')`
|
||||||
expect(data.value).toMatch(/Hello world.*typescript.*/i)
|
expect(data.value).to.match(/Hello world.*typescript.*/i)
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require ('@sap/cds/lib')
|
const cds = require ('@sap/cds')
|
||||||
const LOG = cds.log('cds.log')
|
const LOG = cds.log('cds.log')
|
||||||
|
|
||||||
module.exports = class LogService extends cds.Service {
|
module.exports = class LogService extends cds.Service {
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
||||||
|
|
||||||
# JCI app descriptor contains lower case TITLE
|
# JCI app descriptor contains lower case TITLE
|
||||||
appTitle=Bookshop Sample
|
appTitle=Manage Orders
|
||||||
|
|
||||||
# JCI app descriptor contains lower case DESCRIPTION
|
# JCI app descriptor contains lower case DESCRIPTION
|
||||||
appSubTitle=CAP Sample Application
|
appSubTitle=CAP Sample Application
|
||||||
|
|||||||
1161
package-lock.json
generated
1161
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
112
package.json
112
package.json
@@ -1,56 +1,58 @@
|
|||||||
{
|
{
|
||||||
"name": "@capire/samples",
|
"name": "@capire/samples",
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"description": "A monorepo with several samples for CAP.",
|
"description": "A monorepo with several samples for CAP.",
|
||||||
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
|
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
|
||||||
"author": "daniel.hutzel@sap.com",
|
"author": "daniel.hutzel@sap.com",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sap/cds": ">=7"
|
"@sap/cds": ">=7"
|
||||||
},
|
},
|
||||||
"workspaces": [
|
"workspaces": [
|
||||||
"./bookshop",
|
"./bookshop",
|
||||||
"./bookstore",
|
"./bookstore",
|
||||||
"./common",
|
"./common",
|
||||||
"./data-viewer",
|
"./data-viewer",
|
||||||
"./fiori",
|
"./fiori",
|
||||||
"./hello",
|
"./hello",
|
||||||
"./media",
|
"./media",
|
||||||
"./orders",
|
"./orders",
|
||||||
"./loggers",
|
"./loggers",
|
||||||
"./reviews"
|
"./reviews"
|
||||||
],
|
],
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@cap-js/sqlite": "^1",
|
"@cap-js/sqlite": "^1",
|
||||||
"@sap/eslint-plugin-cds": "^2.6.1",
|
"@sap/eslint-plugin-cds": "^3",
|
||||||
"axios": "^1",
|
"axios": "^1",
|
||||||
"chai": "^4.3.4",
|
"chai": "^4.3.4",
|
||||||
"chai-as-promised": "^7.1.1",
|
"chai-as-promised": "^7.1.1",
|
||||||
"chai-subset": "^1.6.0",
|
"chai-subset": "^1.6.0",
|
||||||
"semver": "^7"
|
"eslint": "^9",
|
||||||
},
|
"semver": "^7"
|
||||||
"scripts": {
|
},
|
||||||
"cleanup": "rm -rf node_modules && rm -rf */node_modules && rm -rf */*/node_modules",
|
"scripts": {
|
||||||
"bookshop": "cds watch bookshop",
|
"cleanup": "rm -rf node_modules && rm -rf */node_modules && rm -rf */*/node_modules",
|
||||||
"fiori": "cds watch fiori",
|
"bookshop": "cds watch bookshop",
|
||||||
"hello": "cds watch hello",
|
"fiori": "cds watch fiori",
|
||||||
"media": "cds watch media",
|
"hello": "cds watch hello",
|
||||||
"mocha": "CDS_TEST_SILENT=y npx mocha",
|
"media": "cds watch media",
|
||||||
"jest": "npx jest --silent",
|
"mocha": "CDS_TEST_SILENT=y npx mocha",
|
||||||
"start": "cds watch fiori",
|
"jest": "npx jest --silent",
|
||||||
"test": "npm run jest -- --silent",
|
"start": "cds watch fiori",
|
||||||
"test:hello": "cd hello && npm test"
|
"test": "npm run jest -- --silent",
|
||||||
},
|
"test:hello": "cd hello && npm test",
|
||||||
"jest": {
|
"lint": "eslint ."
|
||||||
"testTimeout": 20000,
|
},
|
||||||
"testMatch": [
|
"jest": {
|
||||||
"**/*.test.js"
|
"testTimeout": 20000,
|
||||||
]
|
"testMatch": [
|
||||||
},
|
"**/*.test.js"
|
||||||
"mocha": {
|
]
|
||||||
"recursive": true,
|
},
|
||||||
"parallel": true,
|
"mocha": {
|
||||||
"timeout": 6666
|
"recursive": true,
|
||||||
},
|
"parallel": true,
|
||||||
"license": "SEE LICENSE IN LICENSE",
|
"timeout": 6666
|
||||||
"private": true
|
},
|
||||||
}
|
"license": "SEE LICENSE IN LICENSE",
|
||||||
|
"private": true
|
||||||
|
}
|
||||||
@@ -1,7 +1,8 @@
|
|||||||
|
const cds = require('@sap/cds')
|
||||||
|
const { expect } = cds.test
|
||||||
|
|
||||||
describe('cds.ql → cqn', () => {
|
describe('cds.ql → cqn', () => {
|
||||||
|
|
||||||
const cds = require('@sap/cds/lib')
|
|
||||||
const { expect } = cds.test
|
|
||||||
const Foo = { name: 'Foo' }
|
const Foo = { name: 'Foo' }
|
||||||
const Books = { name: 'capire.bookshop.Books' }
|
const Books = { name: 'capire.bookshop.Books' }
|
||||||
|
|
||||||
@@ -676,7 +677,7 @@ describe('cds.ql → cqn', () => {
|
|||||||
.to.eql(INSERT.into(Foo).entries(...entries))
|
.to.eql(INSERT.into(Foo).entries(...entries))
|
||||||
.to.eql(INSERT.into(Foo).entries(entries))
|
.to.eql(INSERT.into(Foo).entries(entries))
|
||||||
.to.eql({
|
.to.eql({
|
||||||
INSERT: { into: 'Foo', entries },
|
INSERT: { into: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] }, entries },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -692,7 +693,7 @@ describe('cds.ql → cqn', () => {
|
|||||||
.to.eql(INSERT.into(Foo).columns('a', 'b').rows([1, 2], [3, 4]))
|
.to.eql(INSERT.into(Foo).columns('a', 'b').rows([1, 2], [3, 4]))
|
||||||
.to.eql({
|
.to.eql({
|
||||||
INSERT: {
|
INSERT: {
|
||||||
into: 'Foo',
|
into: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] },
|
||||||
columns: ['a', 'b'],
|
columns: ['a', 'b'],
|
||||||
rows: [
|
rows: [
|
||||||
[1, 2],
|
[1, 2],
|
||||||
@@ -706,7 +707,7 @@ describe('cds.ql → cqn', () => {
|
|||||||
expect(INSERT.into(Foo).columns('a', 'b').values([1, 2]))
|
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(Foo).columns('a', 'b').values(1, 2))
|
||||||
.to.eql({
|
.to.eql({
|
||||||
INSERT: { into: 'Foo', columns: ['a', 'b'], values: [1, 2] },
|
INSERT: { into: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] }, columns: ['a', 'b'], values: [1, 2] },
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -721,7 +722,7 @@ describe('cds.ql → cqn', () => {
|
|||||||
test('entity (..., <key>)', () => {
|
test('entity (..., <key>)', () => {
|
||||||
const cqnWhere = {
|
const cqnWhere = {
|
||||||
UPDATE: {
|
UPDATE: {
|
||||||
entity: 'capire.bookshop.Books',
|
entity: cds.env.ql.quirks_mode ? 'capire.bookshop.Books' : { ref: ['capire.bookshop.Books'] },
|
||||||
where: [{ ref: ['ID'] }, '=', { val: 4711 }],
|
where: [{ ref: ['ID'] }, '=', { val: 4711 }],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
@@ -765,7 +766,7 @@ describe('cds.ql → cqn', () => {
|
|||||||
.to.eql(UPDATE(Foo).with({ foo: 11, bar: { '-=': 22 } }))
|
.to.eql(UPDATE(Foo).with({ foo: 11, bar: { '-=': 22 } }))
|
||||||
.to.eql({
|
.to.eql({
|
||||||
UPDATE: {
|
UPDATE: {
|
||||||
entity: 'Foo',
|
entity: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] },
|
||||||
data: { foo: 11 },
|
data: { foo: 11 },
|
||||||
with: {
|
with: {
|
||||||
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
|
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
|
||||||
@@ -776,7 +777,7 @@ describe('cds.ql → cqn', () => {
|
|||||||
// some more
|
// some more
|
||||||
expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
|
expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
|
||||||
UPDATE: {
|
UPDATE: {
|
||||||
entity: 'Foo',
|
entity: cds.env.ql.quirks_mode ? 'Foo' : { ref: ['Foo'] },
|
||||||
data: {
|
data: {
|
||||||
car: "foo's bar, car",
|
car: "foo's bar, car",
|
||||||
},
|
},
|
||||||
@@ -796,7 +797,7 @@ describe('cds.ql → cqn', () => {
|
|||||||
test('from (..., <key>)', () => {
|
test('from (..., <key>)', () => {
|
||||||
const cqnWhere = {
|
const cqnWhere = {
|
||||||
DELETE: {
|
DELETE: {
|
||||||
from: 'capire.bookshop.Books',
|
from: cds.env.ql.quirks_mode ? 'capire.bookshop.Books' : { ref: ['capire.bookshop.Books'] },
|
||||||
where: [{ ref: ['ID'] }, '=', { val: 4711 }],
|
where: [{ ref: ['ID'] }, '=', { val: 4711 }],
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
describe('cap/samples - Consuming Services locally', () => {
|
describe('cap/samples - Consuming Services locally', () => {
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
describe('cap/samples - Custom Handlers', () => {
|
describe('cap/samples - Custom Handlers', () => {
|
||||||
|
|
||||||
@@ -8,9 +8,10 @@ describe('cap/samples - Custom Handlers', () => {
|
|||||||
})
|
})
|
||||||
|
|
||||||
it('should reject out-of-stock orders', async () => {
|
it('should reject out-of-stock orders', async () => {
|
||||||
await POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`
|
await expect(POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`).to.be.fulfilled
|
||||||
await POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`
|
await expect(POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`).to.be.fulfilled
|
||||||
await expect(POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`).to.be.rejectedWith(/409 - 5 exceeds stock for book #201/)
|
await expect(POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`).to.be.rejectedWith(
|
||||||
|
/409 - 5 exceeds stock for book #201/)
|
||||||
const { data } = await GET`/admin/Books/201/stock/$value`
|
const { data } = await GET`/admin/Books/201/stock/$value`
|
||||||
expect(data).to.equal(2)
|
expect(data).to.equal(2)
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,8 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
// Quick hack: suppress deprecation warnings w/ Node22 caused by http-proxy (used by OData v2 proxy)
|
||||||
|
// See also: https://github.com/http-party/node-http-proxy/pull/1666
|
||||||
|
require('util')._extend = Object.assign
|
||||||
|
|
||||||
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
describe('cap/samples - Fiori APIs - v2', function() {
|
describe('cap/samples - Fiori APIs - v2', function() {
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
describe('cap/samples - Hello world!', () => {
|
describe('cap/samples - Hello world!', () => {
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
const cds = require('@sap/cds')
|
||||||
const { expect } = cds.test.in(__dirname,'..')
|
const { expect } = cds.test.in(__dirname,'..')
|
||||||
|
|
||||||
describe('cap/samples - Hierarchical Data', ()=>{
|
describe('cap/samples - Hierarchical Data', ()=>{
|
||||||
@@ -33,83 +33,89 @@ describe('cap/samples - Hierarchical Data', ()=>{
|
|||||||
]}
|
]}
|
||||||
))
|
))
|
||||||
|
|
||||||
it ('supports nested reads', async()=>{
|
it ('should generate correct queries for expands', ()=>{
|
||||||
if (require('semver').gte(cds.version, '5.9.0')) {
|
let q = SELECT.from (Cats, c => { c.ID, c.name, c.children (c => c.name) })
|
||||||
expect (await
|
expect (q) .to.eql ({
|
||||||
SELECT.one.from (Cats, c=>{
|
SELECT: {
|
||||||
c.ID, c.name.as('parent'), c.children (c=>{
|
from: { ref:[ "Categories" ] },
|
||||||
c.name.as('child')
|
columns: [
|
||||||
})
|
{ ref: [ "ID" ] },
|
||||||
}) .where ({name:'Cat'})
|
{ ref: [ "name" ] },
|
||||||
) .to.eql (
|
{ ref: [ "children" ], expand: [ {ref:['name']} ] },
|
||||||
{ ID:101, parent:'Cat', children:[
|
]
|
||||||
{ child:'Kitty' },
|
}
|
||||||
{ child:'Catwoman' },
|
})
|
||||||
]}
|
if (q.forSQL) expect (q.forSQL()) .to.eql ({
|
||||||
)
|
SELECT: {
|
||||||
return
|
from: { ref:[ "Categories" ], as: "Categories" },
|
||||||
}
|
columns: [
|
||||||
expect (await
|
{ ref: [ "Categories", "ID" ] },
|
||||||
SELECT.one.from (Cats, c=>{
|
{ ref: [ "Categories", "name" ] },
|
||||||
c.ID, c.name.as('parent'), c.children (c=>{
|
{ as: "children", SELECT: { expand: true,
|
||||||
c.name.as('child')
|
one: false,
|
||||||
})
|
columns: [{ ref: [ "children", "name" ]}],
|
||||||
}) .where ({name:'Cat'})
|
from: { ref:["Categories"], as: "children" },
|
||||||
) .to.eql (
|
where: [
|
||||||
{ ID:101, parent:'Cat', children:[
|
{ref:[ "Categories", "ID" ]}, "=", {ref:[ "children", "parent_ID" ]}
|
||||||
{ ID:102, child:'Kitty' },
|
],
|
||||||
{ ID:106, child:'Catwoman' },
|
}},
|
||||||
]}
|
],
|
||||||
|
}
|
||||||
|
})
|
||||||
|
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 deeply nested reads', async()=>{
|
it ('supports nested reads', ()=> expect (
|
||||||
if (require('semver').gte(cds.version, '5.9.0')) {
|
SELECT.one.from (Cats, c=>{
|
||||||
expect (await SELECT.one.from (Cats, c=>{
|
c.ID, c.name.as('parent'), c.children (c=>{
|
||||||
c.ID, c.name, c.children (
|
c.name.as('child')
|
||||||
c => { c.name },
|
})
|
||||||
{levels:3}
|
}) .where ({name:'Cat'})
|
||||||
)
|
) .to.eventually.eql (
|
||||||
}) .where ({name:'Cat'})
|
{ ID:101, parent:'Cat', children:[
|
||||||
) .to.eql (
|
{ child:'Kitty' },
|
||||||
{ ID:101, name:'Cat', children:[
|
{ child:'Catwoman' },
|
||||||
{ name:'Kitty', children:[
|
]}
|
||||||
{ name:'Kitty Cat', children:[
|
))
|
||||||
{ name:'Aristocat' }, ]}, // level 3
|
|
||||||
{ name:'Kitty Bat', children:[] }, ]},
|
it ('supports deeply nested reads', ()=> expect (
|
||||||
{ name:'Catwoman', children:[
|
SELECT.one.from (Cats, c=>{
|
||||||
{ name:'Catalina', children:[] } ]},
|
|
||||||
]}
|
|
||||||
)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
expect (await SELECT.one.from (Cats, c=>{
|
|
||||||
c.ID, c.name, c.children (
|
c.ID, c.name, c.children (
|
||||||
c => { c.name },
|
c => { c.name },
|
||||||
{levels:3}
|
{levels:3}
|
||||||
)
|
)
|
||||||
}) .where ({name:'Cat'})
|
}) .where ({name:'Cat'})
|
||||||
) .to.eql (
|
) .to.eventually.eql (
|
||||||
{ ID:101, name:'Cat', children:[
|
{ ID:101, name:'Cat', children:[
|
||||||
{ ID:102, name:'Kitty', children:[
|
{ name:'Kitty', children:[
|
||||||
{ ID:103, name:'Kitty Cat', children:[
|
{ name:'Kitty Cat', children:[
|
||||||
{ ID:104, name:'Aristocat' }, ]}, // level 3
|
{ name:'Aristocat' }, ]}, // level 3
|
||||||
{ ID:105, name:'Kitty Bat', children:[] }, ]},
|
{ name:'Kitty Bat', children:[] }, ]},
|
||||||
{ ID:106, name:'Catwoman', children:[
|
{ name:'Catwoman', children:[
|
||||||
{ ID:107, name:'Catalina', children:[] } ]},
|
{ name:'Catalina', children:[] } ]},
|
||||||
]}
|
]}
|
||||||
)
|
))
|
||||||
})
|
|
||||||
|
|
||||||
it ('supports cascaded deletes', async()=>{
|
it ('supports cascaded deletes', async()=>{
|
||||||
const affectedRows = await DELETE.from (Cats) .where ({ID:[102,106]})
|
const affectedRows = await DELETE.from (Cats) .where ({ID:[102,106]})
|
||||||
expect (affectedRows) .to.be.greaterThan (0)
|
expect (affectedRows) .to.be.greaterThan (0)
|
||||||
const expected = [
|
await expect (SELECT`ID,name`.from(Cats) ).to.eventually.eql ([
|
||||||
{ ID:100, name:'Some Cats...' },
|
{ ID:100, name:'Some Cats...' },
|
||||||
{ ID:101, name:'Cat' },
|
{ ID:101, name:'Cat' },
|
||||||
{ ID:108, name:'Catweazle' }
|
{ ID:108, name:'Catweazle' }
|
||||||
]
|
])
|
||||||
expect ( await SELECT`ID,name`.from(Cats) ).to.eql (expected)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
describe('cap/samples - Localized Data', () => {
|
describe('cap/samples - Localized Data', () => {
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
describe('cap/samples - Messaging', ()=>{
|
describe('cap/samples - Messaging', ()=>{
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
const cds = require('@sap/cds/lib')
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
describe('cap/samples - Bookshop APIs', () => {
|
describe('cap/samples - Bookshop APIs', () => {
|
||||||
const { GET, expect, axios } = cds.test ('@capire/bookshop')
|
const { GET, expect, axios } = cds.test ('@capire/bookshop')
|
||||||
@@ -8,9 +8,10 @@ describe('cap/samples - Bookshop APIs', () => {
|
|||||||
const { headers, status, data } = await GET `/browse/$metadata`
|
const { headers, status, data } = await GET `/browse/$metadata`
|
||||||
expect(status).to.equal(200)
|
expect(status).to.equal(200)
|
||||||
expect(headers).to.contain({
|
expect(headers).to.contain({
|
||||||
'content-type': 'application/xml',
|
// 'content-type': 'application/xml', //> fails with 'application/xml;charset=utf-8', which is set by express
|
||||||
'odata-version': '4.0',
|
'odata-version': '4.0',
|
||||||
})
|
})
|
||||||
|
expect(headers['content-type']).to.match(/application\/xml/)
|
||||||
expect(data).to.contain('<EntitySet Name="Books" EntityType="CatalogService.Books">')
|
expect(data).to.contain('<EntitySet Name="Books" EntityType="CatalogService.Books">')
|
||||||
expect(data).to.contain('<Annotation Term="Common.Label" String="Currency"/>')
|
expect(data).to.contain('<Annotation Term="Common.Label" String="Currency"/>')
|
||||||
})
|
})
|
||||||
@@ -28,63 +29,66 @@ describe('cap/samples - Bookshop APIs', () => {
|
|||||||
])
|
])
|
||||||
})
|
})
|
||||||
|
|
||||||
it('supports $search in multiple fields', async () => {
|
describe('query options...', () => {
|
||||||
const { data } = await GET `/browse/Books ${{
|
|
||||||
params: { $search: 'Po', $select: `title,author` },
|
|
||||||
}}`
|
|
||||||
expect(data.value).to.containSubset([
|
|
||||||
{ ID: 201, title: 'Wuthering Heights', author: 'Emily Brontë' },
|
|
||||||
{ ID: 207, title: 'Jane Eyre', author: 'Charlotte Brontë' },
|
|
||||||
{ ID: 251, title: 'The Raven', author: 'Edgar Allen Poe' },
|
|
||||||
{ ID: 252, title: 'Eleonora', author: 'Edgar Allen Poe' },
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('supports $select', async () => {
|
it('supports $search in multiple fields', async () => {
|
||||||
const { data } = await GET(`/browse/Books`, {
|
const { data } = await GET `/browse/Books ${{
|
||||||
params: { $select: `ID,title` },
|
params: { $search: 'Po', $select: `title,author` },
|
||||||
|
}}`
|
||||||
|
expect(data.value).to.containSubset([
|
||||||
|
{ ID: 201, title: 'Wuthering Heights', author: 'Emily Brontë' },
|
||||||
|
{ ID: 207, title: 'Jane Eyre', author: 'Charlotte Brontë' },
|
||||||
|
{ ID: 251, title: 'The Raven', author: 'Edgar Allen Poe' },
|
||||||
|
{ ID: 252, title: 'Eleonora', author: 'Edgar Allen Poe' },
|
||||||
|
])
|
||||||
})
|
})
|
||||||
expect(data.value).to.containSubset([
|
|
||||||
{ ID: 201, title: 'Wuthering Heights' },
|
|
||||||
{ ID: 207, title: 'Jane Eyre' },
|
|
||||||
{ ID: 251, title: 'The Raven' },
|
|
||||||
{ ID: 252, title: 'Eleonora' },
|
|
||||||
{ ID: 271, title: 'Catweazle' },
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('supports $expand', async () => {
|
it('supports $select', async () => {
|
||||||
const { data } = await GET(`/admin/Authors`, {
|
const { data } = await GET(`/browse/Books`, {
|
||||||
params: {
|
params: { $select: `ID,title` },
|
||||||
$select: `name`,
|
})
|
||||||
$expand: `books($select=title)`,
|
expect(data.value).to.containSubset([
|
||||||
},
|
{ ID: 201, title: 'Wuthering Heights' },
|
||||||
|
{ ID: 207, title: 'Jane Eyre' },
|
||||||
|
{ ID: 251, title: 'The Raven' },
|
||||||
|
{ ID: 252, title: 'Eleonora' },
|
||||||
|
{ ID: 271, title: 'Catweazle' },
|
||||||
|
])
|
||||||
})
|
})
|
||||||
expect(data.value).to.containSubset([
|
|
||||||
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
|
|
||||||
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
|
|
||||||
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
|
|
||||||
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
|
|
||||||
])
|
|
||||||
})
|
|
||||||
|
|
||||||
it('supports $value requests', async () => {
|
it('supports $expand', async () => {
|
||||||
const { data } = await GET`/admin/Books/201/stock/$value`
|
const { data } = await GET(`/admin/Authors`, {
|
||||||
expect(data).to.equal(12)
|
params: {
|
||||||
})
|
$select: `name`,
|
||||||
|
$expand: `books($select=title)`,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
expect(data.value).to.containSubset([
|
||||||
|
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
|
||||||
|
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
|
||||||
|
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
|
||||||
|
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
|
||||||
|
])
|
||||||
|
})
|
||||||
|
|
||||||
it('supports $top/$skip paging', async () => {
|
it('supports $value requests', async () => {
|
||||||
const { data: p1 } = await GET`/browse/Books?$select=title&$top=3`
|
const { data } = await GET`/admin/Books/201/stock/$value`
|
||||||
expect(p1.value).to.containSubset([
|
expect(data).to.equal(12)
|
||||||
{ ID: 201, title: 'Wuthering Heights' },
|
})
|
||||||
{ ID: 207, title: 'Jane Eyre' },
|
|
||||||
{ ID: 251, title: 'The Raven' },
|
it('supports $top/$skip paging', async () => {
|
||||||
])
|
const { data: p1 } = await GET`/browse/Books?$select=title&$top=3`
|
||||||
const { data: p2 } = await GET`/browse/Books?$select=title&$skip=3`
|
expect(p1.value).to.containSubset([
|
||||||
expect(p2.value).to.containSubset([
|
{ ID: 201, title: 'Wuthering Heights' },
|
||||||
{ ID: 252, title: 'Eleonora' },
|
{ ID: 207, title: 'Jane Eyre' },
|
||||||
{ ID: 271, title: 'Catweazle' },
|
{ ID: 251, title: 'The Raven' },
|
||||||
])
|
])
|
||||||
|
const { data: p2 } = await GET`/browse/Books?$select=title&$skip=3`
|
||||||
|
expect(p2.value).to.containSubset([
|
||||||
|
{ ID: 252, title: 'Eleonora' },
|
||||||
|
{ ID: 271, title: 'Catweazle' },
|
||||||
|
])
|
||||||
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
it('serves user info', async () => {
|
it('serves user info', async () => {
|
||||||
|
|||||||
Reference in New Issue
Block a user