Compare commits

...

107 Commits

Author SHA1 Message Date
Christian Georgi
7732763a75 How to run embedded OrderService?
cds deploy excludes its definitions if `OrdersService` is marked
as external.  How would we tell it to include them in the embedded case?
2021-07-13 17:02:43 +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
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 Hutzel
28a7c16895 Merge branch 'master' into using-package-lock.json 2021-06-09 07:38:42 +02:00
Christian Georgi
c1141e6c87 Remove trailing comma 2021-06-08 16:19:34 +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
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
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
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
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
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
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
51ee6d167c added comment 2021-03-25 01:08:14 +01:00
Marc Becker
049345a39c Replace @assert.enum with @assert.range
@assert.enum has been deprecated since April 2020: https://cap.cloud.sap/docs/releases/apr20#value-range-annotations-for-odata
@assert.range is the correct annotation to use: https://cap.cloud.sap/docs/guides/generic#assertrange-check-constraints
2021-03-23 15:44:49 +01:00
Daniel
a7e0cf61a3 bump versions 2021-03-23 13:09:05 +01:00
Christian Georgi
1b0462f52e Merge pull request #207 from SAP-samples/avoid-index.cds
Avoid central index.cds files to ease extensibility
2021-03-23 13:08:31 +01:00
Daniel
2bae86079e app/-index -> app/services.cds 2021-03-23 11:14:05 +01:00
Christian Georgi
dcab44ab68 Hide license files 2021-03-17 09:06:48 +01:00
Brian Bernard
9f0629eef1 Fixed REUSE/badge 2021-03-12 11:02:35 -08:00
Brian Bernard
f6c938457f License updates
Added folder, removed top level License file
2021-03-12 10:12:55 -08:00
Daniel
647ac72588 Prepare for compiler v2 2021-03-03 10:39:32 +01:00
Daniel
2975038696 Avoid central index.cds files to ease extensibility 2021-03-02 10:37:02 +01:00
Iwona Hahn
861094cc83 Merge pull request #206 from SAP-samples/promote-code-tour
promote code tours
2021-03-02 09:15:19 +01:00
Christian Georgi
2368913612 Dummy change 2021-03-02 09:09:51 +01:00
Iwona Hahn
22ece2e586 typo 2021-03-01 20:44:05 +01:00
Iwona Hahn
ae788414f8 promote code tours 2021-03-01 20:42:43 +01:00
Iwona Hahn
ebc7017ded UA review 2021-03-01 20:42:23 +01:00
Christian Georgi
876ff94782 Merge pull request #205 from SAP-samples/db-codetour
Codetour for DB functions
2021-03-01 17:29:31 +01:00
Iwona Hahn
76d1b1865b minor edits 2021-03-01 17:24:53 +01:00
Christian Georgi
85dd8558f4 Some more explanation around file structure 2021-03-01 14:38:00 +01:00
Iwona Hahn
d0d95f3c42 first review, minor edits 2021-02-26 17:14:49 +01:00
Christian Georgi
c46a82d0f8 Codetour for DB functions 2021-02-26 16:40:57 +01:00
Iwona Hahn
7654012292 cosmetics codetour 2021-02-26 09:29:51 +01:00
Christian Georgi
65c8c82f74 Showcase how to integrate DB-specific functions (#201)
Augment `AdminService.Author` by 2 fields `age` and `lifetime` whose
values are computed by SQLite and HANA-specific functions.

Don't do this in bookshop directly, to keep it simple.  Instead, use
the fiori module, which also allows to integrate the fields in the UI.

Co-authored-by: Daniel <daniel.hutzel@sap.com>
2021-02-24 17:26:29 +01:00
Daniel
d368eb2ff5 fixed dependency -> never use "latest" 2021-02-19 16:34:15 +01:00
Christian Georgi
3cbb199870 Recommend CodeTours extension 2021-02-19 12:55:35 +01:00
Daniel
b51a08bf4e fixed .registry 2021-02-19 12:40:26 +01:00
Iwona Hahn
b31efc8083 minor edits 2021-02-17 14:07:42 +01:00
Christian Georgi
6669b983b1 First code tour with an overview of the sample repo 2021-02-17 14:07:42 +01:00
Daniel
b6e5a2fced Removed demo 2021-02-17 12:32:27 +01:00
Daniel
6de09e0940 Make usage of @capire/common optional 2021-02-17 12:32:27 +01:00
Daniel
b6f3914d79 Minor cleanup to npm registry mock 2021-02-17 12:32:27 +01:00
Daniel
28402c58b3 Simplified redirects to imported apps 2021-02-17 12:32:27 +01:00
Daniel
77de0e445e demo -> done 2021-02-17 12:32:27 +01:00
Daniel
a037d92c97 demo for bhagat 2021-02-17 12:32:27 +01:00
Christian Georgi
5e829ae7bb Merge branch 'origin/master' into openapi 2021-02-17 09:50:04 +01:00
Christian Georgi
b5031588ce Remove Node 10 for now 2021-02-09 17:15:24 +01:00
Christian Georgi
85319d9e8d Pin sqlite3 to 5.0.0
5.0.1 is broken at the moment
2021-02-09 17:15:24 +01:00
Iwona Hahn
39872200ae Update README.md 2021-02-01 14:54:55 +01:00
Iwona Hahn
6a4af929f1 minor updates 2021-01-27 08:49:33 +01:00
Christian Georgi
5b966c503c Add hint for authentication
So that fewer users stumble over the basic authentication popup.
2021-01-18 14:40:24 +01:00
Christian Georgi
75628b6096 Use latest UI5 again 2021-01-18 14:31:57 +01:00
Daniel
c12e516f5d Prep for upcomming releases 2021-01-13 16:30:40 +01:00
Marius Obert
01073fd6a5 Restructure sentence 2021-01-11 10:54:59 +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
55 changed files with 32866 additions and 416 deletions

View File

@@ -16,7 +16,7 @@ jobs:
strategy: strategy:
matrix: matrix:
node-version: [10.x, 12.x, 14.x] node-version: [12.x, 14.x]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2
@@ -24,5 +24,5 @@ jobs:
uses: actions/setup-node@v1 uses: actions/setup-node@v1
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
- run: npm install - run: npm ci
- run: npm test - run: npm test

2
.gitignore vendored
View File

@@ -15,3 +15,5 @@ default-env.json
packages/messageBox packages/messageBox
reviews/msg-box reviews/msg-box
reviews/db/test.db 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/

1
.registry/.gitignore vendored Normal file
View File

@@ -0,0 +1 @@
*.tgz

View File

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

109
.tours/db-native.tour Normal file
View File

@@ -0,0 +1,109 @@
{
"$schema": "https://aka.ms/codetour-schema",
"title": "Database Functions",
"steps": [
{
"title": "Introduction",
"description": "### Database Functions in CDS Models\n\nIn this tour, you'll learn how to add database-specific functions to CDS models in your application."
},
{
"file": "bookshop/db/schema.cds",
"description": "#### Basic Schema\n\nWe want to add two fields to the `Authors` entity, one for the author's age and one for the span of years that she or he lived.\n\nThese two fields can be computed out of the existing `dateOfBirth` and `dateOfDeath` fields.",
"selection": {
"start": {
"line": 19,
"character": 1
},
"end": {
"line": 21,
"character": 1
}
},
"title": "Base fields in Author"
},
{
"file": "bookshop/srv/admin-service.cds",
"description": "This is how the `Authors` entity gets exposed in an OData or REST service.\n\nIn the next step, you'll see how we extend this projection.",
"selection": {
"start": {
"line": 4,
"character": 1
},
"end": {
"line": 5,
"character": 1
}
},
"title": "Authors service"
},
{
"file": "fiori/db/sqlite/index.cds",
"description": "#### SQLite Implementation\n\nHere's the first implementation for SQLite. It computes the two fields `age` and `lifetime` through SQLite's [strftime](https://sqlite.org/lang_datefunc.html) function.\n\nThrough the [`extend projection`](https://cap.cloud.sap/docs/cds/cdl#extend-view) clause you can add additional fields to projection entities. These are deployed as database views, which is why we can integrate the database functions in the first place.\n",
"selection": {
"start": {
"line": 7,
"character": 1
},
"end": {
"line": 11,
"character": 1
}
},
"title": "SQLite implementation"
},
{
"file": "fiori/db/hana/index.cds",
"description": "#### SAP HANA Implementation\n\nThis is the second implementation for SAP HANA. It computes the same two fields `age` and `lifetime` through the [YEARS_BETWEEN](https://help.sap.com/viewer/7c78579ce9b14a669c1f3295b0d8ca16/Cloud/en-US/7c0d2c161ea34def86de3f5eadd6a0af.html) and [YEAR](https://help.sap.com/viewer/7c78579ce9b14a669c1f3295b0d8ca16/Cloud/en-US/20f5fac6751910148dabd3c6821f907d.html) functions of SAP HANA.\n\n#### File Layout and Code Structure\n\nNote the path of the `.cds` file we are in: it's in a subfolder of `db`, so that it's _not_ automatically picked up when we start the application. The same is true for the SQLite implementation: it's in a separate `db/sqlite/` folder as well. In the next step, you'll see how these files are loaded.\n\nAlso, we choose to implement all of that as an extension of the original bookshop here in the _fiori_ package. See the first [CAP Samples] code tour for more details on the different packages of this repository.",
"selection": {
"start": {
"line": 7,
"character": 1
},
"end": {
"line": 11,
"character": 1
}
},
"title": "SAP HANA implementation"
},
{
"file": "fiori/package.json",
"description": "#### Configuration\n\nThe `cds` section in `package.json` is a place to configure which of the `db/sqlite` and `db/hana` folders are used for which database.\nWe use [Node.js profiles](https://cap.cloud.sap/docs/node.js/cds-env#profiles) to separate the configuration.\nIn the `development` profile, you can see that `db/sqlite` is set as the model, while the `db/hana` folder is configured in the `production` profile.",
"line": 17,
"title": "Configuration"
},
{
"file": "fiori/package.json",
"description": "#### Run with SQLite\n\nTo run with `development` and an in-memory SQLite database, you don't need to do anything special, because it's activated by default. Just run:\n\n>> cds watch fiori\n\nThen open [http://localhost:4004/admin/Authors](http://localhost:4004/admin/Authors) to see the two new fields.\n",
"line": 28,
"title": "Run with SQLite"
},
{
"file": "fiori/package.json",
"description": "#### Deploy the CDS Model to SAP HANA\n\nTo 'activate' SAP HANA through the `production` profile, you can use the global `--production` flag:\n\n>> cd fiori; cds deploy --to hana --production\n\n[Learn more about SAP HANA deployment](https://cap.cloud.sap/docs/guides/databases#get-hana)\n\n#### Run the Application\n\n>> cd fiori; cds watch --production\n\nThe service on [http://localhost:4004/admin/Authors](http://localhost:4004/admin/Authors) is the same as before, but this time the `Authors` entity is backed by a database view with an SAP HANA function.\n\n#### More\n\nIf you don't see data, you can add some in the next step.",
"line": 31,
"title": "Run with SAP HANA"
},
{
"file": "fiori/test/requests.http",
"description": "### Add More Data\n\nOptionally you can add some `Authors` data by clicking on the _Send Request_ link (provided by the [REST client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension).",
"line": 68,
"selection": {
"start": {
"line": 67,
"character": 1
},
"end": {
"line": 73,
"character": 1
}
},
"title": "Add Data"
},
{
"title": "Wrap-up",
"description": "### Summary\n\nThat's it! You have seen: \n- How to integrate database-specific functions in a CDS model.\n- How to switch between the two implementations for SQLite and SAP HANA."
}
],
"ref": "master"
}

136
.tours/samples.tour Normal file
View File

@@ -0,0 +1,136 @@
{
"$schema": "https://aka.ms/codetour-schema",
"title": "CAP Samples",
"steps": [
{
"title": "Welcome",
"file": "README.md",
"description": "### Welcome to CAP Samples!\n\nThis tour leads you through a collection of samples for the [SAP Cloud Application Programming Model (CAP)](https://cap.cloud.sap).\nYou will learn which features of the programming model are demonstrated in which sample.\n\nLet's start!",
"line": 2,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 3,
"character": 108
}
}
},
{
"file": "hello/world.cds",
"description": "### Hello World!\n\nThis is 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).",
"line": 2,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 4,
"character": 1
}
},
"title": "Hello World!"
},
{
"file": "bookshop/db/schema.cds",
"description": "### A Bookshop!\n\nIntroduces:\n- [Project Setup](https://cap.cloud.sap/docs/get-started/) and [Layouts](https://cap.cloud.sap/docs/get-started/projects)\n- [Domain Modeling](https://cap.cloud.sap/docs/guides/domain-models)\n- [Defining Services](https://cap.cloud.sap/docs/guides/providing-services)\n- [Generic Providers](https://cap.cloud.sap/docs/guides/generic-providers)\n- [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl)\n- [Using Databases](https://cap.cloud.sap/docs/guides/databases)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 32,
"character": 1
}
},
"title": "Bookshop"
},
{
"file": "common/index.cds",
"description": "### Extend and Reuse\n\nShowcases how to extend [@sap/cds/common](https://cap.cloud.sap/docs/cds/common) thereby covering:\n- Building [extension packages](https://cap.cloud.sap/docs/guides/domain-models#aspects-extensibility)\n- Providing [reuse packages](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content)\n- [Verticalization](https://cap.cloud.sap/docs/cds/common#adapting-to-your-needs)\n- Using [Aspects](https://cap.cloud.sap/docs/cds/cdl#aspects)\n- Used in the [fiori app sample](#fiori)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 46,
"character": 1
}
},
"title": "Common"
},
{
"file": "orders/db/schema.cds",
"description": "### Compositions and Serving Documents\n\nA standalone orders management service, demonstrating:\n- Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with\n- [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 27,
"character": 1
}
},
"title": "Orders"
},
{
"file": "reviews/db/schema.cds",
"description": "### More Modularity\n\nShows how to implement a modular service to manage product reviews, including:\n- Consuming other services synchronously and asynchronously\n- Serving requests synchronously\n- Emitting events asynchronously\n- Grow as you go, with:\n- Mocking app services\n- Running service meshes\n- Late-cut Micro Services\n- As well as managed data, input validations, and authorization\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 39,
"character": 1
}
},
"title": "Reviews"
},
{
"file": "fiori/app/index.cds",
"description": "### Annotations for SAP Fiori Elements\n\nA [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages:\n - [@capire/bookshop](bookshop)\n - [@capire/reviews](reviews)\n - [@capire/orders](orders)\n - [@capire/common](common)\n\n[Adds a SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to:\n - [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files\n - Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)\n - Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)\n - Serving SAP Fiori apps locally\n\n[The Vue.js app](bookshop/app/vue) imported from bookshop is served as well.\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 13,
"character": 1
}
},
"title": "Fiori"
},
{
"file": "package.json",
"description": "### All-in-one Monorepo\n\nEach sample sub directory essentially is a standard npm package, some with standard npm dependencies to other samples. The root folder's [package.json](package.json) has local links to the sub folders, such that an `npm install` populates a local `node_modules` folder acts like a local npm registry to the individual sample packages.\n",
"line": 8,
"selection": {
"start": {
"line": 8,
"character": 1
},
"end": {
"line": 15,
"character": 1
}
},
"title": "Packages"
}
],
"isPrimary": true,
"description": "Overview of CAP Samples for Node.js"
}

