Compare commits

..

179 Commits

Author SHA1 Message Date
Daniel
67643b8d95 merged from master 2021-07-16 06:20:48 +02:00
Christian Georgi
cd3aad59e1 Fix UI warnings
These were caused by the UI check not being aware of the parallel
`common.cds` file.
2021-07-14 10:11:21 +02:00
Christian Georgi
bcbf65f95e Draft for localized: expose Books.texts well
`Books.texts` must be explicitly exposed in order to be drafted
2021-07-13 16:23:44 +02:00
Christian Georgi
1189374860 Update to cds 5.3.1 2021-07-13 15:16:27 +02:00
Christian Georgi
9f2eb8fa83 Merge pull request #248 from SAP-samples/ts-node_support
Ts node support + tests
2021-07-13 14:32:49 +02:00
Christian Georgi
7b18e15e5b Jest config in package.json again 2021-07-13 13:55:05 +02:00
Christian Georgi
00bfc1507e Isolate all TS stuff in hello package 2021-07-13 13:24:29 +02:00
Christian Georgi
47a58e7393 Moved typescript dependencies to hello package
This allows TS specifics to stay local in just one package
2021-07-13 13:09:54 +02:00
Christian Georgi
c04b972d27 Cosmetics 2021-07-13 13:01:50 +02:00
Christian Georgi
85b9b9d3a3 Clear process variable after test 2021-07-13 13:01:50 +02:00
Tobias Steckenborn
3655eef971 fix: 🚑 Add testEnvironment again 2021-07-13 13:01:50 +02:00
Tobias Steckenborn
87dd356204 refactor: 🔥 Add ts-jest preset, remove manual transformation 2021-07-13 13:01:50 +02:00
Christian Georgi
a89ec34f7c Move jest config out of the way 2021-07-13 13:01:50 +02:00
Christian Georgi
444dcacb14 Add typescript package 2021-07-13 13:01:50 +02:00
Christian Georgi
2011a1ca7a Use transform option to make ts test work 2021-07-13 13:01:50 +02:00
Christian Georgi
7e0c36ede5 use cds-ts binary for starting cds with ts-node runtime 2021-07-13 13:01:50 +02:00
Christian Georgi
e8d08d039e Add example for starting cds with ts-node 2021-07-13 13:01:46 +02:00
Christian Georgi
5e3258913e Merge pull request #247 from SAP-samples/openapi
OpenAPI and Swagger UI
2021-07-12 14:04:30 +02:00
Christian Georgi
44e14926e4 Use latest Jest 2021-07-12 14:02:42 +02:00
Christian Georgi
592e945c28 Update package-lock 2021-07-12 14:02:21 +02:00
Christian Georgi
5d5c787365 Merge remote-tracking branch 'origin/master' into openapi 2021-07-05 17:59:00 +02:00
Christian Georgi
bb79def1d9 Merge pull request #239 from SAP-samples/using-package-lock.json
Using package-lock.json
2021-07-05 17:58:25 +02:00
Christian Georgi
bd4a38bf54 Update package-lock 2021-07-05 17:55:10 +02:00
Christian Georgi
abf18d74db Use npm ci instead of npm install 2021-07-05 17:55:10 +02:00
Christian Georgi
f144354229 Merge branch 'origin/master' into using-package-lock.json 2021-07-05 17:50:39 +02:00
Christian Georgi
97670bbc84 Swagger UI with cds-swagger-ui-express
Moved from `bookshop` to `fiori`, as that one already has a `server.js`
2021-07-05 17:46:24 +02:00
Christian Georgi
82d6f54337 Merge remote-tracking branch 'origin/master' into openapi 2021-07-05 17:39:06 +02:00
D065023
8ba8381ca7 dash to dot 2021-06-18 08:29:33 +02:00
D065023
e7fbcc1ac1 mv annotation to better place 2021-06-16 17:04:40 +02:00
D065023
d977c21483 a bit nicer 2021-06-16 16:46:53 +02:00
D065023
2e10b1ac4d after handler 2021-06-16 16:44:58 +02:00
D065023
cabcd404cf dot notation in line items 2021-06-16 16:41:39 +02:00
D065023
5141df8e0f better value list 2021-06-16 16:40:18 +02:00
Daniel
785ecbda8f cleaned up code 2021-06-16 12:30:56 +02:00
Daniel
2b9ceba62f illustrations 2021-06-16 12:30:30 +02:00
D065023
c6e2fac860 mv data 2021-06-16 12:02:00 +02:00
Uwe Klinger
f2877db34f Disable search field in value help 2021-06-16 11:02:00 +02:00
Uwe Klinger
d2bd4c5bb1 Fix replication error due to removed entity namespace 2021-06-16 07:17:09 +02:00
Uwe Klinger
732b6b081c Add missing profile 2021-06-16 07:16:05 +02:00
D065023
db595a9635 better logs 2021-06-15 15:02:13 +02:00
Daniel
4a8b85f227 Added illustrations to ffollow up on workarounds 2021-06-15 12:58:09 +02:00
Daniel
0cb7349e62 Cosmetics -> for docs 2021-06-15 12:57:42 +02:00
D065023
ba3eeff58b odata-v2 -> odata otherwise mock won't work 2021-06-14 12:44:21 +02:00
D065023
86e6983bed use enterprise-messaging-shared 2021-06-11 14:46:51 +02:00
D065023
9bbc431448 odata-v2 (needs PR) and rm file-based 2021-06-11 14:46:09 +02:00
Uwe Klinger
097b74db03 Adjustments 2021-06-11 10:40:17 +02:00
Uwe Klinger
870f8d063d Add Fiori UI for Book administration
* Copyied from fiori/app/admin
* Added Supplier + Value Help + Texts
* Fixed bug in replication handler that caused to return wrong data
2021-06-11 08:15:27 +02:00
D065023
4a0db3e259 rm sleep 2021-06-10 15:59:00 +02:00
D065023
5e76c7f52e console.log 2021-06-10 15:58:25 +02:00
D065023
31c110e9e2 added profiles 2021-06-10 15:55:26 +02:00
Heiko Witteborg
a4baee6e3b Merge pull request #243 from MikhailGoncharov/not-simple-args-fluent-api-v2
Sync with cds-runtime
2021-06-10 08:37:14 +02:00
Mikhail Goncharov
95b01e7d7a Do not trim quotes 2021-06-09 13:39:12 +02:00
Mikhail Goncharov
c2437fc419 Handle cds versions 2021-06-09 12:49:39 +02:00
Mikhail Goncharov
9381459458 Sync with cds-runtime 2021-06-09 10:43:10 +02:00
Daniel
8d67c0bf62 Merged 2021-06-09 10:22:30 +02:00
Uwe Klinger
21359a7ad3 Use type reference 2021-06-09 08:41:00 +02:00
Uwe Klinger
649b9c8ca8 Test script added as requests 2021-06-09 08:17:59 +02:00
Uwe Klinger
11c54b29d0 Fix minor diffs 2021-06-09 07:51:51 +02:00
Uwe Klinger
80302a0a3d Remove notes test 2021-06-09 07:50:30 +02:00
Daniel
c8c10b7c8a cds.events test 2021-06-09 07:49:55 +02:00
Uwe Klinger
d9d7203e49 Fix unintended diffs 2021-06-09 07:47:28 +02:00
Uwe Klinger
4a8379e953 Remove notes app 2021-06-09 07:47:16 +02:00
Uwe Klinger
dd28bd04f6 Minor cleanups 2021-06-09 07:44:18 +02:00
Daniel Hutzel
28a7c16895 Merge branch 'master' into using-package-lock.json 2021-06-09 07:38:42 +02:00
Uwe Klinger
5397df5e81 Remove unwanted file 2021-06-09 07:34:38 +02:00
Daniel
f57ae79e37 running suppliers 2021-06-09 07:33:48 +02:00
Daniel
6412df75dd cosmetic changes 2021-06-09 07:30:07 +02:00
Daniel
9277aa12da removed obsolete cds.emit monky patch 2021-06-09 07:25:54 +02:00
Daniel
d41a9e10ab Simplifying samples 2021-06-09 07:25:36 +02:00
Daniel
0b2182afcb Fixed: books title was missing 2021-06-09 07:24:35 +02:00
Daniel
6b74c23aa0 Async 'served' event 2021-06-09 07:22:14 +02:00
Dr. David A. Kunz
c854717359 Update mashup.cds 2021-06-09 07:19:34 +02:00
Uwe Klinger
9dfe62c5c7 Fixed mocking for last test 2021-06-09 07:10:28 +02:00
Uwe Klinger
5b4210bb38 Use "db" service to avoid double call of service handlers 2021-06-09 07:10:28 +02:00
Uwe Klinger
48ee934b00 Only profile credentials 2021-06-09 07:10:28 +02:00
Uwe Klinger
254bb40e38 Remove monkey patch and profile destination 2021-06-09 07:10:28 +02:00
Uwe Klinger
fb5d00bbe0 Improved generic handler remote services 2021-06-09 07:10:28 +02:00
Uwe Klinger
7ae992c5bb Implement generic expand and navigation feature 2021-06-09 07:10:28 +02:00
Uwe Klinger
016587094f Add suppliers notes app 2021-06-09 07:10:28 +02:00
Uwe Klinger
d04cb801c4 Updated 2021-06-09 07:10:28 +02:00
Uwe Klinger
902afd8a76 Change event to reflect the real event definition 2021-06-09 07:10:28 +02:00
Uwe Klinger
c4bee1f09a Fixes and improvements 2021-06-09 07:10:28 +02:00
Uwe Klinger
278258c436 Implement eventing 2021-06-09 07:10:28 +02:00
Uwe Klinger
8601bd8a46 Suppliers moved to bookshop namespace 2021-06-09 07:10:28 +02:00
Uwe Klinger
98113c46fd Own transaction when raising an event 2021-06-09 07:10:28 +02:00
Uwe Klinger
eb4bc703dd Improvements for Supplier replication 2021-06-09 07:10:28 +02:00
Uwe Klinger
de806a1e38 Add example with supplier_ID 2021-06-09 07:10:28 +02:00
Uwe Klinger
2999c8df83 Add todo 2021-06-09 07:10:28 +02:00
Daniel
87dbb4552a . 2021-06-09 07:10:27 +02:00
Daniel
0bdf4bb93e . 2021-06-09 07:10:27 +02:00
Daniel
a4810c3a7b . 2021-06-09 07:10:27 +02:00
Daniel
c899843319 Using event : projection on Reviews 2021-06-09 07:10:27 +02:00
Daniel
43e5f6faef Adding fix-antlr script 2021-06-09 07:10:27 +02:00
Daniel
1b5cc62d1c some corrections and optimizations 2021-06-09 07:10:27 +02:00
Iwona Hahn
4b48d68ba6 cosmetics 2021-06-09 07:10:27 +02:00
Iwona Hahn
d21a0e08ca cosmetics 2021-06-09 07:10:27 +02:00
Iwona Hahn
50fd83fd19 cosmetics 2021-06-09 07:10:27 +02:00
Daniel Hutzel
3f20f4b9a2 Delete settings.json 2021-06-09 07:10:27 +02:00
Daniel Hutzel
5031779be8 Delete launch.json 2021-06-09 07:10:27 +02:00
Daniel Hutzel
ad385cbc6c Delete tasks.json 2021-06-09 07:10:27 +02:00
Daniel Hutzel
2650db4ee9 Delete extensions.json 2021-06-09 07:10:27 +02:00
Daniel Hutzel
e04e9d231d Delete .gitignore 2021-06-09 07:10:27 +02:00
Christian Georgi
476724792e Make the CatalogService usage more obvious 2021-06-09 07:10:27 +02:00
Uwe Klinger
5ae31f7c67 Adding suppliers showing integration with S/4 2021-06-09 07:10:25 +02:00
Daniel
edbca44a43 removed obsolete cds.emit monky patch 2021-06-08 18:23:52 +02:00
Daniel
02f19e295d Simplifying samples 2021-06-08 18:20:44 +02:00
Christian Georgi
c1141e6c87 Remove trailing comma 2021-06-08 16:19:34 +02:00
Daniel
05cdb67329 Fixed: books title was missing 2021-06-08 16:01:45 +02:00
D065023
4877386e86 A bit more fault tolerant
(e.g. when only one service is mocked)
2021-06-08 14:48:12 +02:00
Daniel
4bd4446975 Async 'served' event 2021-06-08 13:18:39 +02:00
Daniel
a54b0124ea Using file-based-messaging in fiori 2021-06-08 13:18:21 +02:00
Daniel
944afe3bc8 Using package-lock 2021-06-08 13:17:50 +02:00
Dr. David A. Kunz
92a83f71a1 Update mashup.cds 2021-06-07 14:25:53 +02:00
Daniel
5e5ace0dd4 Merge branch 'master' into adding-suppliers 2021-06-07 08:33:21 +02:00
Daniel
6e686c6c8b adding top-level dependencies to @sap/cds 2021-06-04 11:52:49 +02:00
Daniel
1ccddb2155 . 2021-06-04 11:44:21 +02:00
Daniel
7c8e52d5e9 Using package-lock.json 2021-06-04 11:43:59 +02:00
Daniel
593228a51e cds.events test 2021-06-04 11:34:52 +02:00
Daniel
624cea6343 running suppliers 2021-06-04 11:34:08 +02:00
Christian Georgi
0dc1dc198d Merge pull request #227 from gregorwolf/fiori-currency-format
Fiori currency format
2021-05-28 15:08:15 +02:00
Christian Georgi
20bfb5c5f7 Merge branch 'master' into fiori-currency-format 2021-05-28 15:03:54 +02:00
Pierre Fritsch
be87f5617a Fix typo orderd -> ordered 2021-05-28 14:58:51 +02:00
Christian Georgi
3117df1282 Test with Jest 26
`cds.test` seems to have issues w/ Jest 27.