View File

@@ -4,14 +4,15 @@
// 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": [
"SAPSE.vscode-cds", "sapse.vscode-cds",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"mechatroner.rainbow-csv", "mechatroner.rainbow-csv",
"humao.rest-client", "humao.rest-client",
"alexcvzz.vscode-sqlite", "alexcvzz.vscode-sqlite",
"hbenl.vscode-mocha-test-adapter", "hbenl.vscode-mocha-test-adapter",
"sdras.night-owl" "sdras.night-owl",
"vsls-contrib.codetour"
], ],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace. // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [ "unwantedRecommendations": [

37
.vscode/launch.json vendored
View File

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

13
.vscode/settings.json vendored
View File

@@ -1,6 +1,17 @@
{ {
"files.exclude": { "files.exclude": {
".reuse/**": true,
"**/.gitignore": true, "**/.gitignore": true,
"**/.vscode": 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/**"
]
} }
} }

208
LICENSE
View File

@@ -1,208 +0,0 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction, and distribution
as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct
or indirect, to cause the direction or management of such entity, whether
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
of the outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation
or translation of a Source form, including but not limited to compiled object
code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that
is included in or attached to the work (an example is provided in the Appendix
below).
"Derivative Works" shall mean any work, whether in Source or Object form,
that is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative
Works shall not include works that remain separable from, or merely link (or
bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative
Works thereof, that is intentionally submitted to Licensor for inclusion in
the Work by the copyright owner or by an individual or Legal Entity authorized
to submit on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication
sent to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor
for the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently incorporated
within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
Derivative Works of, publicly display, publicly perform, sublicense, and distribute
the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License,
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section) patent
license to make, have made, use, offer to sell, sell, import, and otherwise
transfer the Work, where such license applies only to those patent claims
licensable by such Contributor that are necessarily infringed by their Contribution(s)
alone or by combination of their Contribution(s) with the Work to which such
Contribution(s) was submitted. If You institute patent litigation against
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
that the Work or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses granted to You
under this License for that Work shall terminate as of the date such litigation
is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and
in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy
of this License; and
(b) You must cause any modified files to carry prominent notices stating that
You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source
form of the Work, excluding those notices that do not pertain to any part
of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable copy
of the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at least
one of the following places: within a NOTICE text file distributed as part
of the Derivative Works; within the Source form or documentation, if provided
along with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works
that You distribute, alongside or as an addendum to the NOTICE text from the
Work, provided that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction,
or distribution of Your modifications, or for any such Derivative Works as
a whole, provided Your use, reproduction, and distribution of the Work otherwise
complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without
any additional terms or conditions. Notwithstanding the above, nothing herein
shall supersede or modify the terms of any separate license agreement you
may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
in writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
of using or redistributing the Work and assume any risks associated with Your
exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether
in tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to
in writing, shall any Contributor be liable to You for damages, including
any direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability
to use the Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the possibility
of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work
or Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such obligations,
You may act only on Your own behalf and on Your sole responsibility, not on
behalf of any other Contributor, and only if You agree to indemnify, defend,
and hold each Contributor harmless for any liability incurred by, or claims
asserted against, such Contributor by reason of your accepting any such warranty
or additional liability. END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own identifying
information. (Don't include the brackets!) The text should be enclosed in
the appropriate comment syntax for the file format. We also recommend that
a file or class name and description of purpose be included on the same "printed
page" as the copyright notice for easier identification within third-party
archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

201
LICENSE.txt Normal file
View File

@@ -0,0 +1,201 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@@ -5,19 +5,19 @@ Find here a collection of samples for the [SAP Cloud Application Programming Mod
![](https://github.com/SAP-samples/cloud-cap-samples/workflows/CI/badge.svg) ![](https://github.com/SAP-samples/cloud-cap-samples/workflows/CI/badge.svg)
[![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-cap-samples)](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples) [![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-cap-samples)](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples)
### Preliminaries ### Preliminaries
1. [Install @sap/cds-dk](https://cap.cloud.sap/docs/get-started/) globally as documented in [capire](https://cap.cloud.sap) 1. Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/) globally:
```sh ```sh
npm i -g @sap/cds-dk npm i -g @sap/cds-dk
``` ```
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/in-vscode)
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/tools#vscode)
### Download ### Download
Clone this repo as shown below, if you have [git](https://git-scm.com/downloads) installed, If you've [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/master.zip).
otherwise [download as zip file](archive/master.zip).
```sh ```sh
git clone https://github.com/sap-samples/cloud-cap-samples samples git clone https://github.com/sap-samples/cloud-cap-samples samples
@@ -42,9 +42,12 @@ cds watch bookshop
After that open this link in your browser: [http://localhost:4004](http://localhost:4004) After that open this link in your browser: [http://localhost:4004](http://localhost:4004)
When asked to log in, type `alice` as user and leave the password field blank, which is the [default user](https://cap.cloud.sap/docs/node.js/authentication#mocked).
### Testing ### Testing
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example: Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example:
```sh ```sh
npx jest npx jest
``` ```
@@ -53,7 +56,7 @@ npx jest
### Serve `npm` ### Serve `npm`
We've simple npm registry mock included which allows you to do an `npm install @capire/<package>` anywhere locally. Use it as follows: We've included a simple npm registry mock, which allows you to do an `npm install @capire/<package>` locally. Use it as follows:
1. Start the @capire registry: 1. Start the @capire registry:
```sh ```sh
@@ -61,18 +64,23 @@ npm run registry
``` ```
> While running this will have `@capire:registry=http://localhost:4444` set with npmrc. > While running this will have `@capire:registry=http://localhost:4444` set with npmrc.
2. Install one of the @capire packages wherever you like, e.g.: 2. Install one of the @capire packages wherever you like, for example:
```sh ```sh
npm add @capire/common @capire/bookshop npm add @capire/common @capire/bookshop
``` ```
## Code Tours
Take one of the [guided tours](.tours) in VS Code through our CAP samples and learn which CAP features are showcased by the different parts of the repository. Just install the [CodeTour extension](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) for VS Code. We'll add more code tours in the future. Stay tuned!
## Get Support ## Get Support
Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br> Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br>
In case you have a question, find a bug, or otherwise need support, please use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce). In case you've a question, find a bug, or otherwise need support, use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce) to get more visibility.
## License ## License
Copyright (c) 2020 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.

View File

@@ -35,7 +35,7 @@ const books = new Vue ({
try { try {
const res = await POST(`/submitOrder`, { amount, book: book.ID }) const res = await POST(`/submitOrder`, { amount, book: book.ID })
book.stock = res.data.stock 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) { } catch (e) {
books.order = { amount, failed: e.response.data.error.message } books.order = { amount, failed: e.response.data.error.message }
} }

View File

@@ -36,7 +36,7 @@
<td class="rating-stars"> <td class="rating-stars">
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }} {{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }}
</td> </td>
<td>{{ book.currency.symbol }} {{ book.price }}</td> <td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td>
</tr> </tr>
</table> </table>

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 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 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 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.", "description": "A simple self-contained bookshop service.",
"dependencies": { "dependencies": {
"@capire/common": "*", "@capire/common": "*",
"@sap/cds": "^4", "@sap/cds": "^5.0.4",
"express": "^4.17.1", "express": "^4.17.1",
"passport": "0.4.1" "passport": "0.4.1"
}, },

View File

@@ -5,10 +5,10 @@ This stand-alone sample introduces the essential tasks in the development of CAP
## Hypothetical Use Cases ## Hypothetical Use Cases
1. Build a service that allows to browse _Books_ and _Authors_. 1. Build a service that allows to browse _Books_ and _Authors_.
2. Books have assigned _Genres_ which are organized hierarchically. 2. Books have assigned _Genres_, which are organized hierarchically.
3. All users may browse books without login. 3. All users may browse books without login.
4. All entries are maintained by Administrators. 4. All entries are maintained by Administrators.
5. End users may order books (the actual order mgmt being out of scope) 5. End users may order books (the actual order mgmt being out of scope).
## Running the Sample ## Running the Sample
@@ -20,12 +20,12 @@ npm run watch
| Links to capire | Sample files / folders | | Links to capire | Sample files / folders |
| --------------------------------------------------------------------------------------------------------- | ------------------------------------ | | --------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| [Project Setup and Layouts](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./`](./) | | [Project Setup & Layouts](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./`](./) |
| [Defining Domain Models](https://cap.cloud.sap/docs/guides/domain-models) | [`./db/schema.cds`](./db/schema.cds) | | [Domain Modeling with CDS](https://cap.cloud.sap/docs/guides/domain-models) | [`./db/schema.cds`](./db/schema.cds) |
| [Defining Services](https://cap.cloud.sap/docs/guides/providing-services) | [`./srv/*.cds`](./srv) | | [Defining Services](https://cap.cloud.sap/docs/guides/services#defining-services) | [`./srv/*.cds`](./srv) |
| [Single-purposed Services](https://cap.cloud.sap/docs/guides/providing-services#single-purposed-services) | [`./srv/*.cds`](./srv) | | [Single-purposed Services](https://cap.cloud.sap/docs/guides/services#single-purposed-services) | [`./srv/*.cds`](./srv) |
| [Generic Providers](https://cap.cloud.sap/docs/guides/providing-services) | http://localhost:4004 | | [Providing & Consuming Providers](https://cap.cloud.sap/docs/guides/providing-services) | http://localhost:4004 |
| Using Databases | [`./db/data/*.csv`](./db/data) | | [Using Databases](https://cap.cloud.sap/docs/guides/databases) | [`./db/data/*.csv`](./db/data) |
| [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) | [`./srv/*.js`](./srv) | | [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) | [`./srv/*.js`](./srv) |
| Adding Tests | [`./test`](./test) | | Adding Tests | [`./test`](./test) |
| [Sharing for Reuse](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./index.cds`](./index.cds) | | [Sharing for Reuse](https://cap.cloud.sap/docs/guides/reuse-and-compose) | [`./index.cds`](./index.cds) |

View File

@@ -1,13 +1,15 @@
using { sap.capire.bookshop as my } from '../db/schema'; using { sap.capire.bookshop as my } from '../db/schema';
service CatalogService @(path:'/browse') { service CatalogService @(path:'/browse') {
@readonly entity Books as SELECT from my.Books { *, /** For displaying lists of Books */
@readonly entity ListOfBooks as projection on Books
excluding { descr };
/** For display in details pages */
@readonly entity Books as projection on my.Books { *,
author.name as author author.name as author
} excluding { createdBy, modifiedBy }; } excluding { createdBy, modifiedBy };
@readonly entity ListOfBooks as SELECT from Books
excluding { descr };
@requires: 'authenticated-user' @requires: 'authenticated-user'
action submitOrder ( book: Books:ID, amount: Integer ) returns { stock: Integer }; action submitOrder ( book: Books:ID, amount: Integer ) returns { stock: Integer };
event OrderedBook : { book: Books:ID; amount: Integer; buyer: String }; 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 // Reduce stock of ordered books if available stock suffices
this.on ('submitOrder', async req => { this.on ('submitOrder', async req => {
const {book,amount} = req.data, tx = cds.tx(req) const {book,amount} = req.data
let {stock} = await tx.read('stock').from(Books,book) let {stock} = await SELECT `stock` .from (Books,book)
if (stock >= amount) { 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 }) await this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
return { stock } return { stock }
} }
@@ -16,10 +16,8 @@ class CatalogService extends cds.ApplicationService { init(){
}) })
// Add some discount for overstocked books // Add some discount for overstocked books
this.after ('READ','Books', each => { this.after ('READ','ListOfBooks', each => {
if (each.stock > 111) { if (each.stock > 111) each.title += ` -- 11% discount!`
each.title += ` -- 11% discount!`
}
}) })
return super.init() return super.init()

View File

@@ -5,9 +5,11 @@ CAD;de;Kanadischer Dollar;Kanadischer Dollar
AUD;de;Australischer Dollar;Australischer Dollar AUD;de;Australischer Dollar;Australischer Dollar
GBP;de;Pfund;Britische Pfund GBP;de;Pfund;Britische Pfund
ILS;de;Schekel;Israelische Schekel ILS;de;Schekel;Israelische Schekel
JPY;de;Yen;Japanische Yen
EUR;fr;euro;de la Zone euro EUR;fr;euro;de la Zone euro
USD;fr;dollar;dollar des États-Unis USD;fr;dollar;dollar des États-Unis
CAD;fr;dollar canadien;dollar canadien CAD;fr;dollar canadien;dollar canadien
AUD;fr;dollar australien;dollar australien AUD;fr;dollar australien;dollar australien
GBP;fr;livre sterling;pound sterling GBP;fr;livre sterling;pound sterling
ILS;fr;Shekel;shekel israelien 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

@@ -3,6 +3,6 @@
"description": "Provides a pre-built extension package for std @sap/cds/common", "description": "Provides a pre-built extension package for std @sap/cds/common",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@sap/cds": "latest" "@sap/cds": "*"
} }
} }

View File

@@ -11,6 +11,7 @@ DateOfBirth = Date of Birth
DateOfDeath = Date of Death DateOfDeath = Date of Death
PlaceOfBirth = Place of Birth PlaceOfBirth = Place of Birth
PlaceOfDeath = Place of Death PlaceOfDeath = Place of Death
Age = Age
Authors = Authors Authors = Authors
Order = Order Order = Order
Orders = Orders Orders = Orders

View File

@@ -6,6 +6,7 @@ Authors = Autoren
Author = Autor Author = Autor
AuthorID = ID des Autors AuthorID = ID des Autors
AuthorName = Name des Autors AuthorName = Name des Autors
Age = Alter
Name = Name Name = Name
Stock = Bestand Stock = Bestand
Order = Bestellung Order = Bestellung

View File

@@ -40,8 +40,7 @@
</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://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" --> <script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/1.78.6/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_fiori_3" data-sap-ui-theme="sap_fiori_3"

View File

@@ -1,4 +1,4 @@
using AdminService from '@capire/bookshop'; using { AdminService, sap.capire.bookshop } from '../../db/schema';
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
@@ -39,6 +39,27 @@ annotate AdminService.Books with @(
} }
); );
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}'},
]
},
}
);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////
@@ -49,7 +70,7 @@ annotate AdminService.Books with @(
annotate sap.capire.bookshop.Books with @fiori.draft.enabled; annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
annotate AdminService.Books with @odata.draft.enabled; annotate AdminService.Books with @odata.draft.enabled;
annotate AdminService.Books_texts with @( annotate AdminService.Books.texts with @(
UI: { UI: {
Identification: [{Value:title}], Identification: [{Value:title}],
SelectionFields: [ locale, title ], SelectionFields: [ locale, title ],
@@ -62,11 +83,12 @@ annotate AdminService.Books_texts with @(
); );
// Add Value Help for Locales // Add Value Help for Locales
annotate AdminService.Books_texts { annotate AdminService.Books.texts {
locale @ValueList:{entity:'Languages',type:#fixed} 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'; using { sap } from '@sap/cds/common';
extend service AdminService { extend service AdminService {
entity Languages as projection on sap.common.Languages; entity Languages as projection on sap.common.Languages;
entity Books.texts as projection on bookshop.Books.texts;
} }

View File

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

View File

@@ -54,7 +54,7 @@ annotate my.Books with {
title @title:'{i18n>Title}'; title @title:'{i18n>Title}';
genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly }; genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
author @title:'{i18n>Author}' @Common: { Text: author.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}'; stock @title:'{i18n>Stock}';
descr @UI.MultiLineText; descr @UI.MultiLineText;
} }

View File

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

10
fiori/db/hana/index.cds Normal file
View File

@@ -0,0 +1,10 @@
//
// Add Author.age and .lifetime with a DB-specific function
//
using { AdminService } from '../schema';
extend projection AdminService.Authors with {
YEARS_BETWEEN(dateOfBirth, dateOfDeath) as age: Integer,
YEAR(dateOfBirth) || ' ' || YEAR(dateOfDeath) as lifetime : String
}

8
fiori/db/schema.cds Normal file
View File

@@ -0,0 +1,8 @@
using { sap.capire.bookshop } from '@capire/bookshop';
// Forward-declare calculated fields to be filled in database-specific ways
// TODO find a better way to have 'default' fields that still can be overwritten.
extend bookshop.Authors with {
virtual age: Integer;
virtual lifetime: String;
}

10
fiori/db/sqlite/index.cds Normal file
View File

@@ -0,0 +1,10 @@
//
// Add Author.age and .lifetime with a DB-specific function
//
using { AdminService } from '../schema';
extend projection AdminService.Authors with {
strftime('%Y',dateOfDeath)-strftime('%Y',dateOfBirth) as age: Integer,
strftime('%Y',dateOfBirth) || ' ' || strftime('%Y',dateOfDeath) as lifetime : String
}

View File

@@ -6,24 +6,38 @@
"@capire/reviews": "*", "@capire/reviews": "*",
"@capire/orders": "*", "@capire/orders": "*",
"@capire/common": "*", "@capire/common": "*",
"@sap/cds": "^4", "@sap/cds": "^5",
"express": "^4.17.1", "express": "^4.17.1",
"passport": "0.4.1" "passport": "^0.4.1"
}, },
"scripts": { "scripts": {
"start": "cds run --in-memory?", "start": "cds run --in-memory?",
"watch": "cds watch" "watch": "cds watch"
}, },
"cds": { "cds": {
"requires": { "hana": {
"ReviewsService": { "deploy-format": "hdbtable"
"kind": "odata", "model": "@capire/reviews"
}, },
"OrdersService": { "requires": {
"kind": "odata", "model": "@capire/orders" "auth": {
"strategy": "dummy"
},
"ReviewsService": {
"kind": "odata",
"model": "@capire/reviews"
},
"--> OrdersService": {
"kind": "odata",
"model": "@capire/orders"
}, },
"db": { "db": {
"kind": "sql" "kind": "sql",
"[development]": {
"model": "db/sqlite"
},
"[production]": {
"model": "db/hana"
}
} }
} }
} }

View File

@@ -1,19 +1,23 @@
const express = require ('express')
const cds = require ('@sap/cds') const cds = require ('@sap/cds')
module.exports = cds.server
cds.once('bootstrap',(app)=>{ cds.once('bootstrap',(app)=>{
const {dirname} = require ('path') app.use ('/orders/webapp', _from('@capire/orders/app/orders/webapp/manifest.json'))
// serving the orders app imported from @capire/orders app.use ('/bookshop', _from('@capire/bookshop/app/vue/index.html'))
const orders_app = dirname (require.resolve('@capire/orders/app/orders/webapp/manifest.json')) app.use ('/reviews', _from('@capire/reviews/app/vue/index.html'))
app.use ('/orders/webapp', express.static(orders_app))
// serving the vue.js app imported from @capire/bookshop
const bookshop_app = dirname (require.resolve('@capire/bookshop/app/vue/index.html'))
app.use ('/vue/bookshop', express.static(bookshop_app))
// serving the vue.js app imported from @capire/reviews
const reviews_app = dirname (require.resolve('@capire/reviews/app/vue/index.html'))
app.use ('/vue/reviews', express.static(reviews_app))
}) })
cds.once('served', require('./srv/mashup')) cds.once('served', require('./srv/mashup'))
module.exports = cds.server // Swagger UI - see https://cap.cloud.sap/docs/advanced/openapi
if (process.env.NODE_ENV !== 'production') {
const cds_swagger = require ('cds-swagger-ui-express')
cds.once ('bootstrap', app => app.use (cds_swagger()) )
}
// -----------------------------------------------------------------------
// 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)))

View File

@@ -38,7 +38,11 @@ GET {{bookshop}}/browse/Books(201)?
&$select=ID,title,rating &$select=ID,title,rating
&$expand=reviews &$expand=reviews
###
GET {{bookshop}}/browse/Books?
&$select=title,author&$expand=currency
Accept-Language: de
################################################# #################################################
# #
@@ -63,3 +67,15 @@ Content-Type: application/json
### Get active order ### Get active order
GET {{bookshop}}/orders/Orders(ID={{newOrderID}},IsActiveEntity=true) GET {{bookshop}}/orders/Orders(ID={{newOrderID}},IsActiveEntity=true)
### Create author
POST {{bookshop}}/admin/Authors
Content-Type: application/json
Authorization: Basic alice:
{
"ID": 200,
"name": "William Shakespeare",
"dateOfBirth": "1564-04-26",
"dateOfDeath": "1616-04-23"
}

View File

@@ -2,6 +2,28 @@
"name": "@capire/hello-world", "name": "@capire/hello-world",
"version": "1.0.0", "version": "1.0.0",
"scripts": { "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; up_ : Association to Orders;
product : Association to Products @assert.integrity:false; // REVISIT: this is a temporary workaround for a glitch in cds-runtime product : Association to Products @assert.integrity:false; // REVISIT: this is a temporary workaround for a glitch in cds-runtime
amount : Integer; amount : Integer;
title : String; title : String; //> intentionally replicated as snapshot from product.title
price : Double; price : Double;
} }
@@ -21,6 +21,3 @@ entity Orders_Items {
entity Products @(cds.persistence.skip:'always') { entity Products @(cds.persistence.skip:'always') {
key ID : String; key ID : String;
} }
// Activate extension package
using from '@capire/common';

View File

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

32038
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

@@ -2,20 +2,23 @@
## Run all-in-one ## 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 ```sh
npm run bookshop npm run fiori
``` ```
## Run as separate services ## Run as Separate Services
Open two terminal windows. In the first one start the reviews service stand-alone:
Open two terminal windows, and in the first one start the reviews service stand-alone:
```sh ```sh
npm run reviews-service npm run reviews-service
``` ```
In the the second one start the bookshop: In the second one start the `fiori` app:
```sh ```sh
npm run bookshop npm run fiori
``` ```

View File

@@ -17,7 +17,7 @@ service ReviewsService {
annotate Reviews with { annotate Reviews with {
subject @mandatory; subject @mandatory;
title @mandatory; title @mandatory;
rating @assert.enum; rating @assert.range;
} }
} }

View File

@@ -1,19 +1,20 @@
# Overview of Samples # Overview of Samples
The list below gives an overview of the samples provided in subdirectories. The following list gives an overview of the samples provided in subdirectories.
Each sub directory essentially is a individual npm package arranged in an [all-in-one monorepo](all-in-one-monorepo) umbrella setup. Each sub directory essentially is an individual npm package arranged in an [all-in-one monorepo](all-in-one-monorepo) umbrella setup.
## [@capire/hello-world](hello) ## [@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). - 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) ## [@capire/bookshop](bookshop)
- [Getting Started](https://cap.cloud.sap/docs/get-started/in-a-nutshell) with CAP, briefly introducing: - [Getting Started](https://cap.cloud.sap/docs/get-started/in-a-nutshell) with CAP, briefly introducing:
- [Project Setup](https://cap.cloud.sap/docs/get-started/) and [Layouts](https://cap.cloud.sap/docs/get-started/projects) - [Project Setup](https://cap.cloud.sap/docs/get-started/) and [Layouts](https://cap.cloud.sap/docs/get-started/projects)
- [Domain Modelling](https://cap.cloud.sap/docs/guides/domain-models) - [Domain Modeling](https://cap.cloud.sap/docs/guides/domain-models)
- [Defining Services](https://cap.cloud.sap/docs/guides/providing-services) - [Defining Services](https://cap.cloud.sap/docs/guides/providing-services)
- [Generic Providers](https://cap.cloud.sap/docs/guides/generic-providers) - [Generic Providers](https://cap.cloud.sap/docs/guides/generic-providers)
- [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) - [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl)
@@ -22,7 +23,7 @@ Each sub directory essentially is a individual npm package arranged in an [all-i
## [@capire/common](common) ## [@capire/common](common)
- Showcases how to extend [@sap/cds/common](https://cap.cloud.sap/docs/cds/common) thereby covering... - Showcases how to extend [@sap/cds/common](https://cap.cloud.sap/docs/cds/common) thereby covering:
- Building [extension packages](https://cap.cloud.sap/docs/guides/domain-models#aspects-extensibility) - Building [extension packages](https://cap.cloud.sap/docs/guides/domain-models#aspects-extensibility)
- Providing [reuse packages](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) - Providing [reuse packages](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content)
- [Verticalization](https://cap.cloud.sap/docs/cds/common#adapting-to-your-needs) - [Verticalization](https://cap.cloud.sap/docs/cds/common#adapting-to-your-needs)
@@ -32,22 +33,22 @@ Each sub directory essentially is a individual npm package arranged in an [all-i
## [@capire/orders](orders) ## [@capire/orders](orders)
- A standalone orders mgmt service, demonstrating... - A standalone orders management service, demonstrating:
- Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with - Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with
- [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data) - [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data)
## [@capire/reviews](reviews) ## [@capire/reviews](reviews)
- Shows how to implement a modular service to manage product reviews, including... - Shows how to implement a modular service to manage product reviews, including:
- Consuming other services synchronously and asynchronously - Consuming other services synchronously and asynchronously
- Serving requests synchronously - Serving requests synchronously
- Emitting events asynchronously - Emitting events asynchronously
- Grow as you go, with... - Grow as you go, with:
- Mocking app services - Mocking app services
- Running service meshes - Running service meshes
- Late-cut Micro Services - Late-cut Micro Services
- As well as managed data, input validations and authorization - As well as managed data, input validations, and authorization
## [@capire/fiori](fiori) ## [@capire/fiori](fiori)
@@ -57,13 +58,13 @@ Each sub directory essentially is a individual npm package arranged in an [all-i
- [@capire/reviews](reviews) - [@capire/reviews](reviews)
- [@capire/orders](orders) - [@capire/orders](orders)
- [@capire/common](common) - [@capire/common](common)
- [Adds a Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to... - [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 - [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 [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)
- Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help) - Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)
- Serving Fiori apps locally - Serving SAP Fiori apps locally
- [The Vue.js app](bookshop/app/vue) imported from bookshop is served as well - [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> <br>

View File

@@ -280,7 +280,7 @@ describe('cds.ql → cqn', () => {
).to.eql({ ).to.eql({
SELECT: { SELECT: {
from: { ref: ['Foo'] }, from: { ref: ['Foo'] },
where: cdr where: cds.version >= '5.3.0'
? [ ? [
// '(', //> this one is not required // '(', //> this one is not required
{ ref: ['ID'] }, { ref: ['ID'] },
@@ -289,7 +289,7 @@ describe('cds.ql → cqn', () => {
'and', 'and',
{ ref: ['args'] }, { ref: ['args'] },
'in', 'in',
{ val: args }, { list: args.map(val => ({ val })) },
'and', 'and',
'(', //> this one is missing, and that's changing the logic -> that's a BUG '(', //> this one is missing, and that's changing the logic -> that's a BUG
{ ref: ['x'] }, { ref: ['x'] },
@@ -325,7 +325,26 @@ describe('cds.ql → cqn', () => {
}) })
// using CQL fragments -> uses cds.parse.expr // using CQL fragments -> uses cds.parse.expr
expect((cqn = CQL`SELECT from Foo where ID=11 and x in ( foo, 'bar', 3)`)).to.eql({ const is_v2 = !!cds.parse.expr('(1,2)').list
if (is_v2) expect((cqn = CQL`SELECT from Foo where ID=11 and x in ( foo, 'bar', 3)`)).to.eql({
SELECT: {
from: { ref: ['Foo'] },
where: [
{ ref: ['ID'] },
'=',
{ val: ID },
'and',
{ ref: ['x'] },
'in',
{list:[
{ ref: ['foo'] },
{ val: 'bar' },
{ val: 3 },
]}
],
},
})
else expect((cqn = CQL`SELECT from Foo where ID=11 and x in ( foo, 'bar', 3)`)).to.eql({
SELECT: { SELECT: {
from: { ref: ['Foo'] }, from: { ref: ['Foo'] },
where: [ where: [
@@ -346,7 +365,7 @@ describe('cds.ql → cqn', () => {
}, },
}) })
expect( if (!is_v2) expect(
SELECT.from(Foo).where(`x=`, 1, `or y.z is null and (a>`, 2, `or b=`, 3, `)`) SELECT.from(Foo).where(`x=`, 1, `or y.z is null and (a>`, 2, `or b=`, 3, `)`)
).to.eql(CQL`SELECT from Foo where x=1 or y.z is null and (a>2 or b=3)`) ).to.eql(CQL`SELECT from Foo where x=1 or y.z is null and (a>2 or b=3)`)

View File

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

View File

@@ -1,11 +1,10 @@
const {expect} = require('../test') const {expect} = require('../test')
const cds = require('@sap/cds/lib') 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 { 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 { entity Categories {
key ID : Integer; key ID : Integer;
name : String; name : String;

View File

@@ -1,6 +1,2 @@
const cds = require('@sap/cds')
const test = require('@sap/cds/lib/utils/tests').in(__dirname,'..') module.exports = cds.test.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,'..')

View File

@@ -43,7 +43,7 @@ describe('Localized Data', () => {
{ title: 'Jane Eyre', author: 'Charlotte Brontë', currency: { name: 'Pfund' } }, { title: 'Jane Eyre', author: 'Charlotte Brontë', currency: { name: 'Pfund' } },
{ title: 'The Raven', author: 'Edgar Allen Poe', currency: { name: 'US-Dollar' } }, { title: 'The Raven', author: 'Edgar Allen Poe', currency: { name: 'US-Dollar' } },
{ title: 'Eleonora', 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: 'Jane Eyre', currency: { name: 'British Pound' } },
{ title: 'The Raven', currency: { name: 'US Dollar' } }, { title: 'The Raven', currency: { name: 'US Dollar' } },
{ title: 'Eleonora', 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 let N=0, received=[], M=0
it ('should add messaging event handlers', ()=>{ 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', ()=>{ it ('should add more messaging event handlers', ()=>{
srv.on('reviewed', (_,next)=> { ++M; return next() }) srv.on('reviewed', ()=> ++M)
}) })
it ('should add review', async ()=>{ it ('should add review', async ()=>{
@@ -42,16 +42,16 @@ describe('Messaging', ()=>{
// { ID: 111 + (++N), subject: "201", title: "Captivating", rating: N }, // { ID: 111 + (++N), subject: "201", title: "Captivating", rating: N },
// ), // ),
srv.create ('Reviews') .entries ( srv.create ('Reviews') .entries (
{ ID: 111 + (++N), subject: "201", title: "Captivating", rating: N } { ID: String(111 + (++N)), subject: "201", title: "Captivating", rating: N }
), ),
srv.create ('Reviews') .entries ( srv.create ('Reviews') .entries (
{ ID: 111 + (++N), subject: "201", title: "Captivating", rating: N } { ID: String(111 + (++N)), subject: "201", title: "Captivating", rating: N }
), ),
srv.create ('Reviews') .entries ( srv.create ('Reviews') .entries (
{ ID: 111 + (++N), subject: "201", title: "Captivating", rating: N } { ID: String(111 + (++N)), subject: "201", title: "Captivating", rating: N }
), ),
srv.create ('Reviews') .entries ( srv.create ('Reviews') .entries (
{ ID: 111 + (++N), subject: "201", title: "Captivating", rating: N } { ID: String(111 + (++N)), subject: "201", title: "Captivating", rating: N }
), ),
])) ]))