To be investigated...
2021-05-28 14:56:25 +02:00
Daniel
adfe170e8d Convenience 2021-05-21 14:03:27 +02:00
D065023
6b08826af5 Mocking events from SAP S/4HANA 2021-05-20 22:01:15 +02:00
Daniel
c6239f0375 Merged from master 2021-05-19 15:07:52 +02:00
Heiko Witteborg
be685437aa Merge pull request #231 from SAP-samples/windows-test-fix
Avoid race condition in customer-handlers.test
2021-05-14 13:41:17 +02:00
Daniel
0d19e56509 cosmetic changes 2021-05-13 12:29:22 +02:00
d049904
ff3801be71 Avoid race condition 2021-05-12 12:41:31 +02:00
Christian Georgi
702c3245bd Fix broken translation UI
Use `Books.texts` instead of `_texts`, requiring sap/cds 5
2021-05-10 10:33:39 +02:00
Daniel Hutzel
14002300c5 Rename index.cds to services.cds 2021-05-03 21:07:04 +02:00
Gregor Wolf
ad417ec061 fix test for Yen 2021-05-03 09:33:05 +02:00
Gregor Wolf
a6ec296129 add annotation for ISOCurrency 2021-05-03 09:04:38 +02:00
Gregor Wolf
1b10bbbfe4 switch currency of a book 2021-05-03 09:04:13 +02:00
Gregor Wolf
aaa1b2d6c7 add translation for Japanese Yen 2021-05-03 09:03:48 +02:00
Daniel
fc088015d3 Merge branch 'master' into adding-suppliers 2021-04-30 13:07:01 +02:00
Daniel
1cb169e886 fixed indentation 2021-04-27 16:06:59 +02:00
Daniel
9b9bb4c114 No process.chdir 2021-04-24 17:16:02 +02:00
Daniel
2536f36596 Adjust to latest capire docs 2021-04-13 09:55:27 +02:00
Daniel
c9f7dc68b8 Merge branch 'master' into adding-suppliers 2021-04-10 13:07:53 +02:00
Daniel
5f160c0927 Added debug conf 2021-04-10 13:06:15 +02:00
Daniel
0c4658a3ef Fixed and adjusted to cds5 2021-04-10 13:06:15 +02:00
Christian Georgi
33cd70065a Use cds.test 2021-04-06 16:27:36 +02:00
Christian Georgi
450577d5a7 Refer to fiori app instead of bookshop 2021-04-06 16:11:24 +02:00
Daniel
5a23ba0f76 Prepare cds5 2021-04-01 16:47:45 +02:00
Daniel
7d9303635e . 2021-03-27 19:44:39 +01:00
Daniel
b724ae900c Merge branch 'master' into adding-suppliers 2021-03-27 19:40:58 +01:00
Daniel
064ce32b8a npm script cleanup 2021-03-27 19:39:48 +01:00
Daniel
2c064d0200 Adhere to public recommendations 2021-03-27 19:39:24 +01:00
Daniel
2f96b92854 . 2021-03-27 19:30:18 +01:00
Daniel
99dc8d31d7 . 2021-03-27 19:27:29 +01:00
Daniel
c672e0f1fa Merge branch 'master' into adding-suppliers 2021-03-27 19:25:27 +01:00
Daniel
51ee6d167c added comment 2021-03-25 01:08:14 +01:00
Daniel
0ddd70acbc Using event : projection on Reviews 2021-03-11 14:14:59 +01:00
Daniel
3e52a9a102 Adding fix-antlr script 2021-03-08 19:19:29 +01:00
Daniel
b44701ef62 Merge branch 'master' into adding-suppliers 2021-03-08 19:07:56 +01:00
Daniel
c23ddc7e54 Merge branch 'master' into adding-suppliers 2021-02-19 16:36:26 +01:00
Daniel
66bd2f707c Merge branch 'master' into adding-suppliers 2021-02-19 12:42:08 +01:00
Daniel
3320c7e5a2 some corrections and optimizations 2021-02-19 12:15:35 +01:00
Daniel
a35782e775 Merge branch 'master' into adding-suppliers 2021-02-19 12:10:49 +01:00
Iwona Hahn
e5bd8ec5a5 cosmetics 2021-02-17 17:55:27 +01:00
Iwona Hahn
0aa95a0a67 cosmetics 2021-02-17 17:54:47 +01:00
Iwona Hahn
5015eb8c52 cosmetics 2021-02-17 17:53:54 +01:00
Daniel Hutzel
6d3f4c689f Delete settings.json 2021-02-17 16:07:53 +01:00
Daniel Hutzel
f0fead2bc2 Delete launch.json 2021-02-17 16:07:43 +01:00
Daniel Hutzel
f1d780d6d9 Delete tasks.json 2021-02-17 16:07:23 +01:00
Daniel Hutzel
796bf62bde Delete extensions.json 2021-02-17 16:07:02 +01:00
Daniel Hutzel
5f176a0b88 Delete .gitignore 2021-02-17 16:06:51 +01:00
Christian Georgi
a5c8b5101e Make the CatalogService usage more obvious 2021-02-17 16:00:10 +01:00
Daniel
d72ff809b0 Adding suppliers showing integration with S/4 2021-02-17 13:24:17 +01:00
Christian Georgi
5e829ae7bb Merge branch 'origin/master' into openapi 2021-02-17 09:50:04 +01:00
Christian Georgi
d265a385f8 Some comments 2020-11-17 11:12:32 +01:00
Christian Georgi
80f469b5b6 Remove need for host to swagger UI 2020-11-17 11:06:04 +01:00
Christian Georgi
8e8ae949df Allow swagger.io to call us 2020-11-16 18:21:06 +01:00
Christian Georgi
3d24dc491d Create files for all services 2020-11-16 12:06:29 +01:00
Christian Georgi
f593265687 Cosmetics 2020-11-14 21:56:08 +01:00
Christian Georgi
6d488b042c Create diagram for Open API spec 2020-11-14 21:38:18 +01:00
Christian Georgi
d5ef630743 Link in index.html. Better server URL 2020-11-14 21:18:50 +01:00
Christian Georgi
b47c9d75df Embed Swagger UI 2020-11-13 16:10:07 +01:00
73 changed files with 38946 additions and 183 deletions

View File

@@ -24,5 +24,5 @@ jobs:
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm ci
- run: npm test

2
.gitignore vendored
View File

@@ -15,3 +15,5 @@ default-env.json
packages/messageBox
reviews/msg-box
reviews/db/test.db
*.openapi3.json

3
.npmrc Normal file
View File

@@ -0,0 +1,3 @@
# Ensure we always use public packages, i.e. avoid using local registries from ~/.npmrc
@sap:registry=https://registry.npmjs.org/
registry=https://registry.npmjs.org/

View File

@@ -5,7 +5,7 @@ const app = express()
const { PORT=4444 } = process.env
const [,,port=PORT] = process.argv
process.chdir(__dirname)
const cwd = __dirname
app.use('/-/:tarball', (req,res,next) => {
console.debug ('GET', req.params)
@@ -13,7 +13,7 @@ app.use('/-/:tarball', (req,res,next) => {
const { tarball } = req.params
const [, pkg ] = /^capire-(\w+)/.exec(tarball)
fs.lstat(tarball,(err => {
if (err) exec(`npm pack ../${pkg}`,next)
if (err) exec(`npm pack ../${pkg}`,{cwd},next)
else next()
}))
} catch (e) {

37
.vscode/launch.json vendored
View File

@@ -4,28 +4,31 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach by Process ID",
"processId": "${command:PickProcess}",
"request": "attach",
"skipFiles": [
"<node_internals>/**"
],
"type": "pwa-node"
},
{
"name": "bookshop",
"command": "cds watch bookshop",
"request": "launch",
"command": "npx cds watch bookshop",
"type": "node-terminal",
"skipFiles": ["<node_internals>/**"]
"request": "launch",
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**",
"**/cds/lib/lazy.js",
"**/cds/lib/req/cls.js",
"**/odata-v4/okra/**"
]
},
{
"name": "Fiori app",
"command": "cds watch fiori",
"request": "launch",
"name": "Fiori App",
"command": "npx cds watch fiori",
"type": "node-terminal",
"skipFiles": ["<node_internals>/**"]
"request": "launch",
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**",
"**/cds/lib/lazy.js",
"**/cds/lib/req/cls.js",
"**/odata-v4/okra/**"
]
}
],
"inputs": [
@@ -33,7 +36,7 @@
"type": "pickString",
"id": "sample",
"description": "Which sample do you want to start?",
"options": ["bookshop", "fiori", "reviews", "reviews/test/bookshop"],
"options": [ "bookshop", "fiori", "reviews", "reviews" ],
"default": "bookshop"
}
]

View File

@@ -4,5 +4,14 @@
"**/.gitignore": true,
"**/.vscode": true,
"LICENSES/**": true
},
"debug.javascript.terminalOptions": {
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**",
"**/cds/lib/lazy.js",
"**/cds/lib/req/cls.js",
"**/odata-v4/okra/**"
]
}
}

View File

@@ -83,4 +83,36 @@ In case you've a question, find a bug, or otherwise need support, use our [commu
## License
Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt) file.
Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSE.txt) file.
# Suppliers - in progress for Messaging & Service Consumption -
## TODOs
1. Fix issues when running in same process
2. Automated tests
## Usage
1. Run:
```
CDS_ENV=local-hybrid cds mock API_BUSINESS_PARTNER -p 5001
```
2. Wait until startup is completed
3. Run in a 2nd terminal:
```
CDS_ENV=local-hybrid cds serve all --with-mocks --in-memory
```
4. Now, you can issues the requests listed in `suppliers/requests.http`
## Request Sequence
* TODO
## URLs
* Get books with their replicated supplier: http://localhost:4004/browse/Books?$expand=supplier
* Get remote suppliers: http://localhost:4004/admin/Suppliers?$top=11

View File

@@ -35,7 +35,7 @@ const books = new Vue ({
try {
const res = await POST(`/submitOrder`, { amount, book: book.ID })
book.stock = res.data.stock
books.order = { amount, succeeded: `Successfully orderd ${amount} item(s).` }
books.order = { amount, succeeded: `Successfully ordered ${amount} item(s).` }
} catch (e) {
books.order = { amount, failed: e.response.data.error.message }
}

View File

@@ -3,4 +3,4 @@ ID;title;descr;author_ID;stock;price;currency_code;genre_ID
207;Jane Eyre;"Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name ""Currer Bell"", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism.";107;11;12.34;GBP;11
251;The Raven;"""The Raven"" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word ""Nevermore"". The poem makes use of folk, mythological, religious, and classical references.";150;333;13.13;USD;16
252;Eleonora;"""Eleonora"" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively ""happy"" ending.";150;555;14;USD;16
271;Catweazle;Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts.;170;22;15;EUR;13
271;Catweazle;Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts.;170;22;150;JPY;13
1 ID title descr author_ID stock price currency_code genre_ID
3 207 Jane Eyre Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name "Currer Bell", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism. 107 11 12.34 GBP 11
4 251 The Raven "The Raven" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word "Nevermore". The poem makes use of folk, mythological, religious, and classical references. 150 333 13.13 USD 16
5 252 Eleonora "Eleonora" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively "happy" ending. 150 555 14 USD 16
6 271 Catweazle Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts. 170 22 15 150 EUR JPY 13

View File

@@ -4,7 +4,7 @@
"description": "A simple self-contained bookshop service.",
"dependencies": {
"@capire/common": "*",
"@sap/cds": ">=4",
"@sap/cds": "^5.0.4",
"express": "^4.17.1",
"passport": "0.4.1"
},
@@ -16,8 +16,7 @@
"cds": {
"requires": {
"db": {
"kind": "sqlite",
"model": "*"
"kind": "sql"
}
}
}

Binary file not shown.

View File

@@ -1,13 +1,16 @@
using { sap.capire.bookshop as my } from '../db/schema';
service CatalogService @(path:'/browse') {
@readonly entity Books as SELECT from my.Books { *,
/** For displaying lists of Books */
@readonly entity ListOfBooks as projection on Books {
ID, title, author, genre, price, currency
}
/** For display in details pages */
@readonly entity Books as projection on my.Books { *,
author.name as author
} excluding { createdBy, modifiedBy };
@readonly entity ListOfBooks as SELECT from Books
excluding { descr };
@requires: 'authenticated-user'
action submitOrder ( book: Books:ID, amount: Integer ) returns { stock: Integer };
event OrderedBook : { book: Books:ID; amount: Integer; buyer: String };

View File

@@ -5,10 +5,10 @@ class CatalogService extends cds.ApplicationService { init(){
// Reduce stock of ordered books if available stock suffices
this.on ('submitOrder', async req => {
const {book,amount} = req.data, tx = cds.tx(req)
let {stock} = await tx.read('stock').from(Books,book)
const {book,amount} = req.data
let {stock} = await SELECT `stock` .from (Books,book)
if (stock >= amount) {
await tx.update (Books,book).with ({ stock: stock -= amount })
await UPDATE (Books,book) .with (`stock -=`, amount)
await this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
return { stock }
}
@@ -16,10 +16,8 @@ class CatalogService extends cds.ApplicationService { init(){
})
// Add some discount for overstocked books
this.after ('READ','Books', each => {
if (each.stock > 111) {
each.title += ` -- 11% discount!`
}
this.after ('READ','ListOfBooks', each => {
if (each.stock > 111) each.title += ` -- 11% discount!`
})
return super.init()

View File

@@ -1,19 +0,0 @@
const cds = require ('@sap/cds')
cds.on('listening', async ()=>{ try { //> just to see the output at the end; will later on be in db service impl
console.log ('\nFilling database with initial data...')
const m = cds.model.minified() //> we likely should do that by default
const tx = cds.tx()
tx.run ([ //> this will be replaced by
DELETE.from ('sap.capire.bookshop.Authors'),
DELETE.from ('sap.capire.bookshop.Books'),
DELETE.from ('sap.capire.bookshop.Books.texts'),
DELETE.from ('sap.capire.bookshop.Genres'),
DELETE.from ('sap.common.Currencies'),
DELETE.from ('sap.common.Currencies.texts'),
])
await cds.deploy(m).to(tx,{ddl:false})
await tx.commit()
} catch(e) { console.error(e) }})
module.exports = cds.server

View File

@@ -5,9 +5,11 @@ CAD;de;Kanadischer Dollar;Kanadischer Dollar
AUD;de;Australischer Dollar;Australischer Dollar
GBP;de;Pfund;Britische Pfund
ILS;de;Schekel;Israelische Schekel
JPY;de;Yen;Japanische Yen
EUR;fr;euro;de la Zone euro
USD;fr;dollar;dollar des États-Unis
CAD;fr;dollar canadien;dollar canadien
AUD;fr;dollar australien;dollar australien
GBP;fr;livre sterling;pound sterling
ILS;fr;Shekel;shekel israelien
JPY;fr;Yen;Yen japonais
1 code locale name descr
5 AUD de Australischer Dollar Australischer Dollar
6 GBP de Pfund Britische Pfund
7 ILS de Schekel Israelische Schekel
8 JPY de Yen Japanische Yen
9 EUR fr euro de la Zone euro
10 USD fr dollar dollar des États-Unis
11 CAD fr dollar canadien dollar canadien
12 AUD fr dollar australien dollar australien
13 GBP fr livre sterling pound sterling
14 ILS fr Shekel shekel israelien
15 JPY fr Yen Yen japonais

View File

@@ -1,2 +1,2 @@
# cds.requires.messaging.kind = file-based-messaging
cds.requires.messaging.kind = file-based-messaging
PORT = 4004

View File

@@ -1,4 +1,5 @@
using { AdminService } from '../../db/schema';
using { AdminService, sap.capire.bookshop } from '../../db/schema';
using from '../common'; // to help UI linter get the complete annotations
////////////////////////////////////////////////////////////////////////////
//
@@ -70,7 +71,7 @@ annotate AdminService.Authors with @(
annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
annotate AdminService.Books with @odata.draft.enabled;
annotate AdminService.Books_texts with @(
annotate AdminService.Books.texts with @(
UI: {
Identification: [{Value:title}],
SelectionFields: [ locale, title ],
@@ -83,11 +84,12 @@ annotate AdminService.Books_texts with @(
);
// Add Value Help for Locales
annotate AdminService.Books_texts {
annotate AdminService.Books.texts {
locale @ValueList:{entity:'Languages',type:#fixed}
}
// In addition we need to expose Languages through AdminService
// In addition we need to expose Languages and Books.texts through AdminService
using { sap } from '@sap/cds/common';
extend service AdminService {
entity Languages as projection on sap.common.Languages;
entity Books.texts as projection on bookshop.Books.texts;
}

View File

@@ -1,3 +0,0 @@
<head>
<meta http-equiv="refresh" content="0;url=bookshop/index.html">
</head>

View File

@@ -54,7 +54,7 @@ annotate my.Books with {
title @title:'{i18n>Title}';
genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
author @title:'{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly };
price @title:'{i18n>Price}';
price @title:'{i18n>Price}' @Measures.ISOCurrency: currency_code;
stock @title:'{i18n>Stock}';
descr @UI.MultiLineText;
}

View File

@@ -1,3 +0,0 @@
<head>
<meta http-equiv="refresh" content="0;url=reviews/index.html">
</head>

View File

@@ -3,10 +3,11 @@
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "*",
"@capire/reviews": "*",
"@capire/orders": "*",
"@capire/common": "*",
"@sap/cds": ">=4",
"@capire/orders": "*",
"@capire/reviews": "*",
"@capire/suppliers": "*",
"@sap/cds": ">=5",
"express": "^4.17.1",
"passport": "^0.4.1"
},
@@ -19,6 +20,10 @@
"deploy-format": "hdbtable"
},
"requires": {
"API_BUSINESS_PARTNER": {
"kind": "odata",
"model": "@capire/suppliers"
},
"auth": {
"strategy": "dummy"
},

View File

@@ -1,18 +1,16 @@
const cds = require ('@sap/cds')
module.exports = cds.server
cds.once('bootstrap',(app)=>{
app.use ('/orders/webapp', _from('@capire/orders/app/orders/webapp/manifest.json'))
app.use ('/bookshop', _from('@capire/bookshop/app/vue/index.html'))
app.use ('/reviews', _from('@capire/reviews/app/vue/index.html'))
app.serve ('/orders/webapp').from('@capire/orders','app/orders/webapp')
app.serve ('/bookshop').from('@capire/bookshop','app/vue')
app.serve ('/reviews').from('@capire/reviews','app/vue')
})
cds.once('served', require('./srv/mashup'))
cds.once('served', require('@capire/suppliers/srv/mashup'))
module.exports = cds.server
// -----------------------------------------------------------------------
// Helper for serving static content from npm-installed packages
const {static} = require('express')
const {dirname} = require('path')
const _from = target => static (dirname (require.resolve(target)))
// Swagger UI - see https://cap.cloud.sap/docs/advanced/openapi
if (process.env.NODE_ENV !== 'production') {
cds.once ('bootstrap', app => app.use (require ('cds-swagger-ui-express')()) )
}

View File

@@ -3,17 +3,17 @@
// Mashing up imported models...
//
using { sap.capire.bookshop.Books } from '@capire/bookshop';
//
// Extend Books with access to Reviews and average ratings
//
using { CatalogService.ListOfBooks, sap.capire.bookshop.Books } from '@capire/bookshop';
using { ReviewsService.Reviews } from '@capire/reviews';
extend Books with {
reviews : Composition of many Reviews on reviews.subject = $self.ID;
rating : Decimal;
rating : Reviews:rating;
}
extend projection ListOfBooks with { rating }
//
// Extend Orders with Books as Products

View File

@@ -38,7 +38,11 @@ GET {{bookshop}}/browse/Books(201)?
&$select=ID,title,rating
&$expand=reviews
###
GET {{bookshop}}/browse/Books?
&$select=title,author&$expand=currency
Accept-Language: de
#################################################
#

View File

@@ -2,6 +2,28 @@
"name": "@capire/hello-world",
"version": "1.0.0",
"scripts": {
"watch": "cds serve world.cds"
"test": "npx jest --silent",
"watch": "cds serve world.cds",
"watch:ts": "cds-ts serve world.cds"
},
"devDependencies": {
"@types/jest": "^26.0.23",
"@types/node": "^15.12.0",
"ts-jest": "^27.0.2",
"typescript": "^4.3.5"
},
"jest": {
"testEnvironment": "node",
"preset": "ts-jest",
"globals": {
"ts-jest": {
"diagnostics": {
"_comment": "see https://githubmemory.com/repo/kulshekhar/ts-jest/issues/2722",
"ignoreCodes": [
151001
]
}
}
}
}
}

View File

@@ -0,0 +1,15 @@
process.env.CDS_TYPESCRIPT = 'true';
import * as cds from '@sap/cds';
//@ts-ignore
const {GET} = cds.test.in(__dirname,'..').run('serve', 'world.cds');
describe('Hello world!', () => {
afterAll(() => { delete process.env.CDS_TYPESCRIPT; });
it('should say hello with class impl from a typescript file', async () => {
const {data} = await GET`/say/hello(to='world')`
expect(data.value).toMatch(/Hello world.*typescript.*/i)
})
})

5
hello/world.ts Normal file
View File

@@ -0,0 +1,5 @@
module.exports = class say {
hello(req: any) {
return `Hello ${req.data.to} from a typescript file!`
}
}

View File

@@ -13,7 +13,7 @@ entity Orders_Items {
up_ : Association to Orders;
product : Association to Products @assert.integrity:false; // REVISIT: this is a temporary workaround for a glitch in cds-runtime
amount : Integer;
title : String;
title : String; //> intentionally replicated as snapshot from product.title
price : Double;
}

View File

@@ -2,6 +2,6 @@
"name": "@capire/orders",
"version": "1.0.0",
"dependencies": {
"@sap/cds": ">=4.3.0"
"@sap/cds": "^5"
}
}

32038
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -11,29 +11,36 @@
"@capire/hello": "./hello",
"@capire/media": "./media",
"@capire/orders": "./orders",
"@capire/reviews": "./reviews"
"@capire/reviews": "./reviews",
"@capire/suppliers": "./suppliers",
"@sap/cds": "^5",
"express": "^4"
},
"devDependencies": {
"cds-swagger-ui-express": "^0.2.0",
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"sqlite3": "^5.0.0"
},
"scripts": {
"cleanup": "rm -rf node_modules && rm -rf */node_modules && rm -rf */*/node_modules",
"registry": "node .registry/server.js",
"bookshop": "cds watch bookshop",
"fiori": "cds watch fiori",
"media": "cds watch media",
"mocha": "npx mocha || echo",
"jest": "npx jest",
"test": "npm run jest --silent"
"test": "npm run jest --silent && npm run test:hello",
"test:hello": "cd hello && npm test"
},
"jest": {
"testEnvironment": "node",
"testMatch": ["**/*.test.js"]
},
"mocha": {
"parallel": true
},
"jest": {
"testEnvironment": "node"
},
"license": "SAP SAMPLE CODE LICENSE",
"private": true
}

View File

@@ -7,7 +7,7 @@
"index.cds"
],
"dependencies": {
"@sap/cds": ">=4",
"@sap/cds": "^5",
"express": "^4.17.1"
},
"scripts": {

View File

@@ -2,10 +2,10 @@
## Run all-in-one
Open a terminal window and run the bookshop in it:
Open a terminal window and run the `fiori` app in it:
```sh
npm run bookshop
npm run fiori
```
@@ -17,8 +17,8 @@ Open two terminal windows. In the first one start the reviews service stand-alon
npm run reviews-service
```
In the second one start the bookshop:
In the second one start the `fiori` app:
```sh
npm run bookshop
npm run fiori
```

View File

@@ -8,9 +8,9 @@ service ReviewsService {
action unlike (review: type of Reviews:ID);
// Async API
event reviewed : {
subject: type of Reviews:subject;
rating: Decimal(2,1)
event reviewed : projection on Reviews {
subject, //> recieved new reviews
rating //> new avg rating
}
// Input validation

View File

@@ -0,0 +1,2 @@
cds.requires.messaging.kind = file-based-messaging
PORT = 4004

View File

@@ -0,0 +1,19 @@
{
"name": "@capire/fiori",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "*",
"@capire/reviews": "*",
"@sap/cds": "^5",
"express": "^4.17.1"
},
"cds": {
"requires": {
"auth": { "strategy": "dummy" },
"ReviewsService": {
"kind": "odata",
"model": "@capire/reviews"
}
}
}
}

View File

@@ -0,0 +1,20 @@
const cds = require ('@sap/cds')
cds.once('bootstrap',(app)=>{
// Delegate to imported apps (reviews only when mocked)
app.serve ('/bookshop').from ('@capire/bookshop','app/vue')
app.serve ('/reviews',).from ('@capire/reviews','app/vue')
})
cds.once('served', async ()=>{
// Update Books' average ratings when ReviewsService signals updated reviews
const ReviewsService = await cds.connect.to ('ReviewsService')
ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { subject, rating } = msg.data
return UPDATE('Books',subject).with({rating})
})
})
module.exports = cds.server

View File

@@ -0,0 +1,11 @@
namespace sap.capire.bookshop; //> allows UPDATE('Books')...
//
// Extend Books with access to Reviews and average ratings
//
using { CatalogService.ListOfBooks, sap.capire.bookshop.Books } from '@capire/bookshop';
using { ReviewsService.Reviews } from '@capire/reviews';
extend Books with {
reviews : Composition of many Reviews on reviews.subject = $self.ID;
rating : Reviews:rating;
}
extend projection ListOfBooks with { rating }

View File

@@ -7,6 +7,7 @@ Each sub directory essentially is an individual npm package arranged in an [all-
## [@capire/hello-world](hello)
- A simplistic [Hello World](https://cap.cloud.sap/docs/get-started/hello-world) service using [CDS](https://cap.cloud.sap/docs/cds/) and [cds.services](https://cap.cloud.sap/docs/node.js/api#services-api).
- [Typescript support](https://cap.cloud.sap/docs/get-started/using-typescript)
## [@capire/bookshop](bookshop)
@@ -49,20 +50,29 @@ Each sub directory essentially is an individual npm package arranged in an [all-
- Late-cut Micro Services
- As well as managed data, input validations, and authorization
## [@capire/suppliers](suppliers)
- Shows how to integrate remote services, in this case the BusinessPartner service from SAP S/4HANA.
- Extending [@capire/bookshop](bookshop) with suppliers from SAP S/4HANA
- Providing that as a pre-built integration & extension package
- Used in [@capire/fiori](fiori)
## [@capire/fiori](fiori)
- A [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages:
- [@capire/bookshop](bookshop)
- [@capire/reviews](reviews)
- [@capire/orders](orders)
- [@capire/common](common)
- [@capire/orders](orders)
- [@capire/reviews](reviews)
- [@capire/suppliers](suppliers)
- [Adds an SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to:
- [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files
- Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)
- Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)
- Serving SAP Fiori apps locally
- [The Vue.js app](bookshop/app/vue) imported from bookshop is served as well
- [OpenAPI export + Swagger UI](https://cap.cloud.sap/docs/advanced/openapi)
<br>

1
suppliers/.env Normal file
View File

@@ -0,0 +1 @@
PORT = 4006

View File

@@ -0,0 +1,31 @@
Books = Books
Book = Book
ID = ID
Title = Title
Author = Author
AuthorID = Author ID
Stock = Stock
Name = Name
AuthorName = Author's Name
DateOfBirth = Date of Birth
DateOfDeath = Date of Death
PlaceOfBirth = Place of Birth
PlaceOfDeath = Place of Death
Age = Age
Authors = Authors
Order = Order
Orders = Orders
Price = Price
Genre = Genre
Genres = Genres
SubGenres = Sub Genres
NumCode = Numeric Code
MinorUnit = Minor Unit
Exponent = Exponent
Supplier = Supplier
SupplierName = Supplier Name
Id = Id
Name = Name

View File

@@ -0,0 +1,18 @@
Books = Bücher
Book = Buch
ID = ID
Title = Titel
Authors = Autoren
Author = Autor
AuthorID = ID des Autors
AuthorName = Name des Autors
Age = Alter
Name = Name
Stock = Bestand
Order = Bestellung
Orders = Bestellungen
Price = Preis
Supplier = Lieferant
SupplierName = Lieferantenname
Id = Id
Name = Name

View File

@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bookshop</title>
<script>
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
applications: {
"browse-books": {
title: "Browse Books",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=bookshop",
applicationType : "URL",
url: "/browse/webapp",
navigationMode: "embedded"
},
"manage-books": {
title: "Manage Books",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=admin",
applicationType : "URL",
url: "/admin/webapp",
navigationMode: "embedded"
},
"manage-orders": {
title: "Manage Orders",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL",
url: "/orders/webapp",
navigationMode: "embedded"
}
}
};
</script>
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.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"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-frameOptions="allow"
></script>
<script>
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
</script>
</head>
<body class="sapUiBody" id="content"></body>
</html>

View File

@@ -0,0 +1,105 @@
//using { AdminService } from '../../db/schema';
////////////////////////////////////////////////////////////////////////////
//
// Books Object Page
//
annotate AdminService.Books with @(
UI: {
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Translations}', Target: 'texts/@UI.LineItem'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Supplier}', Target: '@UI.FieldGroup#Supplier'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'},
],
FieldGroup#General: {
Data: [
{Value: title},
{Value: author_ID},
{Value: genre_ID},
{Value: descr},
]
},
FieldGroup#Details: {
Data: [
{Value: stock},
{Value: price},
{Value: currency_code, Label: '{i18n>Currency}'},
]
},
FieldGroup#Supplier: {
Data: [
{Value: supplier_ID, Label: '{i18n>Id}'},
{Value: supplier.name, Label: '{i18n>Name}'}
]
},
FieldGroup#Admin: {
Data: [
{Value: createdBy},
{Value: createdAt},
{Value: modifiedBy},
{Value: modifiedAt}
]
}
}
);
annotate AdminService.Authors with @(
UI: {
HeaderInfo: {
Description: {Value: lifetime}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Books}', Target: 'books/@UI.LineItem'},
],
FieldGroup#Details: {
Data: [
{Value: placeOfBirth},
{Value: placeOfDeath},
{Value: dateOfBirth},
{Value: dateOfDeath},
{Value: age, Label: '{i18n>Age}'},
]
},
}
);
////////////////////////////////////////////////////////////
//
// Draft for Localized Data
//
annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
annotate AdminService.Books with @odata.draft.enabled;
annotate AdminService.Books.texts with @(
UI: {
Identification: [{Value:title}],
SelectionFields: [ locale, title ],
LineItem: [
{Value: locale, Label: 'Locale'},
{Value: title, Label: 'Title'},
{Value: descr, Label: 'Description'}
]
}
);
// Add Value Help for Locales
annotate AdminService.Books.texts {
locale @ValueList:{entity:'Languages',type:#fixed}
}
// In addition we need to expose Languages through AdminService
using { sap } from '@sap/cds/common';
extend service AdminService {
entity Languages as projection on sap.common.Languages;
}
annotate AdminService.Suppliers with
@(Capabilities.SearchRestrictions : {
Searchable : false
});

View File

@@ -0,0 +1,8 @@
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("admin.Component", {
metadata: { manifest: "json" }
});
});
/* eslint no-undef:0 */

View File

@@ -0,0 +1,11 @@
# This is the resource bundle of itelo
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
# JCI app descriptor contains lower case TITLE
appTitle=Bookshop Sample
# JCI app descriptor contains lower case DESCRIPTION
appSubTitle=CAP Sample Application
# JCI app descriptor contains lower case DESCRIPTION
appDescription=CDS Sample Service

View File

@@ -0,0 +1,128 @@
{
"_version": "1.8.0",
"sap.app": {
"id": "admin",
"type": "application",
"title": "Manage Books",
"description": "Sample Application",
"i18n": "i18n/i18n.properties",
"dataSources": {
"AdminService": {
"uri": "/admin/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
},
"-sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"-id": "ui5template.smartTemplate",
"-version": "1.40.12"
}
},
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
}
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"uri": "i18n/i18n.properties"
},
"": {
"dataSource": "AdminService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect" : true,
"earlyRequests": true,
"groupProperties": {
"default": {
"submit": "Auto"
}
}
}
}
},
"routing": {
"routes": [
{
"pattern": ":?query:",
"name": "BooksList",
"target": "BooksList"
},
{
"pattern": "Books({key}):?query:",
"name": "BooksDetails",
"target": "BooksDetails"
},
{
"pattern": "Books({key}/author({key2}):?query:",
"name": "AuthorsDetails",
"target": "AuthorsDetails"
}
],
"targets": {
"BooksList": {
"type": "Component",
"id": "BooksList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings" : {
"entitySet" : "Books",
"navigation" : {
"Books" : {
"detail" : {
"route" : "BooksDetails"
}
}
}
}
}
},
"BooksDetails": {
"type": "Component",
"id": "BooksDetailsList",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet" : "Books",
"navigation" : {
"Authors" : {
"detail" : {
"route" : "AuthorsDetails"
}
}
}
}
}
},
"AuthorsDetails": {
"type": "Component",
"id": "AuthorsDetailsList",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet" : "Authors"
}
}
}
}
},
"contentDensities": {
"compact": true,
"cozy": true
}
},
"sap.ui": {
"technology": "UI5",
"fullWidth": false
},
"sap.fiori": {
"registrationIds": [],
"archeType": "transactional"
}
}

265
suppliers/app/common.cds Normal file
View File

@@ -0,0 +1,265 @@
/*
Common Annotations shared by all apps
*/
using { sap.capire.bookshop as my } from '@capire/bookshop';
using { sap.common } from '@capire/common';
////////////////////////////////////////////////////////////////////////////
//
// Books Lists
//
annotate my.Books with @(
Common.SemanticKey: [title],
UI: {
Identification: [{Value:title}],
SelectionFields: [ ID, author_ID, price, currency_code, supplier.ID, supplier.name ],
LineItem: [
{Value: ID},
{Value: title},
{Value: author.name, Label:'{i18n>Author}'},
{Value: genre.name},
{Value: stock},
{Value: price},
{Value: currency.symbol, Label:' '},
{Value: supplier.ID},
{Value: supplier.name}
]
}
) {
author @ValueList.entity:'Authors';
};
////////////////////////////////////////////////////////////////////////////
//
// Books Details
//
annotate my.Books with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Book}',
TypeNamePlural: '{i18n>Books}',
Title: {Value: title},
Description: {Value: author.name}
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books Elements
//
annotate my.Books with {
ID @title:'{i18n>ID}' @UI.HiddenFilter;
title @title:'{i18n>Title}';
genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
author @title:'{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly };
price @title:'{i18n>Price}' @Measures.ISOCurrency: currency_code;
stock @title:'{i18n>Stock}';
descr @UI.MultiLineText;
supplier_ID @title:'{i18n>Supplier}';
}
annotate my.Suppliers with {
name @title:'{i18n>SupplierName}';
ID @title:'{i18n>Supplier}';
}
////////////////////////////////////////////////////////////////////////////
//
// Genres List
//
annotate my.Genres with @(
Common.SemanticKey: [name],
UI: {
SelectionFields: [ name ],
LineItem:[
{Value: name},
{Value: parent.name, Label: 'Main Genre'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Genre Details
//
annotate my.Genres with @(
UI: {
Identification: [{Value:name}],
HeaderInfo: {
TypeName: '{i18n>Genre}',
TypeNamePlural: '{i18n>Genres}',
Title: {Value: name},
Description: {Value: ID}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>SubGenres}', Target: 'children/@UI.LineItem'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Genres Elements
//
annotate my.Genres with {
ID @title: '{i18n>ID}';
name @title: '{i18n>Genre}';
}
////////////////////////////////////////////////////////////////////////////
//
// Authors List
//
annotate my.Authors with @(
Common.SemanticKey: [name],
UI: {
Identification: [{Value:name}],
SelectionFields: [ name ],
LineItem:[
{Value: ID},
{Value: name},
{Value: dateOfBirth},
{Value: dateOfDeath},
{Value: placeOfBirth},
{Value: placeOfDeath},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Author Details
//
annotate my.Authors with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Author}',
TypeNamePlural: '{i18n>Authors}',
Title: {Value: name},
Description: {Value: dateOfBirth}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Target: 'books/@UI.LineItem'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Authors Elements
//
annotate my.Authors with {
ID @title:'{i18n>ID}' @UI.HiddenFilter;
name @title:'{i18n>Name}';
dateOfBirth @title:'{i18n>DateOfBirth}';
dateOfDeath @title:'{i18n>DateOfDeath}';
placeOfBirth @title:'{i18n>PlaceOfBirth}';
placeOfDeath @title:'{i18n>PlaceOfDeath}';
}
////////////////////////////////////////////////////////////////////////////
//
// Languages List
//
annotate common.Languages with @(
Common.SemanticKey: [code],
Identification: [{Value:code}],
UI: {
SelectionFields: [ name, descr ],
LineItem:[
{Value: code},
{Value: name},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Language Details
//
annotate common.Languages with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Language}',
TypeNamePlural: '{i18n>Languages}',
Title: {Value: name},
Description: {Value: descr}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
],
FieldGroup#Details: {
Data: [
{Value: code},
{Value: name},
{Value: descr}
]
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currencies List
//
annotate common.Currencies with @(
Common.SemanticKey: [code],
Identification: [{Value:code}],
UI: {
SelectionFields: [ name, descr ],
LineItem:[
{Value: descr},
{Value: symbol},
{Value: code},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currency Details
//
annotate common.Currencies with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Currency}',
TypeNamePlural: '{i18n>Currencies}',
Title: {Value: descr},
Description: {Value: code}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Extended}', Target: '@UI.FieldGroup#Extended'},
],
FieldGroup#Details: {
Data: [
{Value: name},
{Value: symbol},
{Value: code},
{Value: descr}
]
},
FieldGroup#Extended: {
Data: [
{Value: numcode},
{Value: minor},
{Value: exponent}
]
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currencies Elements
//
annotate common.Currencies with {
numcode @title:'{i18n>NumCode}';
minor @title:'{i18n>MinorUnit}';
exponent @title:'{i18n>Exponent}';
}

View File

@@ -0,0 +1,12 @@
/*
This model controls what gets served to Fiori frontends...
*/
using from './admin/fiori-service';
//using from './browse/fiori-service';
using from './common';
using from '@capire/common';
// only works in case of embedded orders service
using from '@capire/orders/app/orders/fiori-service';

1
suppliers/index.cds Normal file
View File

@@ -0,0 +1 @@
using from './srv/mashup';

1
suppliers/model.drawio Normal file
View File

@@ -0,0 +1 @@
<mxfile host="Electron" modified="2021-06-16T08:41:31.745Z" agent="5.0 (Macintosh; Intel Mac OS X 11_4_0) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/14.6.13 Chrome/89.0.4389.128 Electron/12.0.7 Safari/537.36" etag="keOnkQccb51SB06o_xsB" version="14.6.13" type="device"><diagram id="kIZwyAu_b989F7xyfxsf" name="Page-1">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</diagram></mxfile>

40
suppliers/package.json Normal file
View File

@@ -0,0 +1,40 @@
{
"name": "@capire/suppliers",
"version": "1.0.0",
"description": "Shows integration with SAP S/4HANA, in turn provided as a reusable extension package to bookshop.",
"private": true,
"dependencies": {
"@capire/common": "*",
"@sap/cds": ">=4",
"express": "^4"
},
"scripts": {
"start": "cds run --in-memory?",
"watch": "cds watch",
"mocked-s4": "cds mock API_BUSINESS_PARTNER"
},
"cds": {
"requires": {
"API_BUSINESS_PARTNER": {
"kind": "odata",
"model": "srv/external/API_BUSINESS_PARTNER",
"[hybrid]": {
"credentials": {
"destination": "cap-api098",
"path": "/sap/opu/odata/sap/API_BUSINESS_PARTNER"
}
}
},
"[local-hybrid]": {
"messaging": {
"kind": "file-based-messaging"
}
},
"[hybrid]": {
"messaging": {
"kind": "enterprise-messaging-shared"
}
}
}
}
}

51
suppliers/requests.http Normal file
View File

@@ -0,0 +1,51 @@
@server = http://localhost:4006
@bpServer = http://localhost:5001
@authAlice = Authorization: Basic alice:
###
### Replication on changed Business Partner
###
### 1. Check supplier name before update ("A Company Making Everything")
GET {{server}}/admin/Books(299)?$expand=supplier
{{authAlice}}
### 2. Change Business Partner -> Triggers event that updates supplier replication
PATCH {{bpServer}}/api-business-partner/A_BusinessPartner('ACME')
Content-Type: application/json
{
"BusinessPartnerFullName": "A Company Making Everything *better*"
}
### 3. Check supplier name after update ("A Company Making Everything *better*")
GET {{server}}/admin/Books(299)?$expand=supplier
{{authAlice}}
###
### Replication on new assigned supplier
###
### 1. No supplier is assigned to "Wuthering Heights"
GET {{server}}/admin/Books(201)?$expand=supplier
{{authAlice}}
### 2. Assign supplier ID "PNG"
PATCH {{server}}/admin/Books(201)
{{authAlice}}
Content-Type: application/json
{
"supplier_ID": "PNG"
}
### 3. Supplier information is replicated
GET {{server}}/admin/Books(201)?$expand=supplier
{{authAlice}}

3
suppliers/server.js Normal file
View File

@@ -0,0 +1,3 @@
const cds = require ('@sap/cds')
cds.once('served', require('./srv/mashup'))
module.exports = cds.server

View File

@@ -0,0 +1,2 @@
ID;title;descr;author_ID;stock;price;currency_code;genre_ID;supplier_ID;
299;Mobby Dick;"""Moby-Dick""" or """The Whale""" is an 1851 novel by American writer Herman Melville. The book is the sailor Ishmael's narrative of the obsessive quest of Ahab, captain of the whaling ship Pequod, for revenge on Moby Dick, the giant white sperm whale that on the ship's previous voyage bit off Ahab's leg at the knee. A contribution to the literature of the American Renaissance, Moby-Dick was published to mixed reviews, was a commercial failure, and was out of print at the time of the author's death in 1891.;105;99;15.20;GBP;11;ACME
Can't render this file because it contains an unexpected character in line 2 and column 30.

View File

@@ -0,0 +1,3 @@
ID;name
ACME;A Company Making Everything (replicated)
1 ID name
2 ACME A Company Making Everything (replicated)

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,10 @@
const cds = require('@sap/cds');
module.exports = cds.service.impl(function () {
const { A_BusinessPartner } = this.entities;
// https://api.sap.com/event/SAPS4HANACloudBusinessEvents_BusinessPartner/resource
this.after('UPDATE', A_BusinessPartner, async data => {
await this.emit("BusinessPartner.Changed", { BusinessPartner: data.BusinessPartner });
});
});

View File

@@ -0,0 +1,7 @@
BusinessPartner;BusinessPartnerFullName
ACME;A Company Making Everything
B4U;Books for You
S&C;Shakespeare & Co.
WSL;Waterstones
TLD;Thalia
PNG;Penguin Books
1 BusinessPartner BusinessPartnerFullName
2 ACME A Company Making Everything
3 B4U Books for You
4 S&C Shakespeare & Co.
5 WSL Waterstones
6 TLD Thalia
7 PNG Penguin Books

72
suppliers/srv/mashup.cds Normal file
View File

@@ -0,0 +1,72 @@
using { API_BUSINESS_PARTNER as S4 } from './external/API_BUSINESS_PARTNER.csn';
using { CatalogService, sap.capire.bookshop.Books } from '@capire/bookshop';
namespace sap.capire.bookshop;
/**
Add projections to external entities to capture the subset of fields we're
actually interested in. This fosters both: (a) minimized network traffic as
well as (b) options to dynamically add extension fields by SaaS tenants.
*/
entity Suppliers as projection on S4.A_BusinessPartner {
key BusinessPartner as ID,
BusinessPartnerFullName as name,
// to_BusinessPartnerAddress[1: ValidityStartDate <= $now and $now < ValidityEndDate].CityName,
// to_BusinessPartnerAddress[1: ValidityStartDate <= $now and $now < ValidityEndDate].Country,
}
/**
We can mashup entities from external services, or projections thereof, with
our project's own entities, e.g. by adding relationships as below.
*/
extend Books with {
supplier : Association to Suppliers;
}
/*
Optionally expose external entities through own services, e.g. for Value Helps,
or to display details fetched on demand.
For this to work, we need to delegate the respective calls addressed to our
services into calls to the external service.
*/
extend service AdminService with {
@cds.odata.valuelist
entity Suppliers as projection on bookshop.Suppliers;
}
/*
Optionally add local persistency to replicate data for fast access,
e.g. to display lists containing remote data.
*/
annotate Suppliers with @cds.persistence: {table,skip:false};
/**
Having locally cached replicas also allows to display supplier data in lists
of books, which otherwise would generate unwanted traffic on S4 backends.
*/
extend projection CatalogService.ListOfBooks with {
supplier.name as supplier
}
/**
Optionally declare events emitted from the source, but not included in
imported APIs (e.g. as in case of EDMXes from API Hub).
This allows CAP's advanced support for events and messaging to kick in,
e.g. to automatically emit to and subscribe to events from message brokers
behind the scenes.
Note: as sync and async APIs from S/4 sources are not correlated, we have
to specify the event type names, e.g. as be found at:
https://api.sap.com/event/SAPS4HANACloudBusinessEvents_BusinessPartner/resource
*/
extend service S4 {
event BusinessPartner.Changed @(type: 'sap.s4.beh.businesspartner.v1.BusinessPartner.Changed.v1') {
BusinessPartner: S4.A_BusinessPartner:BusinessPartner;
}
}

50
suppliers/srv/mashup.js Normal file
View File

@@ -0,0 +1,50 @@
////////////////////////////////////////////////////////////////////////////
//
// Mashing up provided and required services...
//
module.exports = async()=>{ // called by server.js
if (!cds.services.AdminService) return //> mocking S4 service only
// Connect to services we want to mashup below...
const S4bupa = await cds.connect.to('API_BUSINESS_PARTNER') //> external S4 service
const admin = await cds.connect.to('AdminService') //> local domain service
const db = await cds.connect.to('db') //> our primary database
// Reflect CDS definition of the Suppliers entity
const { Suppliers } = db.entities
admin.prepend (()=>{ //> to ensure our .on handlers below go before the default ones
// Delegate Value Help reads for Suppliers to S4 backend
admin.on ('READ', 'Suppliers', req => {
console.log ('>> delegating to S4 service...')
return S4bupa.run(req.query)
})
// Replicate Supplier data when edited Books have suppliers
admin.after (['CREATE','UPDATE'], 'Books', async data => {
const { supplier_ID: ID } = data
if (ID) {
let replicated = await db.exists (Suppliers,ID)
if (!replicated) { // initially replicate Supplier info
let supplier = await S4bupa.read (Suppliers,ID)
await INSERT(supplier) .into (Suppliers)
}
}
})
})
// Subscribe to changes in the S4 origin of Suppliers data
S4bupa.on ('BusinessPartner.Changed', async msg => { //> would be great if we had batch events from S/4
console.log(">>", msg.event, msg.data)
const ID = msg.data.BusinessPartner;
let replicated = await db.exists (Suppliers,ID)
if (replicated) { // update replicated Supplier info
let supplier = await S4bupa.read (Suppliers,ID)
await UPDATE(Suppliers,ID) .with (supplier)
}
})
}

22
test/cds.events.test.js Normal file
View File

@@ -0,0 +1,22 @@
const cds = require ('@sap/cds/lib')
const { expect } = cds.test.in (__dirname)
describe('cds.events tests', ()=>{
let m; before (async ()=> m = await cds.load('events.cds'))
it ('should have the model loaded', ()=>{
expect(m.definitions).to.have.property('Sue.Foo')
})
it ('should compile the model to edmx', ()=>{
const edmx = cds.compile(m).to.edmx({service:'Sue'})
expect(edmx).to.match(/<EntitySet Name="Foo" EntityType="Sue.Foo"\/>/)
})
it ('should compile the model to sql', ()=>{
const sql = cds.compile(m).to.sql().join(';\n')
expect(sql).not.to.match(/CREATE TABLE Sue_Foo/)
expect(sql).to.match(/CREATE TABLE Sue_Bar/)
})
})

View File

@@ -280,7 +280,7 @@ describe('cds.ql → cqn', () => {
).to.eql({
SELECT: {
from: { ref: ['Foo'] },
where: cdr
where: cds.version >= '5.3.0'
? [
// '(', //> this one is not required
{ ref: ['ID'] },
@@ -289,7 +289,7 @@ describe('cds.ql → cqn', () => {
'and',
{ ref: ['args'] },
'in',
{ val: args },
{ list: args.map(val => ({ val })) },
'and',
'(', //> this one is missing, and that's changing the logic -> that's a BUG
{ ref: ['x'] },

View File

@@ -6,13 +6,9 @@ else cds.User = cds.User.Privileged // hard core monkey patch for older cds rele
describe('Custom Handlers', () => {
it('should reject out-of-stock orders', async () => {
await expect(
Promise.all([
POST('/browse/submitOrder', { book: 201, amount: 5 }),
POST('/browse/submitOrder', { book: 201, amount: 5 }),
POST('/browse/submitOrder', { book: 201, amount: 5 }),
])
).to.be.rejectedWith(/409 - 5 exceeds stock for book #201/)
await POST('/browse/submitOrder', { book: 201, amount: 5 })
await POST('/browse/submitOrder', { book: 201, amount: 5 })
await expect(POST('/browse/submitOrder', { book: 201, amount: 5 })).to.be.rejectedWith(/409 - 5 exceeds stock for book #201/)
const { data } = await GET`/admin/Books/201/stock/$value`
expect(data).to.equal(2)
})

6
test/events.cds Normal file
View File

@@ -0,0 +1,6 @@
service Sue {
@cds.persistence.skip
entity Foo { key ID:Integer; title:String; status:String(1); }
entity Bar { key ID:Integer; foo: Association to Foo }
event Foo.changed : projection on Foo { ID, status }
}

View File

@@ -1,11 +1,10 @@
const {expect} = require('../test')
const cds = require('@sap/cds/lib')
// monkey patching older releases:
if (!cds.compile.cdl) cds.compile.cdl = cds.parse
const { parse:cdr } = cds.ql
const model = cds.compile.cdl (`
// should become cds.compile(...) when cds5 is released
const model = cds.compile.to.csn (`
entity Categories {
key ID : Integer;
name : String;

View File

@@ -1,6 +1,2 @@
const test = require('@sap/cds/lib/utils/tests').in(__dirname,'..')
module.exports = Object.assign(test,{run:test})
// REVISIT: With upcoming release of @sap/cds this should become:
// module.exports = require('@sap/cds/tests').in(__dirname,'..')
const cds = require('@sap/cds')
module.exports = cds.test.in(__dirname,'..')

View File

@@ -43,7 +43,7 @@ describe('Localized Data', () => {
{ title: 'Jane Eyre', author: 'Charlotte Brontë', currency: { name: 'Pfund' } },
{ title: 'The Raven', author: 'Edgar Allen Poe', currency: { name: 'US-Dollar' } },
{ title: 'Eleonora', author: 'Edgar Allen Poe', currency: { name: 'US-Dollar' } },
{ title: 'Catweazle', author: 'Richard Carpenter', currency: { name: 'Euro' } },
{ title: 'Catweazle', author: 'Richard Carpenter', currency: { name: 'Yen' } },
])
})
@@ -85,7 +85,7 @@ describe('Localized Data', () => {
{ title: 'Jane Eyre', currency: { name: 'British Pound' } },
{ title: 'The Raven', currency: { name: 'US Dollar' } },
{ title: 'Eleonora', currency: { name: 'US Dollar' } },
{ title: 'Catweazle', currency: { name: 'Euro' } },
{ title: 'Catweazle', currency: { name: 'Yen' } },
])
})
})

View File

@@ -20,11 +20,11 @@ describe('Messaging', ()=>{
let N=0, received=[], M=0
it ('should add messaging event handlers', ()=>{
srv.on('reviewed', (msg,next)=> { received.push(msg); return next() })
srv.on('reviewed', (msg)=> received.push(msg))
})
it ('should add more messaging event handlers', ()=>{
srv.on('reviewed', (_,next)=> { ++M; return next() })
srv.on('reviewed', ()=> ++M)
})
it ('should add review', async ()=>{