Compare commits

..

113 Commits

Author SHA1 Message Date
Daniel
fc69311ebe . 2021-07-16 07:09:19 +02:00
Daniel
67643b8d95 merged from master 2021-07-16 06:20:48 +02:00
D065023
8ba8381ca7 dash to dot 2021-06-18 08:29:33 +02:00
D065023
e7fbcc1ac1 mv annotation to better place 2021-06-16 17:04:40 +02:00
D065023
d977c21483 a bit nicer 2021-06-16 16:46:53 +02:00
D065023
2e10b1ac4d after handler 2021-06-16 16:44:58 +02:00
D065023
cabcd404cf dot notation in line items 2021-06-16 16:41:39 +02:00
D065023
5141df8e0f better value list 2021-06-16 16:40:18 +02:00
Daniel
785ecbda8f cleaned up code 2021-06-16 12:30:56 +02:00
Daniel
2b9ceba62f illustrations 2021-06-16 12:30:30 +02:00
D065023
c6e2fac860 mv data 2021-06-16 12:02:00 +02:00
Uwe Klinger
f2877db34f Disable search field in value help 2021-06-16 11:02:00 +02:00
Uwe Klinger
d2bd4c5bb1 Fix replication error due to removed entity namespace 2021-06-16 07:17:09 +02:00
Uwe Klinger
732b6b081c Add missing profile 2021-06-16 07:16:05 +02:00
D065023
db595a9635 better logs 2021-06-15 15:02:13 +02:00
Daniel
4a8b85f227 Added illustrations to ffollow up on workarounds 2021-06-15 12:58:09 +02:00
Daniel
0cb7349e62 Cosmetics -> for docs 2021-06-15 12:57:42 +02:00
D065023
ba3eeff58b odata-v2 -> odata otherwise mock won't work 2021-06-14 12:44:21 +02:00
D065023
86e6983bed use enterprise-messaging-shared 2021-06-11 14:46:51 +02:00
D065023
9bbc431448 odata-v2 (needs PR) and rm file-based 2021-06-11 14:46:09 +02:00
Uwe Klinger
097b74db03 Adjustments 2021-06-11 10:40:17 +02:00
Uwe Klinger
870f8d063d Add Fiori UI for Book administration
* Copyied from fiori/app/admin
* Added Supplier + Value Help + Texts
* Fixed bug in replication handler that caused to return wrong data
2021-06-11 08:15:27 +02:00
D065023
4a0db3e259 rm sleep 2021-06-10 15:59:00 +02:00
D065023
5e76c7f52e console.log 2021-06-10 15:58:25 +02:00
D065023
31c110e9e2 added profiles 2021-06-10 15:55:26 +02:00
Daniel
8d67c0bf62 Merged 2021-06-09 10:22:30 +02:00
Uwe Klinger
21359a7ad3 Use type reference 2021-06-09 08:41:00 +02:00
Uwe Klinger
649b9c8ca8 Test script added as requests 2021-06-09 08:17:59 +02:00
Uwe Klinger
11c54b29d0 Fix minor diffs 2021-06-09 07:51:51 +02:00
Uwe Klinger
80302a0a3d Remove notes test 2021-06-09 07:50:30 +02:00
Daniel
c8c10b7c8a cds.events test 2021-06-09 07:49:55 +02:00
Uwe Klinger
d9d7203e49 Fix unintended diffs 2021-06-09 07:47:28 +02:00
Uwe Klinger
4a8379e953 Remove notes app 2021-06-09 07:47:16 +02:00
Uwe Klinger
dd28bd04f6 Minor cleanups 2021-06-09 07:44:18 +02:00
Uwe Klinger
5397df5e81 Remove unwanted file 2021-06-09 07:34:38 +02:00
Daniel
f57ae79e37 running suppliers 2021-06-09 07:33:48 +02:00
Daniel
6412df75dd cosmetic changes 2021-06-09 07:30:07 +02:00
Daniel
9277aa12da removed obsolete cds.emit monky patch 2021-06-09 07:25:54 +02:00
Daniel
d41a9e10ab Simplifying samples 2021-06-09 07:25:36 +02:00
Daniel
0b2182afcb Fixed: books title was missing 2021-06-09 07:24:35 +02:00
Daniel
6b74c23aa0 Async 'served' event 2021-06-09 07:22:14 +02:00
Dr. David A. Kunz
c854717359 Update mashup.cds 2021-06-09 07:19:34 +02:00
Uwe Klinger
9dfe62c5c7 Fixed mocking for last test 2021-06-09 07:10:28 +02:00
Uwe Klinger
5b4210bb38 Use "db" service to avoid double call of service handlers 2021-06-09 07:10:28 +02:00
Uwe Klinger
48ee934b00 Only profile credentials 2021-06-09 07:10:28 +02:00
Uwe Klinger
254bb40e38 Remove monkey patch and profile destination 2021-06-09 07:10:28 +02:00
Uwe Klinger
fb5d00bbe0 Improved generic handler remote services 2021-06-09 07:10:28 +02:00
Uwe Klinger
7ae992c5bb Implement generic expand and navigation feature 2021-06-09 07:10:28 +02:00
Uwe Klinger
016587094f Add suppliers notes app 2021-06-09 07:10:28 +02:00
Uwe Klinger
d04cb801c4 Updated 2021-06-09 07:10:28 +02:00
Uwe Klinger
902afd8a76 Change event to reflect the real event definition 2021-06-09 07:10:28 +02:00
Uwe Klinger
c4bee1f09a Fixes and improvements 2021-06-09 07:10:28 +02:00
Uwe Klinger
278258c436 Implement eventing 2021-06-09 07:10:28 +02:00
Uwe Klinger
8601bd8a46 Suppliers moved to bookshop namespace 2021-06-09 07:10:28 +02:00
Uwe Klinger
98113c46fd Own transaction when raising an event 2021-06-09 07:10:28 +02:00
Uwe Klinger
eb4bc703dd Improvements for Supplier replication 2021-06-09 07:10:28 +02:00
Uwe Klinger
de806a1e38 Add example with supplier_ID 2021-06-09 07:10:28 +02:00
Uwe Klinger
2999c8df83 Add todo 2021-06-09 07:10:28 +02:00
Daniel
87dbb4552a . 2021-06-09 07:10:27 +02:00
Daniel
0bdf4bb93e . 2021-06-09 07:10:27 +02:00
Daniel
a4810c3a7b . 2021-06-09 07:10:27 +02:00
Daniel
c899843319 Using event : projection on Reviews 2021-06-09 07:10:27 +02:00
Daniel
43e5f6faef Adding fix-antlr script 2021-06-09 07:10:27 +02:00
Daniel
1b5cc62d1c some corrections and optimizations 2021-06-09 07:10:27 +02:00
Iwona Hahn
4b48d68ba6 cosmetics 2021-06-09 07:10:27 +02:00
Iwona Hahn
d21a0e08ca cosmetics 2021-06-09 07:10:27 +02:00
Iwona Hahn
50fd83fd19 cosmetics 2021-06-09 07:10:27 +02:00
Daniel Hutzel
3f20f4b9a2 Delete settings.json 2021-06-09 07:10:27 +02:00
Daniel Hutzel
5031779be8 Delete launch.json 2021-06-09 07:10:27 +02:00
Daniel Hutzel
ad385cbc6c Delete tasks.json 2021-06-09 07:10:27 +02:00
Daniel Hutzel
2650db4ee9 Delete extensions.json 2021-06-09 07:10:27 +02:00
Daniel Hutzel
e04e9d231d Delete .gitignore 2021-06-09 07:10:27 +02:00
Christian Georgi
476724792e Make the CatalogService usage more obvious 2021-06-09 07:10:27 +02:00
Uwe Klinger
5ae31f7c67 Adding suppliers showing integration with S/4 2021-06-09 07:10:25 +02:00
Daniel
edbca44a43 removed obsolete cds.emit monky patch 2021-06-08 18:23:52 +02:00
Daniel
02f19e295d Simplifying samples 2021-06-08 18:20:44 +02:00
Daniel
05cdb67329 Fixed: books title was missing 2021-06-08 16:01:45 +02:00
D065023
4877386e86 A bit more fault tolerant
(e.g. when only one service is mocked)
2021-06-08 14:48:12 +02:00
Daniel
4bd4446975 Async 'served' event 2021-06-08 13:18:39 +02:00
Daniel
a54b0124ea Using file-based-messaging in fiori 2021-06-08 13:18:21 +02:00
Daniel
944afe3bc8 Using package-lock 2021-06-08 13:17:50 +02:00
Dr. David A. Kunz
92a83f71a1 Update mashup.cds 2021-06-07 14:25:53 +02:00
Daniel
5e5ace0dd4 Merge branch 'master' into adding-suppliers 2021-06-07 08:33:21 +02:00
Daniel
593228a51e cds.events test 2021-06-04 11:34:52 +02:00
Daniel
624cea6343 running suppliers 2021-06-04 11:34:08 +02:00
Daniel
adfe170e8d Convenience 2021-05-21 14:03:27 +02:00
D065023
6b08826af5 Mocking events from SAP S/4HANA 2021-05-20 22:01:15 +02:00
Daniel
c6239f0375 Merged from master 2021-05-19 15:07:52 +02:00
Daniel
0d19e56509 cosmetic changes 2021-05-13 12:29:22 +02:00
Daniel
fc088015d3 Merge branch 'master' into adding-suppliers 2021-04-30 13:07:01 +02:00
Daniel
c9f7dc68b8 Merge branch 'master' into adding-suppliers 2021-04-10 13:07:53 +02:00
Daniel
7d9303635e . 2021-03-27 19:44:39 +01:00
Daniel
b724ae900c Merge branch 'master' into adding-suppliers 2021-03-27 19:40:58 +01:00
Daniel
2f96b92854 . 2021-03-27 19:30:18 +01:00
Daniel
99dc8d31d7 . 2021-03-27 19:27:29 +01:00
Daniel
c672e0f1fa Merge branch 'master' into adding-suppliers 2021-03-27 19:25:27 +01:00
Daniel
0ddd70acbc Using event : projection on Reviews 2021-03-11 14:14:59 +01:00
Daniel
3e52a9a102 Adding fix-antlr script 2021-03-08 19:19:29 +01:00
Daniel
b44701ef62 Merge branch 'master' into adding-suppliers 2021-03-08 19:07:56 +01:00
Daniel
c23ddc7e54 Merge branch 'master' into adding-suppliers 2021-02-19 16:36:26 +01:00
Daniel
66bd2f707c Merge branch 'master' into adding-suppliers 2021-02-19 12:42:08 +01:00
Daniel
3320c7e5a2 some corrections and optimizations 2021-02-19 12:15:35 +01:00
Daniel
a35782e775 Merge branch 'master' into adding-suppliers 2021-02-19 12:10:49 +01:00
Iwona Hahn
e5bd8ec5a5 cosmetics 2021-02-17 17:55:27 +01:00
Iwona Hahn
0aa95a0a67 cosmetics 2021-02-17 17:54:47 +01:00
Iwona Hahn
5015eb8c52 cosmetics 2021-02-17 17:53:54 +01:00
Daniel Hutzel
6d3f4c689f Delete settings.json 2021-02-17 16:07:53 +01:00
Daniel Hutzel
f0fead2bc2 Delete launch.json 2021-02-17 16:07:43 +01:00
Daniel Hutzel
f1d780d6d9 Delete tasks.json 2021-02-17 16:07:23 +01:00
Daniel Hutzel
796bf62bde Delete extensions.json 2021-02-17 16:07:02 +01:00
Daniel Hutzel
5f176a0b88 Delete .gitignore 2021-02-17 16:06:51 +01:00
Christian Georgi
a5c8b5101e Make the CatalogService usage more obvious 2021-02-17 16:00:10 +01:00
Daniel
d72ff809b0 Adding suppliers showing integration with S/4 2021-02-17 13:24:17 +01:00
210 changed files with 36684 additions and 8880 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -1,9 +0,0 @@
{
"name": "approuter",
"dependencies": {
"@sap/approuter": "^20.0.0"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}

View File

@@ -1 +0,0 @@
../../../bookshop/app/vue

View File

@@ -1 +0,0 @@
../../../orders/app/orders

View File

@@ -1 +0,0 @@
../../../reviews/app/vue

View File

@@ -1,41 +0,0 @@
{
"welcomeFile": "app/bookshop/index.html",
"routes": [
{
"source": "^/app/(.*)$",
"target": "$1",
"localDir": "resources",
"cacheControl": "no-cache, no-store, must-revalidate"
},
{
"source": "^/admin/(.*)$",
"target": "/admin/$1",
"destination": "bookstore-api",
"csrfProtection": true
},
{
"source": "^/browse/(.*)$",
"target": "/browse/$1",
"destination": "bookstore-api",
"csrfProtection": true
},
{
"source": "^/user/(.*)$",
"target": "/user/$1",
"destination": "bookstore-api",
"csrfProtection": true
},
{
"source": "^/odata/v4/orders/(.*)$",
"target": "/odata/v4/orders/$1",
"destination": "orders-api",
"csrfProtection": true
},
{
"source": "^/reviews/(.*)$",
"target": "/reviews/$1",
"destination": "reviews-api",
"csrfProtection": true
}
]
}

27
.eslintrc Normal file
View File

@@ -0,0 +1,27 @@
{
"extends": "eslint:recommended",
"env": {
"browser": true,
"node": true,
"es6": true,
"jest": true,
"mocha": true
},
"parserOptions": {
"ecmaVersion": 2018
},
"globals": {
"SELECT": true,
"INSERT": true,
"UPDATE": true,
"DELETE": true,
"CREATE": true,
"DROP": true,
"cds": true
},
"rules": {
"no-console": "off",
"require-atomic-updates": "off",
"require-await":"warn"
}
}

View File

@@ -1,5 +0,0 @@
blank_issues_enabled: false
contact_links:
- name: This channel is CLOSED.
about: Use SAP community instead
url: https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce

View File

@@ -0,0 +1,10 @@
---
name: This channel is CLOSED.
about: Use our community at https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce
title: ''
labels: ''
assignees: ''
---
Please use our community on https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce

View File

@@ -1,23 +0,0 @@
version: 2
updates:
- package-ecosystem: npm
directory: /
versioning-strategy: increase-if-necessary
schedule:
interval: weekly
groups:
production-dependencies:
dependency-type: "production"
development-dependencies:
dependency-type: "development"
ignore:
- dependency-name: "chai"
# chai 5 doesn't work atm w/ cds.test, TODO fix that in cds.test
versions: ["5.x"]
- dependency-name: "chai-as-promised"
# chai-as-promised 8 doesn't work atm w/ cds.test, TODO fix that in cds.test
versions: ["8.x"]
- dependency-name: "express"
# express 5 not supported atm
versions: ["5.x"]

View File

@@ -5,34 +5,24 @@ name: CI
on:
push:
branches: [ main ]
branches: [ master ]
pull_request:
branches: [ main ]
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [22.x, 20.x]
node-version: [12.x, 14.x]
steps:
- uses: actions/checkout@v4
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm ci
- run: npm test
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with:
node-version: 22.x
- run: npm ci
- run: npm run lint

6
.gitignore vendored
View File

@@ -12,14 +12,8 @@ target/
*.mtar
connection.properties
default-env.json
.cdsrc-private.json
packages/messageBox
reviews/msg-box
reviews/db/test.db
*.openapi3.json
*.sqlite
*.db
@types/
@cds-models/

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

71
.registry/server.js Normal file
View File

@@ -0,0 +1,71 @@
const { exec } = require ('child_process')
const express = require ('express')
const fs = require ('fs')
const app = express()
const { PORT=4444 } = process.env
const [,,port=PORT] = process.argv
const cwd = __dirname
app.use('/-/:tarball', (req,res,next) => {
console.debug ('GET', req.params)
try {
const { tarball } = req.params
const [, pkg ] = /^capire-(\w+)/.exec(tarball)
fs.lstat(tarball,(err => {
if (err) exec(`npm pack ../${pkg}`,{cwd},next)
else next()
}))
} catch (e) {
console.error(e)
res.sendStatus(500)
}
})
app.use('/-', express.static(__dirname))
app.get('/*', (req,res)=>{
const url = decodeURIComponent(req.url)
console.debug ('GET',url)
try {
const [, capire, pkg ] = /^\/(@capire)\/(\w+)/.exec(url)
const package = require (`${capire}/${pkg}/package.json`)
const tarball = `capire-${pkg}-${package.version}.tgz`
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
res.json({
"name": package.name,
"dist-tags": {
"latest": package.version
},
"versions": {
[package.version]: {
"name": package.name,
"version": package.version,
"dist": {
"tarball": `http://localhost:${port}/-/${tarball}`
},
}
},
})
} catch (e) {
console.error(e)
res.sendStatus(404)
}
})
app.listen(port, ()=>{
console.log (`npm set @capire:registry=http://localhost:${port}`)
console.log (`@capire registry listening on http://localhost:${port}`)
exec(`npm set @capire:registry=http://localhost:${port}`)
})
const _exit = ()=>{
console.log ('\nnpm conf rm @capire:registry')
exec('npm conf rm @capire:registry')
exec('rm *.tgz')
process.exit()
}
process.on ('SIGTERM',_exit)
process.on ('SIGHUP',_exit)
process.on ('SIGINT',_exit)
process.on ('SIGUSR2',_exit)

View File

@@ -24,6 +24,6 @@ Disclaimer: The code in this project may include calls to APIs (“API Calls”)
you any rights to use or access any SAP External Product, or provide any third
parties the right to use of access any SAP External Product, through API Calls.
Files: *.*
Copyright: 2019-2025 SAP SE or an SAP affiliate company and cap-cloud-samples
Files: *
Copyright: 2019-2020 SAP SE or an SAP affiliate company and cap-cloud-samples
License: Apache-2.0

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

20
.vscode/launch.json vendored
View File

@@ -13,7 +13,7 @@
"<node_internals>/**",
"**/node_modules/**",
"**/cds/lib/lazy.js",
"**/cds/lib/req/cds-context.js",
"**/cds/lib/req/cls.js",
"**/odata-v4/okra/**"
]
},
@@ -26,24 +26,10 @@
"<node_internals>/**",
"**/node_modules/**",
"**/cds/lib/lazy.js",
"**/cds/lib/req/cds-context.js",
"**/cds/lib/req/cls.js",
"**/odata-v4/okra/**"
]
},
{
"name": "Debug Mocha Tests",
"type": "node",
"request": "attach",
"port": 9229,
"continueOnAttach": true,
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**",
"**/cds/lib/lazy.js",
"**/cds/lib/req/cds-context.js",
"**/odata-v4/okra/**",
]
},
}
],
"inputs": [
{

12
.vscode/settings.json vendored
View File

@@ -1,17 +1,17 @@
{
"files.exclude": {
"**/node_modules": true,
"LICENSES": true,
".reuse": true
".reuse/**": true,
"**/.gitignore": true,
"**/.vscode": true,
"LICENSES/**": true
},
"debug.javascript.terminalOptions": {
"skipFiles": [
"<node_internals>/**",
"**/node_modules/**",
"**/cds/lib/lazy.js",
"**/cds/lib/req/cds-context.js",
"**/cds/lib/req/cls.js",
"**/odata-v4/okra/**"
]
},
"jest.jestCommandLine": "npx jest"
}
}

118
README.md Normal file
View File

@@ -0,0 +1,118 @@
# Welcome to cap/samples
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo). &rarr; See [**Overview** of contained samples](samples.md)
![](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)
### Preliminaries
1. Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/) globally:
```sh
npm i -g @sap/cds-dk
```
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/tools#vscode)
### Download
If you've [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/master.zip).
```sh
git clone https://github.com/sap-samples/cloud-cap-samples samples
cd samples
```
### Setup
In the samples folder run:
```sh
npm install
```
### Run
With that you're ready to run the samples, for example:
```sh
cds watch bookshop
```
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
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example:
```sh
npx jest
```
> While mocha is a bit smaller and faster, jest runs tests in parallel and isolation, which allows to run all tests.
### Serve `npm`
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:
```sh
npm run registry
```
> While running this will have `@capire:registry=http://localhost:4444` set with npmrc.
2. Install one of the @capire packages wherever you like, for example:
```sh
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
Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br>
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
Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSE.txt) file.
# Suppliers - in progress for Messaging & Service Consumption -
## TODOs
1. Fix issues when running in same process
2. Automated tests
## Usage
1. Run:
```
CDS_ENV=local-hybrid cds mock API_BUSINESS_PARTNER -p 5001
```
2. Wait until startup is completed
3. Run in a 2nd terminal:
```
CDS_ENV=local-hybrid cds serve all --with-mocks --in-memory
```
4. Now, you can issues the requests listed in `suppliers/requests.http`
## Request Sequence
* TODO
## URLs
* Get books with their replicated supplier: http://localhost:4004/browse/Books?$expand=supplier
* Get remote suppliers: http://localhost:4004/admin/Suppliers?$top=11

View File

@@ -0,0 +1,2 @@
// Incorporate pre-build extensions from...
using from '@capire/common';

View File

@@ -3,15 +3,14 @@ const $ = sel => document.querySelector(sel)
const GET = (url) => axios.get('/browse'+url)
const POST = (cmd,data) => axios.post('/browse'+cmd,data)
const books = Vue.createApp ({
const books = new Vue ({
data() {
return {
el:'#app',
data: {
list: [],
book: undefined,
order: { quantity:1, succeeded:'', failed:'' },
user: undefined
}
order: { amount:1, succeeded:'', failed:'' }
},
methods: {
@@ -19,7 +18,7 @@ const books = Vue.createApp ({
search: ({target:{value:v}}) => books.fetch(v && '&$search='+v),
async fetch (etc='') {
const {data} = await GET(`/ListOfBooks?$expand=genre($select=name),currency($select=symbol)${etc}`)
const {data} = await GET(`/ListOfBooks?$expand=genre,currency${etc}`)
books.list = data.value
},
@@ -27,63 +26,23 @@ const books = Vue.createApp ({
const book = books.book = books.list [eve.currentTarget.rowIndex-1]
const res = await GET(`/Books/${book.ID}?$select=descr,stock,image`)
Object.assign (book, res.data)
books.order = { quantity:1 }
books.order = { amount:1 }
setTimeout (()=> $('form > input').focus(), 111)
},
async submitOrder () {
const {book,order} = books, quantity = parseInt (order.quantity) || 1 // REVISIT: Okra should be less strict
const {book,order} = books, amount = parseInt (order.amount) || 1 // REVISIT: Okra should be less strict
try {
const res = await POST(`/submitOrder`, { quantity, book: book.ID })
const res = await POST(`/submitOrder`, { amount, book: book.ID })
book.stock = res.data.stock
books.order = { quantity, succeeded: `Successfully ordered ${quantity} item(s).` }
books.order = { amount, succeeded: `Successfully ordered ${amount} item(s).` }
} catch (e) {
books.order = { quantity, failed: e.response.data.error ? e.response.data.error.message : e.response.data }
books.order = { amount, failed: e.response.data.error.message }
}
},
}
async login() {
try {
const { data:user } = await axios.post('/user/login',{})
if (user.id !== 'anonymous') books.user = user
} catch (err) { books.user = { id: err.message } }
},
async getUserInfo() {
try {
const { data:user } = await axios.get('/user/me')
if (user.id !== 'anonymous') books.user = user
} catch (err) { books.user = { id: err.message } }
},
}
}).mount('#app')
books.getUserInfo()
books.fetch() // initially fill list of books
document.addEventListener('keydown', (event) => {
// hide user info on request
if (event.key === 'u') books.user = undefined
})
axios.interceptors.request.use(csrfToken)
function csrfToken (request) {
if (request.method === 'head' || request.method === 'get') return request
if ('csrfToken' in document) {
request.headers['x-csrf-token'] = document.csrfToken
return request
}
return fetchToken().then(token => {
document.csrfToken = token
request.headers['x-csrf-token'] = document.csrfToken
return request
}).catch(() => {
document.csrfToken = null // set mark to not try again
return request
})
function fetchToken() {
return axios.get('/', { headers: { 'x-csrf-token': 'fetch' } })
.then(res => res.headers['x-csrf-token'])
}
}
// initially fill list of books
books.fetch()

View File

@@ -3,34 +3,21 @@
<head>
<title> Capire Books </title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/primitive-ui/dist/css/main.css">
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<style>
.hovering tr:hover td { color:cyan; background: #123; cursor: pointer; }
.rating-stars { color:teal }
.succeeded { color:teal }
.failed { color:red }
.user {text-align: end; color: grey;}
</style>
</head>
<body class="small-container", style="margin-top: 70px;">
<div id='app'>
<form class="user" @submit.prevent="login">
<div v-if="user">
<div v-if="user.tenant">Tenant: {{ user.tenant }}</div>
<div> User: {{ user.id }}</div>
<div>Locale: {{ user.locale }}</div>
</div>
<div v-else>
<input type="submit" value="Login" class="muted-button">
<!-- <a href="/user/login()">Login</a> -->
</div>
</form>
<h1> Capire Books </h1>
<h1> {{ document.title }} </h1>
<input type="text" placeholder="Search..." @input="search">
@@ -47,7 +34,7 @@
<td>{{ book.author }}</td>
<td>{{ book.genre.name }}</td>
<td class="rating-stars">
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }} ({{ book.numberOfReviews }})
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }}
</td>
<td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td>
</tr>
@@ -61,7 +48,7 @@
&nbsp;&nbsp; {{ book.stock }} in stock
</label>
<form @submit.prevent="submitOrder" style="float:right; display:flex; flex-direction:row-reverse">
<input type="number" v-model="order.quantity" v-bind:class="{ failed: order.failed }" style="width:5em">
<input type="number" v-model="order.amount" v-bind:class="{ failed: order.failed }" style="width:5em">
<input type="submit" value="Order:" class="muted-button">
</form>
<h4> {{ book.title }} </h4>

View File

@@ -1,5 +1,5 @@
ID,name,dateOfBirth,placeOfBirth,dateOfDeath,placeOfDeath
101,Emily Brontë,1818-07-30,"Thornton, Yorkshire",1848-12-19,"Haworth, Yorkshire"
107,Charlotte Brontë,1818-04-21,"Thornton, Yorkshire",1855-03-31,"Haworth, Yorkshire"
150,Edgar Allen Poe,1809-01-19,"Boston, Massachusetts",1849-10-07,"Baltimore, Maryland"
170,Richard Carpenter,1929-08-14,"Kings Lynn, Norfolk",2012-02-26,"Hertfordshire, England"
ID;name;dateOfBirth;placeOfBirth;dateOfDeath;placeOfDeath
101;Emily Brontë;1818-07-30;Thornton, Yorkshire;1848-12-19;Haworth, Yorkshire
107;Charlotte Brontë;1818-04-21;Thornton, Yorkshire;1855-03-31;Haworth, Yorkshire
150;Edgar Allen Poe;1809-01-19;Boston, Massachusetts;1849-10-07;Baltimore, Maryland
170;Richard Carpenter;1929-08-14;Kings Lynn, Norfolk;2012-02-26;Hertfordshire, England
1 ID name dateOfBirth placeOfBirth dateOfDeath placeOfDeath
2 101 Emily Brontë 1818-07-30 Thornton, Yorkshire 1848-12-19 Haworth, Yorkshire
3 107 Charlotte Brontë 1818-04-21 Thornton, Yorkshire 1855-03-31 Haworth, Yorkshire
4 150 Edgar Allen Poe 1809-01-19 Boston, Massachusetts 1849-10-07 Baltimore, Maryland
5 170 Richard Carpenter 1929-08-14 King’s Lynn, Norfolk 2012-02-26 Hertfordshire, England

View File

@@ -1,6 +1,6 @@
ID,title,descr,author_ID,stock,price,currency_code,genre_ID
201,Wuthering Heights,"Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym ""Ellis Bell"". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850.",101,12,11.11,GBP,11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
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,11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
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,16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
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,15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
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,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
ID;title;descr;author_ID;stock;price;currency_code;genre_ID
201;Wuthering Heights;"Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym ""Ellis Bell"". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850.";101;12;11.11;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
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;150;JPY;13
1 ID title descr author_ID stock price currency_code genre_ID
2 201 Wuthering Heights Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym "Ellis Bell". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850. 101 12 11.11 GBP 11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 11
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 11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 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 16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 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 15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 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 150 JPY 13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 13

View File

@@ -1,5 +0,0 @@
ID_texts,ID,locale,title,descr
d2a65a27-9f2a-480f-bc38-84ee8ec5c13e,201,de,Sturmhöhe,"Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (18181848). Der 1847 unter dem Pseudonym Ellis Bell veröffentlichte Roman wurde vom viktorianischen Publikum weitgehend abgelehnt, heute gilt er als ein Klassiker der britischen Romanliteratur des 19. Jahrhunderts."
8c42c706-a979-41cf-9ffe-91e6cf1383a0,201,fr,Les Hauts de Hurlevent,"Les Hauts de Hurlevent (titre original : Wuthering Heights), parfois orthographié Les Hauts de Hurle-Vent, est l'unique roman d'Emily Brontë, publié pour la première fois en 1847 sous le pseudonyme dEllis Bell. Loin d'être un récit moralisateur, Emily Brontë achève néanmoins le roman dans une atmosphère sereine, suggérant le triomphe de la paix et du Bien sur la vengeance et le Mal."
9e1c4c81-dc90-4600-85b1-e9dd4bf12ce0,207,de,Jane Eyre,"Jane Eyre. Eine Autobiographie (Originaltitel: Jane Eyre. An Autobiography), erstmals erschienen im Jahr 1847 unter dem Pseudonym Currer Bell, ist der erste veröffentlichte Roman der britischen Autorin Charlotte Brontë und ein Klassiker der viktorianischen Romanliteratur des 19. Jahrhunderts. Der Roman erzählt in Form einer Ich-Erzählung die Lebensgeschichte von Jane Eyre (ausgesprochen /ˌdʒeɪn ˈɛə/), die nach einer schweren Kindheit eine Stelle als Gouvernante annimmt und sich in ihren Arbeitgeber verliebt, jedoch immer wieder um ihre Freiheit und Selbstbestimmung kämpfen muss. Als klein, dünn, blass, stets schlicht dunkel gekleidet und mit strengem Mittelscheitel beschrieben, gilt die Heldin des Romans Jane Eyre nicht zuletzt aufgrund der Kino- und Fernsehversionen der melodramatischen Romanvorlage als die bekannteste englische Gouvernante der Literaturgeschichte"
9be0524b-4cb9-4fc1-9dc2-d65b1c13cf53,252,de,Eleonora,"“Eleonora” ist eine Erzählung von Edgar Allan Poe. Sie wurde 1841 erstveröffentlicht. In ihr geht es um das Paradox der Treue in der Treulosigkeit."
1 ID_texts ID locale title descr
2 d2a65a27-9f2a-480f-bc38-84ee8ec5c13e 201 de Sturmhöhe Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (1818–1848). Der 1847 unter dem Pseudonym Ellis Bell veröffentlichte Roman wurde vom viktorianischen Publikum weitgehend abgelehnt, heute gilt er als ein Klassiker der britischen Romanliteratur des 19. Jahrhunderts.
3 8c42c706-a979-41cf-9ffe-91e6cf1383a0 201 fr Les Hauts de Hurlevent Les Hauts de Hurlevent (titre original : Wuthering Heights), parfois orthographié Les Hauts de Hurle-Vent, est l'unique roman d'Emily Brontë, publié pour la première fois en 1847 sous le pseudonyme d’Ellis Bell. Loin d'être un récit moralisateur, Emily Brontë achève néanmoins le roman dans une atmosphère sereine, suggérant le triomphe de la paix et du Bien sur la vengeance et le Mal.
4 9e1c4c81-dc90-4600-85b1-e9dd4bf12ce0 207 de Jane Eyre Jane Eyre. Eine Autobiographie (Originaltitel: Jane Eyre. An Autobiography), erstmals erschienen im Jahr 1847 unter dem Pseudonym Currer Bell, ist der erste veröffentlichte Roman der britischen Autorin Charlotte Brontë und ein Klassiker der viktorianischen Romanliteratur des 19. Jahrhunderts. Der Roman erzählt in Form einer Ich-Erzählung die Lebensgeschichte von Jane Eyre (ausgesprochen /ˌdʒeɪn ˈɛə/), die nach einer schweren Kindheit eine Stelle als Gouvernante annimmt und sich in ihren Arbeitgeber verliebt, jedoch immer wieder um ihre Freiheit und Selbstbestimmung kämpfen muss. Als klein, dünn, blass, stets schlicht dunkel gekleidet und mit strengem Mittelscheitel beschrieben, gilt die Heldin des Romans Jane Eyre nicht zuletzt aufgrund der Kino- und Fernsehversionen der melodramatischen Romanvorlage als die bekannteste englische Gouvernante der Literaturgeschichte
5 9be0524b-4cb9-4fc1-9dc2-d65b1c13cf53 252 de Eleonora “Eleonora” ist eine Erzählung von Edgar Allan Poe. Sie wurde 1841 erstveröffentlicht. In ihr geht es um das Paradox der Treue in der Treulosigkeit.

View File

@@ -0,0 +1,5 @@
ID;locale;title;descr
201;de;Sturmhöhe;Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (18181848). Der 1847 unter dem Pseudonym Ellis Bell veröffentlichte Roman wurde vom viktorianischen Publikum weitgehend abgelehnt, heute gilt er als ein Klassiker der britischen Romanliteratur des 19. Jahrhunderts.
201;fr;Les Hauts de Hurlevent;Les Hauts de Hurlevent (titre original : Wuthering Heights), parfois orthographié Les Hauts de Hurle-Vent, est l'unique roman d'Emily Brontë, publié pour la première fois en 1847 sous le pseudonyme dEllis Bell. Loin d'être un récit moralisateur, Emily Brontë achève néanmoins le roman dans une atmosphère sereine, suggérant le triomphe de la paix et du Bien sur la vengeance et le Mal.
207;de;Jane Eyre;Jane Eyre. Eine Autobiographie (Originaltitel: Jane Eyre. An Autobiography), erstmals erschienen im Jahr 1847 unter dem Pseudonym Currer Bell, ist der erste veröffentlichte Roman der britischen Autorin Charlotte Brontë und ein Klassiker der viktorianischen Romanliteratur des 19. Jahrhunderts. Der Roman erzählt in Form einer Ich-Erzählung die Lebensgeschichte von Jane Eyre (ausgesprochen /ˌdʒeɪn ˈɛə/), die nach einer schweren Kindheit eine Stelle als Gouvernante annimmt und sich in ihren Arbeitgeber verliebt, jedoch immer wieder um ihre Freiheit und Selbstbestimmung kämpfen muss. Als klein, dünn, blass, stets schlicht dunkel gekleidet und mit strengem Mittelscheitel beschrieben, gilt die Heldin des Romans Jane Eyre nicht zuletzt aufgrund der Kino- und Fernsehversionen der melodramatischen Romanvorlage als die bekannteste englische Gouvernante der Literaturgeschichte
252;de;Eleonora;“Eleonora” ist eine Erzählung von Edgar Allan Poe. Sie wurde 1841 erstveröffentlicht. In ihr geht es um das Paradox der Treue in der Treulosigkeit.
1 ID locale title descr
2 201 de Sturmhöhe Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (1818–1848). Der 1847 unter dem Pseudonym Ellis Bell veröffentlichte Roman wurde vom viktorianischen Publikum weitgehend abgelehnt, heute gilt er als ein Klassiker der britischen Romanliteratur des 19. Jahrhunderts.
3 201 fr Les Hauts de Hurlevent Les Hauts de Hurlevent (titre original : Wuthering Heights), parfois orthographié Les Hauts de Hurle-Vent, est l'unique roman d'Emily Brontë, publié pour la première fois en 1847 sous le pseudonyme d’Ellis Bell. Loin d'être un récit moralisateur, Emily Brontë achève néanmoins le roman dans une atmosphère sereine, suggérant le triomphe de la paix et du Bien sur la vengeance et le Mal.
4 207 de Jane Eyre Jane Eyre. Eine Autobiographie (Originaltitel: Jane Eyre. An Autobiography), erstmals erschienen im Jahr 1847 unter dem Pseudonym Currer Bell, ist der erste veröffentlichte Roman der britischen Autorin Charlotte Brontë und ein Klassiker der viktorianischen Romanliteratur des 19. Jahrhunderts. Der Roman erzählt in Form einer Ich-Erzählung die Lebensgeschichte von Jane Eyre (ausgesprochen /ˌdʒeɪn ˈɛə/), die nach einer schweren Kindheit eine Stelle als Gouvernante annimmt und sich in ihren Arbeitgeber verliebt, jedoch immer wieder um ihre Freiheit und Selbstbestimmung kämpfen muss. Als klein, dünn, blass, stets schlicht dunkel gekleidet und mit strengem Mittelscheitel beschrieben, gilt die Heldin des Romans Jane Eyre nicht zuletzt aufgrund der Kino- und Fernsehversionen der melodramatischen Romanvorlage als die bekannteste englische Gouvernante der Literaturgeschichte
5 252 de Eleonora “Eleonora” ist eine Erzählung von Edgar Allan Poe. Sie wurde 1841 erstveröffentlicht. In ihr geht es um das Paradox der Treue in der Treulosigkeit.

View File

@@ -1,64 +0,0 @@
ID;parent_ID;name;page;book_ID
f846b0b9-01d4-4f6d-82a4-d79204f62514;;Foreword;3;207
f846b0b9-01d4-4f6d-82a4-d79204f62515;;Chapter 1;4;207
f846b0b9-01d4-4f6d-82a4-d79204f62516;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.1;5;207
f846b0b9-01d4-4f6d-82a4-d79204f62517;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.2;28;207
f846b0b9-01d4-4f6d-82a4-d79204f62518;f846b0b9-01d4-4f6d-82a4-d79204f62517;Subsection 1.2.1;30;207
f846b0b9-01d4-4f6d-82a4-d79204f62519;f846b0b9-01d4-4f6d-82a4-d79204f62517;Subsection 1.2.2;33;207
f846b0b9-01d4-4f6d-82a4-d79204f62520;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.3;36;207
f846b0b9-01d4-4f6d-82a4-d79204f62521;;Chapter 2;54;207
f846b0b9-01d4-4f6d-82a4-d79204f62522;f846b0b9-01d4-4f6d-82a4-d79204f62521;Section 2.1;56;207
f846b0b9-01d4-4f6d-82a4-d79204f62523;f846b0b9-01d4-4f6d-82a4-d79204f62521;Section 2.2;58;207
f846b0b9-01d4-4f6d-82a4-d79204f62524;;Conclusion;63;207
f846b0b9-01d4-4f6d-82a4-d79204f62525;;Endnotes;65;207
f846b0b9-01d4-4f6d-82a4-d79204f62526;;Copyright notice;2;251
f846b0b9-01d4-4f6d-82a4-d79204f62527;;Chapter 1;3;251
f846b0b9-01d4-4f6d-82a4-d79204f62528;f846b0b9-01d4-4f6d-82a4-d79204f62527;Section 1.1;5;251
f846b0b9-01d4-4f6d-82a4-d79204f62529;f846b0b9-01d4-4f6d-82a4-d79204f62527;Section 1.2;13;251
f846b0b9-01d4-4f6d-82a4-d79204f62530;;Chapter 2;20;251
f846b0b9-01d4-4f6d-82a4-d79204f62531;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.1;21;251
f846b0b9-01d4-4f6d-82a4-d79204f62532;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.2;25;251
f846b0b9-01d4-4f6d-82a4-d79204f62533;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.3;27;251
f846b0b9-01d4-4f6d-82a4-d79204f62534;;Chapter 3;30;251
f846b0b9-01d4-4f6d-82a4-d79204f62535;;Endnotes;41;251
f846b0b9-01d4-4f6d-82a4-d79204f62551;;Acknowledgements;1;271
f846b0b9-01d4-4f6d-82a4-d79204f62552;;The Flight;2;271
f846b0b9-01d4-4f6d-82a4-d79204f62553;;Hexwood Farm;8;271
f846b0b9-01d4-4f6d-82a4-d79204f62554;f846b0b9-01d4-4f6d-82a4-d79204f62553;Castle Saburac;13;271
f846b0b9-01d4-4f6d-82a4-d79204f62555;f846b0b9-01d4-4f6d-82a4-d79204f62553;The Curse of Rapkyn;27;271
f846b0b9-01d4-4f6d-82a4-d79204f62556;f846b0b9-01d4-4f6d-82a4-d79204f62553;The Mannikin;35;271
f846b0b9-01d4-4f6d-82a4-d79204f62557;;The Eye of Time;44;271
f846b0b9-01d4-4f6d-82a4-d79204f62558;;The Enchanting;59;271
f846b0b9-01d4-4f6d-82a4-d79204f62559;f846b0b9-01d4-4f6d-82a4-d79204f62558;The Telling Bone;73;271
f846b0b9-01d4-4f6d-82a4-d79204f62560;f846b0b9-01d4-4f6d-82a4-d79204f62558;The Power of Adamcos;86;271
f846b0b9-01d4-4f6d-82a4-d79204f62561;f846b0b9-01d4-4f6d-82a4-d79204f62558;The House of the Sorcerer;98;271
f846b0b9-01d4-4f6d-82a4-d79204f62562;;The Changeling;105;271
f846b0b9-01d4-4f6d-82a4-d79204f62563;f846b0b9-01d4-4f6d-82a4-d79204f62562;The Flying Broomsticks;118;271
f846b0b9-01d4-4f6d-82a4-d79204f62564;f846b0b9-01d4-4f6d-82a4-d79204f62563;The Fish Out of Water;126;271
f846b0b9-01d4-4f6d-82a4-d79204f62565;;The Final Magic;138;271
f846b0b9-01d4-4f6d-82a4-d79204f62566;;Copyright;159;271
f846b0b9-01d4-4f6d-82a4-d79204f62567;;Editor's Note;1;201
f846b0b9-01d4-4f6d-82a4-d79204f62568;;Chapter I;2;201
f846b0b9-01d4-4f6d-82a4-d79204f62569;;Chapter II;31;201
f846b0b9-01d4-4f6d-82a4-d79204f62570;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.I;47;201
f846b0b9-01d4-4f6d-82a4-d79204f62571;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.II;62;201
f846b0b9-01d4-4f6d-82a4-d79204f62572;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.III;75;201
f846b0b9-01d4-4f6d-82a4-d79204f62573;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.IV;87;201
f846b0b9-01d4-4f6d-82a4-d79204f62574;;Chapter III;105;201
f846b0b9-01d4-4f6d-82a4-d79204f62575;f846b0b9-01d4-4f6d-82a4-d79204f62574;Section III.I;128;201
f846b0b9-01d4-4f6d-82a4-d79204f62576;f846b0b9-01d4-4f6d-82a4-d79204f62575;Subsection III.I.I;156;201
f846b0b9-01d4-4f6d-82a4-d79204f62577;f846b0b9-01d4-4f6d-82a4-d79204f62575;Subsection III.I.II;173;201
f846b0b9-01d4-4f6d-82a4-d79204f62578;f846b0b9-01d4-4f6d-82a4-d79204f62574;Section III.II;185;201
f846b0b9-01d4-4f6d-82a4-d79204f62579;;Chapter IV;203;201
f846b0b9-01d4-4f6d-82a4-d79204f62580;;Acknowledgments;250;201
f846b0b9-01d4-4f6d-82a4-d79204f62582;;Foreword;1;252
f846b0b9-01d4-4f6d-82a4-d79204f62583;;Chapter 1;3;252
f846b0b9-01d4-4f6d-82a4-d79204f62584;f846b0b9-01d4-4f6d-82a4-d79204f62583;Section 1.1;5;252
f846b0b9-01d4-4f6d-82a4-d79204f62585;f846b0b9-01d4-4f6d-82a4-d79204f62583;Section 1.2;8;252
f846b0b9-01d4-4f6d-82a4-d79204f62586;;Chapter 2;10;252
f846b0b9-01d4-4f6d-82a4-d79204f62587;f846b0b9-01d4-4f6d-82a4-d79204f62586;Section 2.1;12;252
f846b0b9-01d4-4f6d-82a4-d79204f62588;f846b0b9-01d4-4f6d-82a4-d79204f62587;Subsection 2.1.1;14;252
f846b0b9-01d4-4f6d-82a4-d79204f62589;f846b0b9-01d4-4f6d-82a4-d79204f62587;Subsection 2.1.2;16;252
f846b0b9-01d4-4f6d-82a4-d79204f62590;f846b0b9-01d4-4f6d-82a4-d79204f62586;Section 2.2;18;252
f846b0b9-01d4-4f6d-82a4-d79204f62591;f846b0b9-01d4-4f6d-82a4-d79204f62590;Subsection 2.2.1;21;252
f846b0b9-01d4-4f6d-82a4-d79204f62592;;Endnotes;25;252
1 ID parent_ID name page book_ID
2 f846b0b9-01d4-4f6d-82a4-d79204f62514 Foreword 3 207
3 f846b0b9-01d4-4f6d-82a4-d79204f62515 Chapter 1 4 207
4 f846b0b9-01d4-4f6d-82a4-d79204f62516 f846b0b9-01d4-4f6d-82a4-d79204f62515 Section 1.1 5 207
5 f846b0b9-01d4-4f6d-82a4-d79204f62517 f846b0b9-01d4-4f6d-82a4-d79204f62515 Section 1.2 28 207
6 f846b0b9-01d4-4f6d-82a4-d79204f62518 f846b0b9-01d4-4f6d-82a4-d79204f62517 Subsection 1.2.1 30 207
7 f846b0b9-01d4-4f6d-82a4-d79204f62519 f846b0b9-01d4-4f6d-82a4-d79204f62517 Subsection 1.2.2 33 207
8 f846b0b9-01d4-4f6d-82a4-d79204f62520 f846b0b9-01d4-4f6d-82a4-d79204f62515 Section 1.3 36 207
9 f846b0b9-01d4-4f6d-82a4-d79204f62521 Chapter 2 54 207
10 f846b0b9-01d4-4f6d-82a4-d79204f62522 f846b0b9-01d4-4f6d-82a4-d79204f62521 Section 2.1 56 207
11 f846b0b9-01d4-4f6d-82a4-d79204f62523 f846b0b9-01d4-4f6d-82a4-d79204f62521 Section 2.2 58 207
12 f846b0b9-01d4-4f6d-82a4-d79204f62524 Conclusion 63 207
13 f846b0b9-01d4-4f6d-82a4-d79204f62525 Endnotes 65 207
14 f846b0b9-01d4-4f6d-82a4-d79204f62526 Copyright notice 2 251
15 f846b0b9-01d4-4f6d-82a4-d79204f62527 Chapter 1 3 251
16 f846b0b9-01d4-4f6d-82a4-d79204f62528 f846b0b9-01d4-4f6d-82a4-d79204f62527 Section 1.1 5 251
17 f846b0b9-01d4-4f6d-82a4-d79204f62529 f846b0b9-01d4-4f6d-82a4-d79204f62527 Section 1.2 13 251
18 f846b0b9-01d4-4f6d-82a4-d79204f62530 Chapter 2 20 251
19 f846b0b9-01d4-4f6d-82a4-d79204f62531 f846b0b9-01d4-4f6d-82a4-d79204f62530 Section 2.1 21 251
20 f846b0b9-01d4-4f6d-82a4-d79204f62532 f846b0b9-01d4-4f6d-82a4-d79204f62530 Section 2.2 25 251
21 f846b0b9-01d4-4f6d-82a4-d79204f62533 f846b0b9-01d4-4f6d-82a4-d79204f62530 Section 2.3 27 251
22 f846b0b9-01d4-4f6d-82a4-d79204f62534 Chapter 3 30 251
23 f846b0b9-01d4-4f6d-82a4-d79204f62535 Endnotes 41 251
24 f846b0b9-01d4-4f6d-82a4-d79204f62551 Acknowledgements 1 271
25 f846b0b9-01d4-4f6d-82a4-d79204f62552 The Flight 2 271
26 f846b0b9-01d4-4f6d-82a4-d79204f62553 Hexwood Farm 8 271
27 f846b0b9-01d4-4f6d-82a4-d79204f62554 f846b0b9-01d4-4f6d-82a4-d79204f62553 Castle Saburac 13 271
28 f846b0b9-01d4-4f6d-82a4-d79204f62555 f846b0b9-01d4-4f6d-82a4-d79204f62553 The Curse of Rapkyn 27 271
29 f846b0b9-01d4-4f6d-82a4-d79204f62556 f846b0b9-01d4-4f6d-82a4-d79204f62553 The Mannikin 35 271
30 f846b0b9-01d4-4f6d-82a4-d79204f62557 The Eye of Time 44 271
31 f846b0b9-01d4-4f6d-82a4-d79204f62558 The Enchanting 59 271
32 f846b0b9-01d4-4f6d-82a4-d79204f62559 f846b0b9-01d4-4f6d-82a4-d79204f62558 The Telling Bone 73 271
33 f846b0b9-01d4-4f6d-82a4-d79204f62560 f846b0b9-01d4-4f6d-82a4-d79204f62558 The Power of Adamcos 86 271
34 f846b0b9-01d4-4f6d-82a4-d79204f62561 f846b0b9-01d4-4f6d-82a4-d79204f62558 The House of the Sorcerer 98 271
35 f846b0b9-01d4-4f6d-82a4-d79204f62562 The Changeling 105 271
36 f846b0b9-01d4-4f6d-82a4-d79204f62563 f846b0b9-01d4-4f6d-82a4-d79204f62562 The Flying Broomsticks 118 271
37 f846b0b9-01d4-4f6d-82a4-d79204f62564 f846b0b9-01d4-4f6d-82a4-d79204f62563 The Fish Out of Water 126 271
38 f846b0b9-01d4-4f6d-82a4-d79204f62565 The Final Magic 138 271
39 f846b0b9-01d4-4f6d-82a4-d79204f62566 Copyright 159 271
40 f846b0b9-01d4-4f6d-82a4-d79204f62567 Editor's Note 1 201
41 f846b0b9-01d4-4f6d-82a4-d79204f62568 Chapter I 2 201
42 f846b0b9-01d4-4f6d-82a4-d79204f62569 Chapter II 31 201
43 f846b0b9-01d4-4f6d-82a4-d79204f62570 f846b0b9-01d4-4f6d-82a4-d79204f62569 Section II.I 47 201
44 f846b0b9-01d4-4f6d-82a4-d79204f62571 f846b0b9-01d4-4f6d-82a4-d79204f62569 Section II.II 62 201
45 f846b0b9-01d4-4f6d-82a4-d79204f62572 f846b0b9-01d4-4f6d-82a4-d79204f62569 Section II.III 75 201
46 f846b0b9-01d4-4f6d-82a4-d79204f62573 f846b0b9-01d4-4f6d-82a4-d79204f62569 Section II.IV 87 201
47 f846b0b9-01d4-4f6d-82a4-d79204f62574 Chapter III 105 201
48 f846b0b9-01d4-4f6d-82a4-d79204f62575 f846b0b9-01d4-4f6d-82a4-d79204f62574 Section III.I 128 201
49 f846b0b9-01d4-4f6d-82a4-d79204f62576 f846b0b9-01d4-4f6d-82a4-d79204f62575 Subsection III.I.I 156 201
50 f846b0b9-01d4-4f6d-82a4-d79204f62577 f846b0b9-01d4-4f6d-82a4-d79204f62575 Subsection III.I.II 173 201
51 f846b0b9-01d4-4f6d-82a4-d79204f62578 f846b0b9-01d4-4f6d-82a4-d79204f62574 Section III.II 185 201
52 f846b0b9-01d4-4f6d-82a4-d79204f62579 Chapter IV 203 201
53 f846b0b9-01d4-4f6d-82a4-d79204f62580 Acknowledgments 250 201
54 f846b0b9-01d4-4f6d-82a4-d79204f62582 Foreword 1 252
55 f846b0b9-01d4-4f6d-82a4-d79204f62583 Chapter 1 3 252
56 f846b0b9-01d4-4f6d-82a4-d79204f62584 f846b0b9-01d4-4f6d-82a4-d79204f62583 Section 1.1 5 252
57 f846b0b9-01d4-4f6d-82a4-d79204f62585 f846b0b9-01d4-4f6d-82a4-d79204f62583 Section 1.2 8 252
58 f846b0b9-01d4-4f6d-82a4-d79204f62586 Chapter 2 10 252
59 f846b0b9-01d4-4f6d-82a4-d79204f62587 f846b0b9-01d4-4f6d-82a4-d79204f62586 Section 2.1 12 252
60 f846b0b9-01d4-4f6d-82a4-d79204f62588 f846b0b9-01d4-4f6d-82a4-d79204f62587 Subsection 2.1.1 14 252
61 f846b0b9-01d4-4f6d-82a4-d79204f62589 f846b0b9-01d4-4f6d-82a4-d79204f62587 Subsection 2.1.2 16 252
62 f846b0b9-01d4-4f6d-82a4-d79204f62590 f846b0b9-01d4-4f6d-82a4-d79204f62586 Section 2.2 18 252
63 f846b0b9-01d4-4f6d-82a4-d79204f62591 f846b0b9-01d4-4f6d-82a4-d79204f62590 Subsection 2.2.1 21 252
64 f846b0b9-01d4-4f6d-82a4-d79204f62592 Endnotes 25 252

View File

@@ -1,43 +1,16 @@
ID,parent_ID,name
10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,,Fiction
11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Drama
12aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Poetry
13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Fantasy
131aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Fairy Tale
132aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Epic Fantasy
133aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,High Fantasy
134aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Gothic
14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Science Fiction
141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Utopian and Dystopian
1411aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Utopian
1412aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Dystopian
14121aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1412aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Cyberpunk
141211aa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14121aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Steampunk
142aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Space Opera
143aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Time Travel
144aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Tech Noir
15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Romance
151aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Contemporary Romance
152aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Historical Romance
153aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Romantic Suspense
16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Mystery
161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Crime
1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Thriller
16111aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Police Procedural
16112aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Legal Thriller
16113aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Medical Thriller
16114aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Spy Thriller
1612aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Detective
1613aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Suspense
162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Noir
1621aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Nordic Noir
1622aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Tart Noir
163aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Cozy Mystery
17aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Adventure
18aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Short Story
19aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Graphic Novel
20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,,Non-Fiction
21aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Biography
22aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,21aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Autobiography
23aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Essay
24aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Speech
ID;parent_ID;name
10;;Fiction
11;10;Drama
12;10;Poetry
13;10;Fantasy
14;10;Science Fiction
15;10;Romance
16;10;Mystery
17;10;Thriller
18;10;Dystopia
19;10;Fairy Tale
20;;Non-Fiction
21;20;Biography
22;21;Autobiography
23;20;Essay
24;20;Speech
1 ID parent_ID name
2 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Fiction
3 11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 11 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Drama
4 12aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 12 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Poetry
5 13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 13 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Fantasy
6 131aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 14 13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Fairy Tale Science Fiction
7 132aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 15 13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Epic Fantasy Romance
8 133aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 16 13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 High Fantasy Mystery
9 134aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 17 13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Gothic Thriller
10 14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 18 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Science Fiction Dystopia
11 141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 19 14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10 Utopian and Dystopian Fairy Tale
12 1411aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 20 141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Utopian Non-Fiction
13 1412aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 21 141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 20 Dystopian Biography
14 14121aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 22 1412aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 21 Cyberpunk Autobiography
15 141211aa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 23 14121aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 20 Steampunk Essay
16 142aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 24 14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 20 Space Opera Speech
143aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Time Travel
144aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Tech Noir
15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Romance
151aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Contemporary Romance
152aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Historical Romance
153aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Romantic Suspense
16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Mystery
161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Crime
1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Thriller
16111aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Police Procedural
16112aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Legal Thriller
16113aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Medical Thriller
16114aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Spy Thriller
1612aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Detective
1613aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Suspense
162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Noir
1621aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Nordic Noir
1622aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Tart Noir
163aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Cozy Mystery
17aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Adventure
18aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Short Story
19aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Graphic Novel
20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Non-Fiction
21aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Biography
22aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 21aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Autobiography
23aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Essay
24aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa 20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa Speech

View File

@@ -1,21 +0,0 @@
const cds = require('@sap/cds')
/**
* In order to keep basic bookshop sample as simple as possible, we don't add
* reuse dependencies. This db/init.js ensures we still have a minimum set of
* currencies, if not obtained through @capire/common.
*/
// NOTE: We use cds.on('served') to delay the UPSERTs after the db init
// to run after all INSERTs from .csv files happened.
module.exports = cds.on('served', ()=>
UPSERT.into ('sap.common.Currencies') .columns (
[ 'code', 'symbol', 'name' ]
) .rows (
[ 'EUR', '€', 'Euro' ],
[ 'USD', '$', 'US Dollar' ],
[ 'GBP', '£', 'British Pound' ],
[ 'ILS', '₪', 'Shekel' ],
[ 'JPY', '¥', 'Yen' ],
)
)

View File

@@ -2,48 +2,30 @@ using { Currency, managed, sap } from '@sap/cds/common';
namespace sap.capire.bookshop;
entity Books : managed {
key ID : Integer;
title : localized String(111) @mandatory;
descr : localized String(1111);
author : Association to Authors @mandatory;
genre : Association to Genres;
stock : Integer;
price : Price;
key ID : Integer;
title : localized String(111);
descr : localized String(1111);
author : Association to Authors;
genre : Association to Genres;
stock : Integer;
price : Decimal;
currency : Currency;
image : LargeBinary @Core.MediaType: 'image/png';
contents : Composition of many Contents on contents.book = $self;
image : LargeBinary @Core.MediaType : 'image/png';
}
entity Authors : managed {
key ID : Integer;
name : String(111) @mandatory;
key ID : Integer;
name : String(111);
dateOfBirth : Date;
dateOfDeath : Date;
placeOfBirth : String;
placeOfDeath : String;
books : Association to many Books on books.author = $self;
books : Association to many Books on books.author = $self;
}
/** Hierarchically organized Code List for Genres */
entity Genres : sap.common.CodeList {
key ID : UUID;
key ID : Integer;
parent : Association to Genres;
children : Composition of many Genres on children.parent = $self;
}
/** Hierarchically organized entity for Contents */
entity Contents {
key ID : UUID;
name : String;
page : Integer;
parent : Association to Contents;
children : Composition of many Contents on children.parent = $self;
book : Association to Books;
}
type Price : Decimal(9,2);
// ------------------------------------------------------------------
// temporary workaround for reuse in fiori sample and hana deployment
annotate Books with @fiori.draft.enabled;

View File

@@ -2,4 +2,3 @@ namespace sap.capire.bookshop; //> important for reflection
using from './db/schema';
using from './srv/cat-service';
using from './srv/admin-service';
using from './srv/user-service';

1
bookshop/index.js Normal file
View File

@@ -0,0 +1 @@
exports.CatalogService = require('./srv/cat-service')

View File

@@ -2,22 +2,22 @@
"name": "@capire/bookshop",
"version": "1.0.0",
"description": "A simple self-contained bookshop service.",
"files": [
"app",
"srv",
"db",
"index.cds"
],
"devDependencies": {
"@cap-js/sqlite": ">=1"
},
"dependencies": {
"@sap/cds": ">=7",
"express": "^4.17.1"
"@capire/common": "*",
"@sap/cds": "^5.0.4",
"express": "^4.17.1",
"passport": "0.4.1"
},
"scripts": {
"genres": "cds serve test/genres.cds",
"start": "cds-serve",
"start": "cds run",
"watch": "cds watch"
},
"cds": {
"requires": {
"db": {
"kind": "sql"
}
}
}
}

View File

@@ -20,12 +20,12 @@ npm run watch
| Links to capire | Sample files / folders |
| --------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| [Project Setup & Layouts](https://cap.cloud.sap/docs/get-started/jumpstart#project-structure) | [`./`](./) |
| [Domain Modeling with CDS](https://cap.cloud.sap/docs/guides/domain-modeling) | [`./db/schema.cds`](./db/schema.cds) |
| [Defining Services](https://cap.cloud.sap/docs/guides/providing-services#modeling-services) | [`./srv/*.cds`](./srv) |
| [Single-purposed Services](https://cap.cloud.sap/docs/guides/providing-services#single-purposed-services) | [`./srv/*.cds`](./srv) |
| [Project Setup & Layouts](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./`](./) |
| [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/services#defining-services) | [`./srv/*.cds`](./srv) |
| [Single-purposed Services](https://cap.cloud.sap/docs/guides/services#single-purposed-services) | [`./srv/*.cds`](./srv) |
| [Providing & Consuming Providers](https://cap.cloud.sap/docs/guides/providing-services) | http://localhost:4004 |
| [Using Databases](https://cap.cloud.sap/docs/guides/databases) | [`./db/data/*.csv`](./db/data) |
| [Adding Custom Logic](https://cap.cloud.sap/docs/guides/providing-services#adding-custom-logic) | [`./srv/*.js`](./srv) |
| [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) | [`./srv/*.js`](./srv) |
| Adding Tests | [`./test`](./test) |
| [Sharing for Reuse](https://cap.cloud.sap/docs/guides/extensibility/composition) | [`./index.cds`](./index.cds) |
| [Sharing for Reuse](https://cap.cloud.sap/docs/guides/reuse-and-compose) | [`./index.cds`](./index.cds) |

View File

@@ -1,2 +0,0 @@
using { AdminService } from './admin-service';
annotate AdminService with @requires:'admin';

View File

@@ -1,7 +1,5 @@
using { sap.capire.bookshop as my } from '../db/schema';
service AdminService @(path:'/admin') {
entity Authors as projection on my.Authors;
service AdminService @(requires:'admin') {
entity Books as projection on my.Books;
entity Genres as projection on my.Genres;
entity Contents as projection on my.Contents;
entity Authors as projection on my.Authors;
}

View File

@@ -1,14 +1,12 @@
const cds = require('@sap/cds')
module.exports = class AdminService extends cds.ApplicationService { init(){
this.before (['NEW','CREATE'],'Authors', genid)
this.before (['NEW','CREATE'],'Books', genid)
return super.init()
}}
module.exports = cds.service.impl (function(){
this.before ('NEW','Authors', genid)
this.before ('NEW','Books', genid)
})
/** Generate primary keys for target entity in request */
async function genid (req) {
if (req.data.ID) return
const {id} = await SELECT.one.from(req.target).columns('max(ID) as id')
req.data.ID = id + 4 // Note: that is not safe! ok for this sample only.
const {ID} = await cds.tx(req).run (SELECT.one.from(req.target).columns('max(ID) as ID'))
req.data.ID = ID - ID % 100 + 100 + 1
}

View File

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

View File

@@ -1,38 +1,26 @@
const cds = require('@sap/cds')
const { Books } = cds.entities ('sap.capire.bookshop')
class CatalogService extends cds.ApplicationService { init() {
const { Books } = cds.entities('sap.capire.bookshop')
const { ListOfBooks } = this.entities
// Add some discount for overstocked books
this.after('each', ListOfBooks, book => {
if (book.stock > 111) book.title += ` -- 11% discount!`
})
class CatalogService extends cds.ApplicationService { init(){
// Reduce stock of ordered books if available stock suffices
this.on('submitOrder', async req => {
let { book:id, quantity } = req.data
let book = await SELECT.from (Books, id, b => b.stock)
// Validate input data
if (!book) return req.error (404, `Book #${id} doesn't exist`)
if (quantity < 1) return req.error (400, `quantity has to be 1 or more`)
if (quantity > book.stock) return req.error (409, `${quantity} exceeds stock for book #${id}`)
// Reduce stock in database and return updated stock value
await UPDATE (Books, id) .with ({ stock: book.stock -= quantity })
return book
this.on ('submitOrder', async req => {
const {book,amount} = req.data
let {stock} = await SELECT `stock` .from (Books,book)
if (stock >= amount) {
await UPDATE (Books,book) .with (`stock -=`, amount)
await this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
return { stock }
}
else return req.error (409,`${amount} exceeds stock for book #${book}`)
})
// Emit event when an order has been submitted
this.after('submitOrder', async (_,req) => {
let { book, quantity } = req.data
await this.emit('OrderedBook', { book, quantity, buyer: req.user.id })
// Add some discount for overstocked books
this.after ('READ','ListOfBooks', each => {
if (each.stock > 111) each.title += ` -- 11% discount!`
})
// Delegate requests to the underlying generic service
return super.init()
}}
module.exports = CatalogService
module.exports = { CatalogService }

View File

@@ -1,15 +0,0 @@
/**
* Exposes user information
*/
service UserService @(path: '/user') {
/**
* The current user
*/
@odata.singleton entity me @cds.persistence.skip {
id : String; // user id
locale : String;
tenant : String;
}
action login() returns me;
}

View File

@@ -1,9 +0,0 @@
const cds = require('@sap/cds')
module.exports = class UserService extends cds.Service { init(){
this.on('READ', 'me', ({ tenant, user, locale }) => ({ id: user.id, locale, tenant }))
this.on('login', (req) => {
if (req.user._is_anonymous)
req._.res.set('WWW-Authenticate','Basic realm="Users"').sendStatus(401)
else return this.read('me')
})
}}

View File

@@ -3,35 +3,36 @@
# Genres
#
GET http://localhost:4004/odata/v4/test/Genres?
GET http://localhost:4004/test/Genres?
###
GET http://localhost:4004/odata/v4/test/Genres?
GET http://localhost:4004/test/Genres?
&$filter=parent_ID eq null&$select=name
&$expand=children($select=name)
###
POST http://localhost:4004/odata/v4/test/Genres?
POST http://localhost:4004/test/Genres?
Content-Type: application/json
{ "ID":"100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Some Sample Genres...", "children":[
{ "ID":"101aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Cat", "children":[
{ "ID":"102aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Kitty", "children":[
{ "ID":"103aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Aristocat" },
{ "ID":"104aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Kitty Bat" } ]},
{ "ID":"105aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catwoman", "children":[
{ "ID":"106aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catalina" } ]} ]},
{ "ID":"107aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catweazle" }
{ "ID":100, "name":"Some Sample Genres...", "children":[
{ "ID":101, "name":"Cat", "children":[
{ "ID":102, "name":"Kitty", "children":[
{ "ID":103, "name":"Kitty Cat", "children":[
{ "ID":104, "name":"Aristocat" } ]},
{ "ID":105, "name":"Kitty Bat" } ]},
{ "ID":106, "name":"Catwoman", "children":[
{ "ID":107, "name":"Catalina" } ]} ]},
{ "ID":108, "name":"Catweazle" }
]}
###
GET http://localhost:4004/odata/v4/test/Genres(100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)?
&$expand=children
&$expand=children($expand=children($expand=children($expand=children)))
GET http://localhost:4004/test/Genres(100)?
# &$expand=children
# &$expand=children($expand=children($expand=children($expand=children)))
###
DELETE http://localhost:4004/odata/v4/test/Genres(103aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)
DELETE http://localhost:4004/test/Genres(103)
###
DELETE http://localhost:4004/odata/v4/test/Genres(100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)
DELETE http://localhost:4004/test/Genres(100)
###

View File

@@ -16,9 +16,9 @@ GET {{server}}/browse/$metadata
### ------------------------------------------------------------------------
# Browse Books as any user
GET {{server}}/browse/ListOfBooks?
GET {{server}}/browse/Books?
# &$select=title,stock
&$expand=genre
# &$expand=currency
# &sap-language=de
{{me}}
@@ -32,18 +32,6 @@ GET {{server}}/admin/Authors?
# &sap-language=de
Authorization: Basic alice:
### ------------------------------------------------------------------------
# Create Author
POST {{server}}/admin/Authors
Content-Type: application/json;IEEE754Compatible=true
Authorization: Basic alice:
{
"ID": 112,
"name": "Shakespeeeeere"
}
### ------------------------------------------------------------------------
# Create book
POST {{server}}/admin/Books
@@ -55,7 +43,7 @@ Authorization: Basic alice:
"title": "Poems : Pocket Poets",
"descr": "The Everyman's Library Pocket Poets hardcover series is popular for its compact size and reasonable price which does not compromise content. Poems: Bronte contains poems that demonstrate a sensibility elemental in its force with an imaginative discipline and flexibility of the highest order. Also included are an Editor's Note and an index of first lines.",
"author": { "ID": 101 },
"genre": { "ID": "12aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" },
"genre": { "ID": 12 },
"stock": 5,
"price": "12.05",
"currency": { "code": "USD" }
@@ -83,7 +71,7 @@ POST {{server}}/browse/submitOrder
Content-Type: application/json
{{me}}
{ "book":201, "quantity":5 }
{ "book":201, "amount":5 }
### ------------------------------------------------------------------------

View File

@@ -1,12 +0,0 @@
Books = Bücher
Book = Buch
Title = Titel
Description = Beschreibung
Stock = Bestand
Image = Bild
Price = Preis
Currency = Währung
Authors = Autoren
Author = Autor
AuthorID = ID des Autors

View File

@@ -1,20 +0,0 @@
Books = Livres
Book = Livre
Title = Titre
Description = Description
Stock = Action
Image = Image
Price = Prix
Currency = Devise
Authors = Auteurs
Author = Auteur
AuthorID = ID de l''auteur
Name = Nom
DateOfBirth = Date de naissance
DateOfDeath = Date de décès
PlaceOfBirth = Lieu de naissance
PlaceOfDeath = Lieu de décès
Genres = Genre
Genre = Genre

View File

@@ -1,34 +0,0 @@
400 = Ungültige Anfrage
401 = Nicht autorisiert
403 = Verboten
404 = Nicht gefunden
405 = Methode nicht zulässig
406 = Nicht akzeptabel
407 = Proxy-Authentifizierung erforderlich
408 = Anfrage-Timeout
409 = Konflikt
410 = Weg
411 = Erforderliche Länge
412 = Vorbedingung fehlgeschlagen
413 = Nutzlast zu groß
414 = URI zu lang
415 = Nicht unterstützter Medientyp
416 = Bereich nicht erfüllbar
417 = Erwartung fehlgeschlagen
422 = Nicht verarbeitbarer Inhalt
424 = Fehlgeschlagene Abhängigkeit
428 = Vorbedingung erforderlich
429 = Zu viele Anfragen
431 = Anfrage-Headerfelder zu groß
451 = Aus rechtlichen Gründen nicht verfügbar
500 = Interner Serverfehler
501 = Der Server unterstützt die zur Erfüllung der Anfrage erforderliche Funktionalität nicht
502 = Ungültiges Gateway
503 = Dienst nicht verfügbar
504 = Gateway-Timeout
ASSERT_RANGE = Wert {0} liegt nicht im angegebenen Bereich [{1}, {2}]
ASSERT_FORMAT = Wert "{0}" liegt nicht im angegebenen Format "{1}"
ASSERT_ARRAY = Wert muss ein Array sein
ASSERT_ENUM = Wert {0} ist gemäß Enumerationsdeklaration {{1}} ungültig
ASSERT_NOT_NULL = Wert ist erforderlich

View File

@@ -1,34 +0,0 @@
400 = Bad Request
401 = Unauthorized
403 = Forbidden
404 = Not Found
405 = Method Not Allowed
406 = Not Acceptable
407 = Proxy Authentication Required
408 = Request Timeout
409 = Conflict
410 = Gone
411 = Length Required
412 = Precondition Failed
413 = Payload Too Large
414 = URI Too Long
415 = Unsupported Media Type
416 = Range Not Satisfiable
417 = Expectation Failed
422 = Unprocessable Content
424 = Failed Dependency
428 = Precondition Required
429 = Too Many Requests
431 = Request Header Fields Too Large
451 = Unavailable For Legal Reasons
500 = Internal Server Error
501 = The server does not support the functionality required to fulfill the request
502 = Bad Gateway
503 = Service Unavailable
504 = Gateway Timeout
ASSERT_RANGE = Value {0} is not in specified range [{1}, {2}]
ASSERT_FORMAT = Value "{0}" is not in specified format "{1}"
ASSERT_ARRAY = Value must be an array
ASSERT_ENUM = Value {0} is invalid according to enum declaration {{1}}
ASSERT_NOT_NULL = Value is required

View File

@@ -1,34 +0,0 @@
400 = Requête incorrecte
401 = Non autorisée
403 = Interdite
404 = Introuvable
405 = Méthode non autorisée
406 = Non acceptable
407 = Authentification proxy requise
408 = Délai d''expiration de la requête
409 = Conflit
410 = Disparu
411 = Longueur requise
412 = Échec de la condition préalable
413 = Charge utile trop importante
414 = URI trop longue
415 = Type de média non pris en charge
416 = Plage non satisfaisante
417 = Échec de l''attente
422 = Contenu non traitable
424 = Dépendance échouée
428 = Condition préalable requise
429 = Trop de requêtes
431 = Champs d''en-tête de requête trop importants
451 = Indisponible pour des raisons juridiques
500 = Erreur interne du serveur
501 = Le serveur ne prend pas en charge la fonctionnalité requise pour répondre à la requête
502 = Passerelle incorrecte
503 = Service indisponible
504 = Délai d''attente de la passerelle
ASSERT_RANGE = La valeur {0} n''est pas dans la plage spécifiée [{1}, {2}]
ASSERT_FORMAT = La valeur "{0}" n''est pas au format spécifié "{1}"
ASSERT_ARRAY = La valeur doit être un tableau
ASSERT_ENUM = La valeur {0} n''est pas valide selon la déclaration d''énumération {{1}}
ASSERT_NOT_NULL = La valeur est obligatoire

View File

@@ -1 +0,0 @@
require('./srv/server')

View File

@@ -1,2 +0,0 @@
namespace sap.capire.bookshop; //> important for reflection
using from './srv/mashup';

View File

@@ -1,34 +0,0 @@
{
"name": "@capire/bookstore",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "*",
"@capire/reviews": "*",
"@capire/orders": "*",
"@capire/common": "*",
"@capire/data-viewer": "*",
"@sap-cloud-sdk/http-client": "^4",
"@sap-cloud-sdk/resilience": "^4",
"@sap/cds": ">=5",
"express": "^4.17.1",
"@cap-js/hana": ">=1"
},
"cds": {
"requires": {
"ReviewsService": {
"kind": "odata",
"model": "@capire/reviews"
},
"OrdersService": {
"kind": "odata",
"model": "@capire/orders"
},
"messaging": true,
"db": true
},
"log": { "service": true }
},
"scripts": {
"start": "cds-serve"
}
}

View File

@@ -1,31 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Enhancing bookshop with Reviews and Orders provided through
// respective reuse packages and services
//
//
// Extend Books with access to Reviews and average ratings
//
using { sap.capire.bookshop.Books } from '@capire/bookshop';
using { ReviewsService.Reviews } from '@capire/reviews';
extend Books with {
reviews : Composition of many Reviews on reviews.subject = $self.ID;
rating : type of Reviews:rating; // average rating
numberOfReviews : Integer @title : '{i18n>NumberOfReviews}';
}
//
// Extend Orders with Books as Products
//
using { sap.capire.orders.Orders } from '@capire/orders';
extend Orders:Items with {
book : Association to Books on product.ID = book.ID
}
// Ensure models from all imported packages are loaded
using from '@capire/orders/app/fiori';
using from '@capire/data-viewer';
using from '@capire/common';

View File

@@ -1,65 +0,0 @@
const cds = require ('@sap/cds')
// Add routes to UIs from imported packages
if (!cds.env.production) cds.once ('bootstrap', (app) => {
app.serve ('/bookshop') .from ('@capire/bookshop','app/vue')
app.serve ('/reviews') .from ('@capire/reviews','app/vue')
app.serve ('/orders') .from('@capire/orders','app/orders')
})
// Mashing up bookshop services with required services...
cds.once ('served', async ()=>{
const CatalogService = await cds.connect.to ('CatalogService')
const ReviewsService = await cds.connect.to ('ReviewsService')
const OrdersService = await cds.connect.to ('OrdersService')
const db = await cds.connect.to ('db')
// reflect entity definitions used below...
const { Books } = db.entities ('sap.capire.bookshop')
//
// Delegate requests to read reviews to the ReviewsService
// Note: prepend is neccessary to intercept generic default handler
//
CatalogService.prepend (srv => srv.on ('READ', 'Books/reviews', (req) => {
console.debug ('> delegating request to ReviewsService') // eslint-disable-line no-console
const [id] = req.params, { columns, limit } = req.query.SELECT
return ReviewsService.read ('Reviews',columns).limit(limit).where({subject:String(id)})
}))
//
// Create an order with the OrdersService when CatalogService signals a new order
//
CatalogService.on ('OrderedBook', async (msg) => {
const { book, quantity, buyer } = msg.data
const { title, price } = await db.read (Books, book, b => { b.title, b.price })
return OrdersService.create ('OrdersNoDraft').entries({
OrderNo: 'Order at '+ (new Date).toLocaleString(),
Items: [{ product:{ID:`${book}`}, title, price, quantity }],
buyer, createdBy: buyer
})
})
//
// Update Books' average ratings when ReviewsService signals updated reviews
//
ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data) // eslint-disable-line no-console
const { subject, count, rating } = msg.data
return UPDATE(Books,subject).with({ numberOfReviews:count, rating })
})
//
// Reduce stock of ordered books for orders are created from Orders admin UI
//
OrdersService.on ('OrderChanged', (msg) => {
console.debug ('> received:', msg.event, msg.data) // eslint-disable-line no-console
const { product, deltaQuantity } = msg.data
return UPDATE (Books) .where ('ID =', product)
.and ('stock >=', deltaQuantity)
.set ('stock -=', deltaQuantity)
})
})

View File

@@ -1 +0,0 @@
// dummy to auto-load the plugin

View File

@@ -1,13 +0,0 @@
using { sap } from '@sap/cds/common';
extend sap.common.Currencies with {
// Currencies.code = ISO 4217 alphabetic three-letter code
// with the first two letters being equal to ISO 3166 alphabetic country codes
// See also:
// [1] https://www.iso.org/iso-4217-currency-codes.html
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
numcode : Integer;
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
minor : String; //> e.g. 'Cent'
}

View File

@@ -1,2 +1,45 @@
using from './currencies';
using from './regions';
using { sap } from '@sap/cds/common';
extend sap.common.Currencies with {
// Currencies.code = ISO 4217 alphabetic three-letter code
// with the first two letters being equal to ISO 3166 alphabetic country codes
// See also:
// [1] https://www.iso.org/iso-4217-currency-codes.html
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
numcode : Integer;
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
minor : String; //> e.g. 'Cent'
}
/**
* The Code Lists below are designed as optional extensions to
* the base schema. Switch them on by adding an Association to
* one of the code list entities in your models or by:
* annotate sap.common.Countries with @cds.persistence.skip:false;
*/
context sap.common_countries {
extend sap.common.Countries {
regions : Composition of many Regions on regions._parent = $self.code;
}
entity Regions : sap.common.CodeList {
key code : String(5); // ISO 3166-2 alpha5 codes, e.g. DE-BW
children : Composition of many Regions on children._parent = $self.code;
cities : Composition of many Cities on cities.region = $self;
_parent : String(11);
}
entity Cities : sap.common.CodeList {
key code : String(11);
region : Association to Regions;
districts : Composition of many Districts on districts.city = $self;
}
entity Districts : sap.common.CodeList {
key code : String(11);
city : Association to Cities;
}
}

View File

@@ -4,12 +4,5 @@
"version": "1.0.0",
"dependencies": {
"@sap/cds": "*"
},
"cds": {
"requires": {
"@capire/common/data": {
"model": "@capire/common"
}
}
}
}

View File

@@ -1,22 +0,0 @@
using { sap.common } from '@sap/cds/common';
namespace sap.common.countries;
extend common.Countries {
regions : Composition of many Regions on regions._parent = $self.code;
}
entity Regions : common.CodeList {
key code : String(5); // ISO 3166-2 alpha5 codes, e.g. DE-BW
children : Composition of many Regions on children._parent = $self.code;
cities : Composition of many Cities on cities.region = $self;
_parent : String(11);
}
entity Cities : common.CodeList {
key code : String(11);
region : Association to Regions;
districts : Composition of many Districts on districts.city = $self;
}
entity Districts : common.CodeList {
key code : String(11);
city : Association to Cities;
}

View File

@@ -1,20 +0,0 @@
import cds from '@sap/cds/eslint.config.mjs'
export default [ ...cds.recommended ]
//
// The above is the recommended minimalistic eslint config, just using
// recommended defaults provided by cds. Alternatively, go for enhanced
// project-specific options, but only if really required, like this:
//
// export default [ ...cds.recommended, {
// rules: {
// 'complexity': [ 'warn', 66 ],
// 'require-await': 'warn',
// 'no-await-in-loop': 'warn',
// },
// }, {
// ignores: [
// '**/webapp/**'
// ]
// }]
//

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 67 KiB

View File

@@ -1,195 +0,0 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="357px" height="283px" viewBox="-0.5 -0.5 357 283" content="&lt;mxfile&gt;&lt;diagram id=&quot;QQJxv4aCTC7ZgE7HHOvM&quot; name=&quot;Page-1&quot;&gt;7VrZcqM4FP0av7q0L4+T9DIvU9VVeZhnFtlQwRYly7HTX98XgzBgOpNJG7eZGlKVwNECuudwdSSyoI+b41cXldlfNjXFgqD0uKCfFoQIIuF3BbzWACO6BtYuT2sInYGn/LupQRzQfZ6aXYPVkLe28HnZBxO73ZrE97DIOXvoV1vZIu0BZbQ2vceogKckKsxFtb/z1Gc1qsKwKvxPk6+zcGcsmvHFUfK8dna/be63IBQxXpdtotBRc9NdFqX20IHo5wV9dNb6+mxzfDRFFdd+zL78pLR9aGe2/j0NSN3gJSr2zbgXDCVRmTtTNSIi2pQL+rCNd9UfuC6g24fYwdnad5EAxNY+7zJbhgLoJT5XPg3Zv4YYw+jL6jQzx2htt1ClNC7fGG/cGf0WIAjOwyHLvXkqo6RqdgDhAZb5TQFXGE5X+dEELVXXu2fjkyri6FRYFI+2sO50c7paGZEkVSXv7LPplKRSxwi1JYF+0g6hG98m5C/GeXPsQE28vxoLD+9eoUpTSiSrm4QXQ9L6+nCWmW7kkXUUphosaoS9bns+8wsnDcXjdNNJ6Pa2an73fKvEjPMdK874zfimktyMb3Ztvp15yQ2k1rtnO+VGpWyMbUViKsRUbFOB+2xjfTO2+bXZTuxmA8TdPdkrXv1UuN36Dl4fYyIQp+NWImDhRbyBCMS1RWBdatwc3vjIqNVofheJMvFqKrKxEr+NbHlJ9pATs03/qOwwXCVFtNvlST/EvZBeBgiaN/GXp/LI+d71INRfTsdbgd3ZvUtML2FBp2vjew7FpMGd/yT4neDykeAGzJki8vlL39OPRby5wzebw/OeuSW4P3lzRPp91MNpmnWN9kVPYmD7+KCnOgoXPZ0k0A78XapQ81aFuHdVwKu75Iq2B+knAKrJslNKqfyYZIhmS4ZFewzXDQgvO6VC0Kn0pOetJzJzPTH+NtHX0hN7W7bX01PY4pmroNjMBcWHgppET3ygJzaZnIJlu5rl3e3Lssj/d73/wvXecp2LyUj+GFD4BJ2A4tD9M2hwys1oVtJC0miyRaoeEHjDbSk8sg85pwkgCLA7A4QsNI8pYOgp5CA7T+QpyGCqueIkMLLVOXdJibtSFAaqJVKcICw1ZIuw+dAuRzQoQSKGKFeSsY+aVIyXRGMmtSJIEBk+jrRrKw2yJpprwTAYmek86shm6qz0FL6Z3q1JxVpAClKnL8MaCc4HmUMuYaUDVhXWuUroD267EFClolhKLhHSlA1uwtmSUsGY4lphitRkarr6ruwqty7/T5ubCyczovl3f3Qjt3Q3974rO0gA/5hL6IjbEb/V3Ei9FFIpyBuKcKF0f5KozA3lEqYQLDimUvyCuaGQSSFzYMkoHtwFvI1gkGGwokwJWHBdKXvA5fkfQOrq5/+woZ9/AA==&lt;/diagram&gt;&lt;/mxfile&gt;" style="background-color: rgb(0, 68, 85);">
<defs/>
<g>
<path d="M 199 202 L 249 202 L 269 242 L 249 282 L 199 282 L 179 242 Z" fill="#ffe6cc" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 242px; margin-left: 180px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
@capire/
<br/>
<b>
bookshop
</b>
</div>
</div>
</div>
</foreignObject>
<text x="224" y="246" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
@capire/...
</text>
</switch>
</g>
<path d="M 199 101 L 249 101 L 269 141 L 249 181 L 199 181 L 179 141 Z" fill="#f8cecc" stroke="#b85450" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 141px; margin-left: 180px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
@capire/
<br/>
<b>
bookstore
</b>
</div>
</div>
</div>
</foreignObject>
<text x="224" y="145" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
@capire/...
</text>
</switch>
</g>
<path d="M 286 48 L 336 48 L 356 88 L 336 128 L 286 128 L 266 88 Z" fill="#d5e8d4" stroke="#82b366" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 88px; margin-left: 267px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
@capire/
<br/>
<b>
reviews
</b>
</div>
</div>
</div>
</foreignObject>
<text x="311" y="92" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
@capire/...
</text>
</switch>
</g>
<path d="M 286 153 L 336 153 L 356 193 L 336 233 L 286 233 L 266 193 Z" fill="#f5f5f5" stroke="#666666" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 193px; margin-left: 267px;">
<div data-drawio-colors="color: #333333; " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(51, 51, 51); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
@capire/
<br/>
<b>
common
</b>
</div>
</div>
</div>
</foreignObject>
<text x="311" y="197" fill="#333333" font-family="Helvetica" font-size="12px" text-anchor="middle">
@capire/...
</text>
</switch>
</g>
<path d="M 111 153 L 161 153 L 181 193 L 161 233 L 111 233 L 91 193 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 193px; margin-left: 92px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
@capire/
<br/>
<b>
orders
</b>
</div>
</div>
</div>
</foreignObject>
<text x="136" y="197" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
@capire/...
</text>
</switch>
</g>
<path d="M 276.35 172.29 L 266.36 166.32" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 260.57 162.86 L 270.6 163.61 L 266.36 166.32 L 265.98 171.34 Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 170.74 172.47 L 181.53 166.1" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 187.34 162.66 L 181.88 171.12 L 181.53 166.1 L 177.3 163.37 Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 224 202 L 224 189.99" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 224 183.24 L 228.5 192.24 L 224 189.99 L 219.5 192.24 Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 276.51 109.01 L 266.17 115.31" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 260.4 118.82 L 265.75 110.3 L 266.17 115.31 L 270.43 117.98 Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 111 48 L 161 48 L 181 88 L 161 128 L 111 128 L 91 88 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 88px; margin-left: 92px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
@capire/
<br/>
<b>
suppliers
</b>
</div>
</div>
</div>
</foreignObject>
<text x="136" y="92" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
@capire/...
</text>
</switch>
</g>
<path d="M 21 101 L 71 101 L 91 141 L 71 181 L 21 181 L 1 141 Z" fill="#e1d5e7" stroke="#9673a6" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 141px; margin-left: 2px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
S/4
</b>
</div>
</div>
</div>
</foreignObject>
<text x="46" y="145" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
S/4
</text>
</switch>
</g>
<path d="M 80.76 120.53 L 93.49 113.03" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 99.31 109.61 L 93.84 118.05 L 93.49 113.03 L 89.27 110.3 Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 80.91 161.17 L 93.31 168.33" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 99.15 171.71 L 89.11 171.1 L 93.31 168.33 L 93.61 163.31 Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 170.59 108.83 L 181.72 115.53" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 187.5 119.02 L 177.47 118.23 L 181.72 115.53 L 182.11 110.52 Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 199 1 L 249 1 L 269 41 L 249 81 L 199 81 L 179 41 Z" fill="#e1d5e7" stroke="#9673a6" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 41px; margin-left: 180px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
@capire/
<br/>
<b>
fiori
</b>
</div>
</div>
</div>
</foreignObject>
<text x="224" y="45" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
@capire/...
</text>
</switch>
</g>
<path d="M 224 101 L 224 89.99" fill="none" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 224 83.24 L 228.5 92.24 L 224 89.99 L 219.5 92.24 Z" fill="#ffffff" stroke="#ffffff" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

Before

Width:  |  Height:  |  Size: 16 KiB

View File

@@ -1,119 +0,0 @@
/* global Vue axios */ //> from vue.html
const GET = (url) => axios.get('/odata/v4/-data'+url)
const storageGet = (key, def) => localStorage.getItem('data-viewer:'+key) || def
const storageSet = (key, val) => localStorage.setItem('data-viewer:'+key, val)
const columnKeysFirst = (c1, c2) => {
if (c1.isKey && !c2.isKey) return -1
if (!c1.isKey && c2.isKey) return 1
if (c1.isKey && c2.isKey) return c1.name.localeCompare(c2.name)
return 0 // retain natural order of normal columns
}
const vue = Vue.createApp ({
data() { return {
error: undefined,
dataSource: storageGet('data-source', 'db'),
skip: storageGet('skip', 0),
top: storageGet('top', 20),
entity: storageGet('entity') ? JSON.parse(storageGet('entity')) : undefined,
entities: [],
columns: [],
data: [],
rowDetails: {},
rowKey: storageGet('rowKey')
}},
watch: {
dataSource: (v) => { storageSet('data-source', v); vue.fetchEntities() },
skip: (v) => { storageSet('skip', v); if (vue.entity) vue.fetchData() },
top: (v) => { storageSet('top', v); if (vue.entity) vue.fetchData() },
},
methods: {
async fetchEntities () {
let url = `/Entities`
if (vue.dataSource === 'db') url += `?dataSource=db`
const {data} = await GET(url)
vue.entities = data.value
vue.entities.forEach(entity => entity.columns.sort(columnKeysFirst))
const entity = vue.entity && vue.entities.find(e => e.name === vue.entity.name)
if (entity) { // restore selection from previous fetch
vue.columns = entity.columns
await vue.fetchData(entity)
} else {
vue.entity = undefined
vue.columns = []
vue.data = []
vue.rowDetails = {}
}
},
async inspectEntity (eve) {
const entity = vue.entity = vue.entities [eve.currentTarget.rowIndex-1]
storageSet('entity', JSON.stringify(entity))
vue.columns = vue.entities.find(e => e.name === entity.name).columns
return await this.fetchData()
},
async fetchData () {
let url = `/Data?entity=${vue.entity.name}&$skip=${vue.skip}&$top=${vue.top}`
if (vue.dataSource === 'db') url += `&dataSource=db`
try {
const {data} = await GET(url)
// sort data along column order
const columnIndexes = {}
vue.columns.forEach((col, i) => columnIndexes[col.name] = i)
vue.data = data.value.map(d => d.record
.sort((r1, r2) => columnIndexes[r1.column] - columnIndexes[r2.column])
.map(r => r.data)
)
const row = vue.data.find(data => vue._makeRowKey(data) === vue.rowKey)
if (row) vue._setRowDetails(row)
else vue.rowDetails = {}
vue.error = undefined
} catch (err) {
vue.data = []
vue.rowDetails = {}
if (err.response?.data?.error) {
vue.error = err.response.data.error
} else {
vue.error = { code:err.code, message:err.message }
}
}
},
inspectRow (eve) {
vue.rowDetails = {}
const selectedRow = eve.currentTarget.rowIndex-1
vue.rowKey = vue._makeRowKey(vue.data[selectedRow])
storageSet('rowKey', vue.rowKey)
vue._setRowDetails(vue.data[selectedRow])
},
_setRowDetails(row) {
vue.rowDetails = {}
row.forEach((line, colIndex) => {
vue.rowDetails[vue.columns[colIndex].name] = line
})
},
_makeRowKey(row) {
// to identify a row, build a key string out of all key columns' values
return row
.filter((_, colIndex) => vue.columns[colIndex] && vue.columns[colIndex].isKey)
.reduce(((prev, next) => prev += next), '')
},
isActiveRow(row) {
return vue._makeRowKey(row) === vue.rowKey
}
}
})
.mount('#app')
vue.fetchEntities()

View File

@@ -1,95 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title>Data Browser</title>
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<script src="app.js" defer></script>
<style>
th { position: sticky; top:0; z-index: 2; background-color: white; }
.noscroll { overflow: hidden; }
.hovering tr:hover td { background: #ebeefc; cursor: pointer; }
.highlight { background: #ebeefc !important; }
.rating-stars { color:teal }
.succeeded { color:teal }
.failed { color:red }
.condensed { max-width: 100px; text-overflow: ellipsis; white-space: nowrap; }
.key { font-weight: bold }
.not-key { font-weight: lighter;}
.with-sidebar { display: flex; flex-wrap: wrap; gap: 1rem; }
.sidebar { flex-basis: 20rem; flex-grow: 1; }
.sidebar-main { height: 100vh; overflow-y: scroll; }
.not-sidebar { flex-basis: 0; flex-grow: 999; min-inline-size: 50%; align-items: stretch;}
.not-sidebar-main { max-height: 40vh; overflow-y: scroll; }
.not-sidebar-sub { max-height: 40vh; overflow-y: scroll; }
.horizontal label { display: inline; }
.horizontal input { width: initial; display: inline; }
.error { color: red; }
</style>
</head>
<body class="noscroll">
<div id='app' class="full-container">
<h1>Data Browser &ndash; {{ entity ? entity.name : '' }}</h1>
<div class="with-sidebar">
<div class="sidebar">
<div class="horizontal" style="padding: 0.75rem 0;">
<label>Datasource:</label>
<input type="radio" id="dataSource-db" value="db" v-model="dataSource">
<label for="dataSource-db">Database</label>
<input type="radio" id="dataSource-srv" value="service" v-model="dataSource">
<label for="dataSource-srv">Service</label>
</div>
<div class="sidebar-main">
<table id='entities' class="hovering">
<thead>
<th>Entity Name</th>
</thead>
<tr v-for="e in entities" :key="e.name" @click="inspectEntity" :class="{'highlight': (entity && e.name === entity.name)}">
<td>{{ e.name }}</td>
</tr>
</table>
</div>
</div>
<div class="not-sidebar">
<div class="horizontal">
<label for="skip">Skip:</label>
<input id="skip" v-model.lazy="skip" title="No. of entries to skip" type="number" min="0">
<label for="top">Top:</label>
<input id="top" v-model.lazy="top" title="No. of entries to read" type="number" min="0">
</div>
<div v-if="data" class="not-sidebar-main">
<table id='data' class="hovering striped-table condensed">
<thead>
<th v-for="col in columns" :title="col.type" :class="[col.isKey ? 'key' : 'not-key']">{{ col.name }} </th>
</thead>
<tr v-for="row in data" @click="inspectRow" :class="{'highlight': isActiveRow(row)}">
<td v-for="d in row" :title="d">{{ d }}</td>
</tr>
</table>
</div>
<div v-if="error" class="not-sidebar-main error">
Error: {{ error.code ? error.code + ' &ndash; ' + error.message : error.message }}
</div>
<p></p>
<div v-if="rowDetails" class="not-sidebar-sub">
<table id='rowDetails'>
<tr v-for="(key, value) in rowDetails" >
<td class="key">{{ value }}</td>
<td>{{ key }}</td>
</tr>
</table>
</div>
</div>
</div>
</div>
</body>
</html>

View File

@@ -1,4 +0,0 @@
const cds = require("@sap/cds")
cds.on ('served', ()=> {
cds.app.serve ('/data') .from ('@capire/data-viewer','app/viewer')
})

View File

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

View File

@@ -1,13 +0,0 @@
{
"name": "@capire/data-viewer",
"version": "0.1.0",
"description": "A generic browser for data",
"dependencies": {
"@sap/cds": ">=5.0.4"
},
"files": [
"app",
"srv",
"index.cds"
]
}

View File

@@ -1,30 +0,0 @@
/**
* Exposes data + entity metadata
*/
@requires:'authenticated-user'
@odata service DataService @( path:'-data' ) {
/**
* Metadata like name and columns/elements
*/
entity Entities @cds.persistence.skip {
key name : String;
columns: Composition of many {
name : String;
type : String;
isKey: Boolean;
}
}
/**
* The actual data, organized by column name
*/
entity Data @cds.persistence.skip {
key ID : String; // to be OData-compliant
record : array of {
column : String;
data : String;
}
}
}

View File

@@ -1,63 +0,0 @@
const cds = require('@sap/cds')
const log = cds.log('data')
class DataService extends cds.ApplicationService { init(){
this.on ('READ', 'Entities', req => {
const { dataSource } = req.req.query
const srvPrefixes = cds.db.model.all('service').map(srv => srv.name+'.')
const dataSourceFilter = dataSource === 'db'
? e => e['@cds.persistence.skip'] !== true // for DB, excl. entities w/o persistence
: e => !!srvPrefixes.find(srvName => e.name.startsWith(srvName)) // only entities reachable from a service
return cds.db.model.all('entity')
.filter (e => req.data && req.data.name ? e.name === req.data.name : true) // honor name filter from request, if any
.filter (e => !e.name.startsWith('DRAFT.')) // exclude synthetic stuff
.filter (e => !e.name.startsWith('DataService.')) // exclude this service
.filter (dataSourceFilter)
.sort((e1, e2) => e1.name.localeCompare(e2.name))
.map(e => {
const columns = Object.entries(e.elements)
.filter(([,el]) => !(el instanceof cds.Association)) // exclude assocs+compositions
.map(([name, el]) => { return { name, type: el.type, isKey:!!el.key }})
return { name: e.name, columns }
})
})
this.on ('READ', 'Data', async req => {
const { entity: entityName, dataSource: dataSourceName } = req.req.query
if (!entityName) return req.reject(400, `Must provide 'entity' query`)
const entity = cds.db.model.definitions[entityName]
if (!entity) return req.reject(404, 'No such entity: ' + entityName)
const query = SELECT.from(entity)
query.SELECT.limit = req.query.SELECT.limit // forward $skip / $top
const dataSource = findDataSource(dataSourceName, entityName)
let res = await dataSource.run(query)
if (!Array.isArray(res)) res = [res] // singleton result
return res.map((line) => {
const record = Object.entries(line).map(([column, data]) => ({ column, data }))
return {
record,
ID: cds.utils.uuid() // just to be OData-compliant
}
})
})
return super.init()
}}
module.exports = { DataService }
/** @returns {cds.Service} */
function findDataSource(dataSourceName, entityName) {
for (let srv of Object.values(cds.services)) { // all connected services
if (!srv.name) continue // FIXME intermediate/pending in cds.services ?
if (dataSourceName === srv.name || entityName.startsWith(srv.name+'.')) {
log._debug && log.debug(`using ${srv.name} as data source`)
return srv
}
}
return cds.services.db // fallback
}

View File

@@ -1,281 +0,0 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="569px" height="428px" viewBox="-0.5 -0.5 569 428" content="&lt;mxfile&gt;&lt;diagram id=&quot;QQJxv4aCTC7ZgE7HHOvM&quot; name=&quot;Page-1&quot;&gt;7Vpbc9soFP41fqxHCF0f7VyanWl2O3W3fcYSltkg4UH41l+/EIEtGdl1HTlRM508RBzDMfB9h/NxrAG8yTcfOVrMH1mK6cB10s0A3g5cFwAQy3/Ksq0sgQcrQ8ZJWpmcvWFCfmA90liXJMWltlUmwRgVZNE0JqwocCIaNsQ5Wze7zRhNG4YFynBjGsowSRDFVrfvJBXzyhq54d7+gEk2N98MAr3gHJnO2nE5Rylb10zwbgBvOGOieso3N5iqzWvuy/2RT3cT47gQ5wxwqwErRJd6bRPZ2ZOmh9HfIz1HsTUL52xZpFiNdQZwvJ4TgScLlKhP1xJqaZuLnMoWkI8Jy0miu5ZPWCRz3fiPZNmzR082ZqwQ9ygnVHHhAdMVFiRBaojg7Gm3v3JnxoiSrJDPvNra8Qpz1ZeOtF0wNYNSTogUmWz7Oy83jDL+vAQYuepPzZRx8kN+OaJmjtXALxq4UE9uopcPdLvmy7tVf9Jub7tGQk0Rb2omDcNHzHIs+FZ20Z9+AIHmhA4KYDiy3lMMxH5lm9fpZVBEmtbZzvkeefmgwW8nArSIMF6WpMBlOVDzCKja8CmXT5l6+oy4KDA/QRDwSgSpwTGNfM93WqkzI5TWes6iBCdJV8CBaOg3ofNjGzoDZx0633k5cp6F3A4ug9ZfRSIPTLlC13nMclFDVDrf9bJRvtsIXJSEFXLEaLHo6XFA8eyc0yA+eRpcMbLPiGvohTY5oBe/nB2+xQ5DhrJfwRskEZ7OzgneFOFo1lHwun7YBCd27cgFLYduF5EbtETuASS4SEdKsMhWSlDOivTrnEh2j+UH94QaMGRLqyQQNGG6KgClkGnAzO/uS46KrbHqyTmmracXn4KtZEueGIml90KOzbDZ88qEU6POjmBbx84EH8cUCbJqCrg28LS7z4w8n5cmhr0GTTzgmyPf+Kgmr4fVFdeBp92BfdxVtWbL1TOddss8i2GhxbCbZSmkB96z6E/DeOqcl7pnOOgqdcdNKFqD/1ppO7KgmSyTROqte5QIdhKf3197t0htS46/pfZ2g8BmQhwMWxKB63SgvmOLDN8Zf5oxdRy2ym8ZwEqV9SmEe6O+oQNfL4xNSaIG3V2+oGyL25H7SnJczjEWf8BrBy9qibyrgQcs8E4psCllyZNceYokggaovRBzmkLMvVSIdZkL65IK2ooqfENF1RTePggv1VM/cdSdmjKatEaXR5mwUYZ7pqb6cZeCDrBD+Vp3KWAXsE6Fcl8vU7Vr0nmXq7Mi3+9V5LshbPIEwgtD3/yOcMxRh6FvV9kmmK/IEX2m1FvfqqO9uGK9rjazi1/vOL3HdpAb1r5FlINDYQeDSxN8fOAq8A9m02Gc/1pNrmAFPkWS3uQPtsDFpdnDsKgnpTgQuEMnigNVlAO+E0O3QQ43iC/jGQjg0AMODEPoR17sBFHTL7hecrHLdCaBtOSW0QoRiqaEErH9k2HsDPOqF0i7iveOM4ypdTRSzBsKSWDlGO/CHAMc77SjDoPdrvWNUfIkcZdGrSnt66Qpt35CU0w/s5II9ausZBQTguUKavMiiVPnl1Or3CYSWnmkNDh1pIpr0UZ6X6iZ5JtMvdEzzCWPl4thjrj6lyw53Y758zFzorT0wuOGM4H0qp0TZakZJYtvenkdnC4fwMHvNb7Tcr5AYJ8vbgfni2tXF/9V9QZp+kbw+l3xZHdqXZcn5sTrmCfRYSHzdXliFzLr727cIoHeE1V26viqVNkp646p4nrxGzLFrmFazDDQJFtKpJrk8OdSclrpzk/TnUHmtOxZjf6zFNIN1vayEjfuMIaxH4cQ+B4MXAgOATx+yzn/ptQBViBqYgXi0MYqtqEC/i9DJZv79z4rYbF/exbe/Q8=&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<rect x="1" y="1" width="195" height="122" fill="rgb(255, 255, 255)" stroke="#828282" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)rotate(-90 11 13)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-end; width: 107px; height: 1px; padding-top: 13px; margin-left: -96px;">
<div data-drawio-colors="color: #4D4D4D; " style="box-sizing: border-box; font-size: 0px; text-align: right;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(77, 77, 77); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
S/4 HANA
</div>
</div>
</div>
</foreignObject>
<text x="11" y="25" fill="#4D4D4D" font-family="Helvetica" font-size="12px" text-anchor="end" font-weight="bold">
S/4 HANA
</text>
</switch>
</g>
<rect x="42.5" y="40" width="120" height="50" rx="7.5" ry="7.5" fill="#f8cecc" stroke="#b85450" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 65px; margin-left: 44px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Business
<br/>
Partner
</div>
</div>
</div>
</foreignObject>
<text x="103" y="69" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Business...
</text>
</switch>
</g>
<rect x="221" y="1" width="347" height="349" fill="rgb(255, 255, 255)" stroke="#828282" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-start; width: 331px; height: 1px; padding-top: 15px; margin-left: 230px;">
<div data-drawio-colors="color: #4D4D4D; " style="box-sizing: border-box; font-size: 0px; text-align: left;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(77, 77, 77); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Incident Mgmt
</b>
<br/>
Extension App
</div>
</div>
</div>
</foreignObject>
<text x="230" y="27" fill="#4D4D4D" font-family="Helvetica" font-size="12px">
Incident Mgmt...
</text>
</switch>
</g>
<rect x="418" y="73" width="115" height="50" rx="7.5" ry="7.5" fill="#dae8fc" stroke="#6c8ebf" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 113px; height: 1px; padding-top: 98px; margin-left: 419px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Incidents
</div>
</div>
</div>
</foreignObject>
<text x="476" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Incidents
</text>
</switch>
</g>
<path d="M 475.5 182 L 475.5 142.97" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 481.5 182 L 475.5 170 L 469.5 182" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 475.5 124.97 L 480.79 133.97 L 475.5 142.97 L 470.21 133.97 Z" fill="#6c8ebf" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<rect x="255" y="73" width="120" height="50" rx="7.5" ry="7.5" fill="#ffe6cc" stroke="#d79b00" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 98px; margin-left: 256px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Customers
</div>
</div>
</div>
</foreignObject>
<text x="315" y="102" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Customers
</text>
</switch>
</g>
<rect x="1" y="147" width="196.5" height="202" fill="rgb(255, 255, 255)" stroke="#828282" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)rotate(-90 11 159)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe flex-end; width: 187px; height: 1px; padding-top: 159px; margin-left: -176px;">
<div data-drawio-colors="color: #4D4D4D; " style="box-sizing: border-box; font-size: 0px; text-align: right;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(77, 77, 77); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
SuccessFactors
</div>
</div>
</div>
</foreignObject>
<text x="11" y="171" fill="#4D4D4D" font-family="Helvetica" font-size="12px" text-anchor="end" font-weight="bold">
SuccessFactors
</text>
</switch>
</g>
<rect x="42.5" y="184" width="120" height="50" rx="7.5" ry="7.5" fill="#f8cecc" stroke="#b85450" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 209px; margin-left: 44px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Workforce
<br/>
Person
</div>
</div>
</div>
</foreignObject>
<text x="103" y="213" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Workforce...
</text>
</switch>
</g>
<rect x="42.5" y="267" width="120" height="50" rx="7.5" ry="7.5" fill="#f8cecc" stroke="#b85450" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 292px; margin-left: 44px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Employee
<br/>
Timesheet
</div>
</div>
</div>
</foreignObject>
<text x="103" y="296" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Employee...
</text>
</switch>
</g>
<path d="M 162.5 74.32 L 238.96 86.19" fill="none" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="6 6" pointer-events="stroke"/>
<path d="M 252.79 88.34 L 237.88 93.11 L 240.03 79.27 Z" fill="none" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<rect x="418" y="182" width="115" height="50" rx="7.5" ry="7.5" fill="#dae8fc" stroke="#6c8ebf" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 113px; height: 1px; padding-top: 207px; margin-left: 419px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Messages
</div>
</div>
</div>
</foreignObject>
<text x="476" y="211" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Messages
</text>
</switch>
</g>
<path d="M 418 98 L 394.97 98" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 418 92 L 406 98 L 418 104" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 376.97 98 L 385.97 92.71 L 394.97 98 L 385.97 103.29 Z" fill="#6c8ebf" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<rect x="255" y="184" width="120" height="50" rx="7.5" ry="7.5" fill="#ffe6cc" stroke="#d79b00" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 209px; margin-left: 256px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Service
<br/>
Worker
</div>
</div>
</div>
</foreignObject>
<text x="315" y="213" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Service...
</text>
</switch>
</g>
<path d="M 162.5 209 L 238.76 209" fill="none" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="6 6" pointer-events="stroke"/>
<path d="M 252.76 209 L 238.76 216 L 238.76 202 Z" fill="none" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 354.83 181.46 L 439.35 123" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 358.91 171.95 L 352.99 182.73 L 365.16 180.99" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<rect x="255" y="267" width="120" height="50" rx="7.5" ry="7.5" fill="#ffe6cc" stroke="#d79b00" stroke-width="2" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 118px; height: 1px; padding-top: 292px; margin-left: 256px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
Worker
<br/>
Availability
</div>
</div>
</div>
</foreignObject>
<text x="315" y="296" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Worker...
</text>
</switch>
</g>
<path d="M 162.5 292 L 238.76 292" fill="none" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" stroke-dasharray="6 6" pointer-events="stroke"/>
<path d="M 252.76 292 L 238.76 299 L 238.76 285 Z" fill="none" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 37 407 C 37 401.48 41.48 397 47 397 L 92.5 397 C 98.02 397 102.5 392.52 102.5 387 C 102.5 392.52 106.98 397 112.5 397 L 158 397 C 163.52 397 168 401.48 168 407" fill="none" stroke="#b85450" stroke-width="2" stroke-miterlimit="10" transform="translate(0,397)scale(1,-1)translate(0,-397)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 414px; margin-left: 103px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">
Backend Services
</div>
</div>
</div>
</foreignObject>
<text x="103" y="426" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Backend Services
</text>
</switch>
</g>
<path d="M 249.5 407 C 249.5 401.48 253.98 397 259.5 397 L 305 397 C 310.52 397 315 392.52 315 387 C 315 392.52 319.48 397 325 397 L 370.5 397 C 376.02 397 380.5 401.48 380.5 407" fill="none" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" transform="translate(0,397)scale(1,-1)translate(0,-397)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 414px; margin-left: 315px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">
Usage Views
</div>
</div>
</div>
</foreignObject>
<text x="315" y="426" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Usage Views
</text>
</switch>
</g>
<path d="M 410 407 C 410 401.48 414.48 397 420 397 L 465.5 397 C 471.02 397 475.5 392.52 475.5 387 C 475.5 392.52 479.98 397 485.5 397 L 531 397 C 536.52 397 541 401.48 541 407" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" transform="translate(0,397)scale(1,-1)translate(0,-397)" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe flex-start; justify-content: unsafe center; width: 1px; height: 1px; padding-top: 414px; margin-left: 476px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 12px; font-family: Helvetica; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: nowrap;">
Extension Data
</div>
</div>
</div>
</foreignObject>
<text x="476" y="426" fill="rgb(0, 0, 0)" font-family="Helvetica" font-size="12px" text-anchor="middle">
Extension Data
</text>
</switch>
</g>
<path d="M 350 80.94 C 350 79.32 354.25 78 359.5 78 C 362.02 78 364.44 78.31 366.22 78.86 C 368 79.41 369 80.16 369 80.94 L 369 90.06 C 369 91.68 364.75 93 359.5 93 C 354.25 93 350 91.68 350 90.06 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 369 80.94 C 369 82.56 364.75 83.88 359.5 83.88 C 354.25 83.88 350 82.56 350 80.94" fill="none" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

Before

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 273 KiB

View File

@@ -1,76 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title> cds.log </title>
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
<style>
select { border-color: transparent; padding: 4px 12px; margin: 0px; }
button { padding: 2px 11px; margin: 0px 4px; font: 90% italic; }
</style>
</head>
<body class="small-container" , style="margin-top: 70px;">
<div id='app'>
<h1> Log Levels </h1>
<input type="text" placeholder="Search by ID or Log Level..." @input="fetch">
<table id='loggers'>
<thead>
<th> Module ID </th>
<th> Log Level </th>
</thead>
<tr v-for="each in list">
<td>{{ each.id }}</td>
<td><select v-bind:id="each.id" v-model="each.level" @change="set">
<option>SILENT</option>
<option>ERROR</option>
<option>WARN</option>
<option>INFO</option>
<option>DEBUG</option>
<option>TRACE</option>
</select>
</td>
</tr>
</table>
<h4>Log Format:</h4>
[ <button class="round-button" :class={'muted-button':!format.timestamp} @click="toggle_format" id="timestamp">Timestamp </button>
| <button class="round-button" :class={'muted-button':!format.level} @click="toggle_format" id="level">Log Level </button>
| <button class="round-button" :class={'muted-button':!format.tenant} @click="toggle_format" id="tenant">Tenant </button>
| <button class="round-button" :class={'muted-button':!format.reqid} @click="toggle_format" id="reqid">Request ID </button>
| <button class="round-button" :class={'muted-button':!format.id} @click="toggle_format" id="module">Logger ID </button>
] - <i>log message ...</i>
</div>
</body>
<script>
axios.defaults.headers['Content-Type'] = 'application/json'
axios.defaults.baseURL = '/log'
const loggers = Vue.createApp({ el: '#app',
data() {
return {
format: { timestamp:false, level:false, tenant:false, reqid:false, id:true, },
list: [],
}
},
methods: {
async fetch (eve) {
this.list = (await axios.get (`/Loggers${
eve && eve.target.value ? `?$search=${eve.target.value}` : ''
}`)).data
},
async set (eve) {
const { id, value:level } = eve.target
await axios.put (`/Logger/${id}`, {id,level})
},
async toggle_format (eve) {
this.format[eve.target.id] = !this.format[eve.target.id]
await axios.post (`/format`, this.format)
},
},
}).mount('#app')
loggers.fetch() // initially fill list of loggers
</script>
</html>

View File

@@ -1,17 +0,0 @@
{
"name": "@capire/loggers",
"version": "1.0.0",
"description": "Simple sample on how to dynamically set cds.log levels and formats.",
"files": [
"app",
"srv"
],
"dependencies": {
"@sap/cds": ">=5.9",
"express": "^4.17.1"
},
"scripts": {
"start": "cds-serve",
"watch": "cds watch"
}
}

View File

@@ -1,11 +0,0 @@
# Dynamically Set `cds.log` Levels and Formats
### Run
```sh
cds watch
```
### Test
Either using the UI through http://localhost:4004/loggers.html, or try the requests in `test/requests.http`

View File

@@ -1,3 +0,0 @@
service Sue {
entity Dummy { key ID: UUID; title: String; }
}

View File

@@ -1,20 +0,0 @@
@rest service LogService {
@readonly entity Loggers : Logger {};
entity Logger {
key id : String;
level : String;
}
action format (
timestamp : Boolean,
level : Boolean,
tenant : Boolean,
reqid : Boolean,
id : Boolean,
);
action debug (logger : String) returns Logger;
action reset (logger : String) returns Logger;
}

View File

@@ -1,56 +0,0 @@
const cds = require ('@sap/cds')
const LOG = cds.log('cds.log')
module.exports = class LogService extends cds.Service {
init(){
this.on('GET','Loggers', (req)=>{
let loggers = Object.values(cds.log.loggers).map (_logger)
let {$search} = req._.req.query
if ($search) {
const re = RegExp($search,'i')
loggers = loggers.filter (l => re.test(l.id) || re.test(l.level))
}
return loggers.sort ((a,b) => a.id < b.id ? -1 : 1)
})
this.on('PUT','Logger', (req)=>{
const {id} = req.params[0] || req.data
if (!id) return req.reject('No logger id specified in request')
return _logger (cds.log (id, req.data))
})
this.on('debug', (req)=>{
const {logger:id} = req.params[0] || req.data
if (!id) return req.reject('No logger id specified in request')
return _logger (cds.log (id, {level:'debug'}))
})
this.on('reset', (req)=>{
const {logger:id} = req.params[0] || req.data
if (!id) return req.reject('No logger id specified in request')
return _logger (cds.log (id, {level:'info'}))
})
this.on('format', (req)=>{
const $ = req.data; LOG.info('format:',$)
// Set format for new loggers constructed subsequently
cds.log.format = (id, level, ...args) => {
const fmt = []
if ($.timestamp) fmt.push ('|', (new Date).toISOString())
if ($.level) fmt.push ('|', _levels[level].padEnd(5))
if ($.tenant) fmt.push ('|', cds.context && cds.context.tenant)
if ($.reqid) fmt.push ('|', cds.context && cds.context.id)
if ($.id) fmt.push ('|', id)
fmt[0] = '[', fmt.push ('] -', ...args)
return fmt
}
// Apply this format to all existing loggers
Object.values(cds.log.loggers).forEach (l => l.setFormat (cds.log.format))
})
}
}
const _logger = ({id,level}) => ({id, level:_levels[level] })
const _levels = [ 'SILENT', 'ERROR', 'WARN', 'INFO', 'DEBUG', 'TRACE' ]

View File

@@ -1,18 +0,0 @@
http://localhost:4004/loggers.html
@body: = Content-Type: application/json\n\n
###
GET http://localhost:4004/log/Loggers
###
PUT http://localhost:4004/log/Logger/sqlite
{{body:}} { "level": "debug" }
###
POST http://localhost:4004/log/debug(logger='sqlite')
###
POST http://localhost:4004/log/reset(logger='sqlite')
### Dummy request to see sqlite debug output
GET http://localhost:4004/sue/Dummy

View File

@@ -1,212 +0,0 @@
<svg host="65bd71144e" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" version="1.1" width="368px" height="282px" viewBox="-0.5 -0.5 368 282" content="&lt;mxfile&gt;&lt;diagram id=&quot;QQJxv4aCTC7ZgE7HHOvM&quot; name=&quot;Page-1&quot;&gt;7Zpbc9o4FIB/DY9hdL88JqRJH9ppd9mZndk3YwvwxCBXmIT01+9xLNvYMaEl4ZIUeLB1JOt2viMdXXp0MFvduiCdfrWRSXoERaseve4RgpnU8Mglj4VEIF4IJi6OChGqBcP4p/FfltJlHJmFlxWizNoki9OmMLTzuQmzhixwzj40k41tEjUEaTAxzwTDMEieS/+No2xaSBWRtfyziSfTsmQsfINnQZnYt2QxDSL7sCain3p04KzNirfZamCSvPOa/XKzIbaqmDPz7Fc+mJDb2+X19NuX/778vEB/LX+gO3vhc7kPkqVvcI+IBPK7GuVVzh59P4gfy7yeV2M7zy4WT1q6hAQYp6Dpqzoe3ib588rau8XUpmVuUK1RGVeV4F5VxGWa+o6tsiDQx2n+OjWrYGLnkCo1Lp6ZzLha+r0UgQquHqZxZoZpEOafPQDDIJtmswRCOK9MvDIllUU4SQY2se6pPDoeGxGGIF9kzt6ZtZhI6hFCVUzJDvENvAlmcZKbxMDO4hAaMQzmC3h8HfoEZZm4DPtGoqceaGu+VKNxmVmtiTwJt8ZCg90jJPGxhPtPvFlSLYrwQw259uBO1/hWXhZ4s5pUOdfkwYuH7zdAJBtBXKTB/FWgvHlGowblmXVmHelm2i7wQVbUZQ3kNzGJDQUc30xUaLrNZKQ443syE+iCMJ5P/oFRiF7z/dgN0fK4dkP3bzevmwn+NvexgVl4fxPB0Lj7ODQf3IYiblTEumxIkREVYh829Hp7oaxlL0wd117YSw5PoWrop5nNTWcbs4XkOsiCo9NhMPAhu+jQQtLgndBBGT8uHfzUR9NvLjLuPJj+mYMpFidmLqLDXFqaNfPoMl+GQyhMgsUiDpuKanZzLoEvfCcW41ngska4pTA+yP/7U8/CLl1ots8oUM+JybavcEzU2HB4ruw1ZfIOZZYyZ5Igi++b2xRdGvYlfLcxtKxmCesmTKzc0ijzKBruP1vfV2jnRNqjuGzlVHTNs5yekKsavjuF8kxhNXO9MwoJl32uaPUjokkSlv21WEp3RJRI3GdYVD/JmsUQ0V+LFYIelF915rfaiPxg/MqXwXorfsXLZrJnfvWZ32of5mPxyw7DLzsuv+VR0+mu+obLNE3i88LvvPDrncYuGt58bvhqkxlCjdjJ8hMYNe48yRChMqPxRn7aeGznyTey45z3t/kRvOkvIHxkfMjZZaiNaKvPUE5Q78NpaDu95WS/Z6e3rMWhnIaug7czwpsS8pMiGANaEikOs5DUVJJy66tEizIgTyKGKFeSsR29XqxUn+j8CpQiSBApabMUwsCMiOZaMEw4P7DT23UQ9ufxi36R39NatmFJYYhV+aat1kjw1vQuSR+WapxjhrASmuyKr4ABFkvJJUK6OmuoCtF9SgVjimuFKVKHpffkD+puYuvifd9++9BLtfF4TDbcrRMjwfeyVDvMpSF87Mt2+HxwV4/p293v03JeCBd9wmE8Fk+neKp1+IZ1n2kYjyV4FAIcmJ237FBfMsEZlVIphHXLd4ElABIaE8byKyak5ePvPPxDsL4RXSSv75XTT/8D&lt;/diagram&gt;&lt;/mxfile&gt;">
<defs/>
<g>
<path d="M 207 201 L 257 201 L 277 241 L 257 281 L 207 281 L 187 241 Z" fill="#ffe6cc" stroke="#d79b00" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 241px; margin-left: 188px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Comic Sans MS&quot;; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b style="font-size: 11px;">
Bookshop
</b>
<br style="font-size: 11px;"/>
App
</div>
</div>
</div>
</foreignObject>
<text x="232" y="244" fill="rgb(0, 0, 0)" font-family="Comic Sans MS" font-size="11px" text-anchor="middle">
Bookshop...
</text>
</switch>
</g>
<path d="M 207 102 L 257 102 L 277 142 L 257 182 L 207 182 L 187 142 Z" fill="#f8cecc" stroke="#b85450" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 145px; margin-left: 188px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Comic Sans MS&quot;; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<span style="font-size: 11px;">
<span style="font-size: 11px;">
<b>
Bookstore
<br/>
</b>
</span>
App
<br style="font-size: 11px;"/>
</span>
</div>
</div>
</div>
</foreignObject>
<text x="232" y="148" fill="rgb(0, 0, 0)" font-family="Comic Sans MS" font-size="11px" text-anchor="middle">
Bookstore...
</text>
</switch>
</g>
<path d="M 297 53 L 347 53 L 367 93 L 347 133 L 297 133 L 277 93 Z" fill="#d5e8d4" stroke="#82b366" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 93px; margin-left: 278px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Comic Sans MS&quot;; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<span style="font-size: 11px;">
<b style="font-size: 11px;">
Reviews
</b>
<br style="font-size: 11px;"/>
Service
<br style="font-size: 11px;"/>
</span>
</div>
</div>
</div>
</foreignObject>
<text x="322" y="96" fill="rgb(0, 0, 0)" font-family="Comic Sans MS" font-size="11px" text-anchor="middle">
Reviews...
</text>
</switch>
</g>
<path d="M 297 150 L 347 150 L 367 190 L 347 230 L 297 230 L 277 190 Z" fill="#e1d5e7" stroke="#9673a6" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 190px; margin-left: 278px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Comic Sans MS&quot;; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<b>
Common
</b>
<br/>
Data
</div>
</div>
</div>
</foreignObject>
<text x="322" y="193" fill="rgb(0, 0, 0)" font-family="Comic Sans MS" font-size="11px" text-anchor="middle">
Common...
</text>
</switch>
</g>
<path d="M 117 150 L 167 150 L 187 190 L 167 230 L 117 230 L 97 190 Z" fill="#d5e8d4" stroke="#82b366" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 190px; margin-left: 98px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Comic Sans MS&quot;; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<span style="font-size: 11px;">
<b style="font-size: 11px;">
Orders
</b>
<br style="font-size: 11px;"/>
Service
<br style="font-size: 11px;"/>
</span>
</div>
</div>
</div>
</foreignObject>
<text x="142" y="193" fill="rgb(0, 0, 0)" font-family="Comic Sans MS" font-size="11px" text-anchor="middle">
Orders...
</text>
</switch>
</g>
<path d="M 286.47 171.05 L 273.81 164.3" fill="none" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 268.51 161.47 L 277.45 161.71 L 273.81 164.3 L 273.69 168.77 Z" fill="#5c5c5c" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 177.53 171.05 L 190.19 164.3" fill="none" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 195.49 161.47 L 190.31 168.77 L 190.19 164.3 L 186.55 161.71 Z" fill="#5c5c5c" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 232 201 L 232 189.12" fill="none" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 232 183.12 L 236 191.12 L 232 189.12 L 228 191.12 Z" fill="#5c5c5c" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 286.63 112.26 L 273.62 119.34" fill="none" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 268.35 122.21 L 273.47 114.87 L 273.62 119.34 L 277.29 121.9 Z" fill="#5c5c5c" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 117 53 L 167 53 L 187 93 L 167 133 L 117 133 L 97 93 Z" fill="#d5e8d4" stroke="#82b366" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 93px; margin-left: 98px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Comic Sans MS&quot;; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<span style="font-size: 11px;">
<b style="font-size: 11px;">
Suppliers
</b>
<br style="font-size: 11px;"/>
Service
<br style="font-size: 11px;"/>
</span>
</div>
</div>
</div>
</foreignObject>
<text x="142" y="96" fill="rgb(0, 0, 0)" font-family="Comic Sans MS" font-size="11px" text-anchor="middle">
Suppliers...
</text>
</switch>
</g>
<path d="M 21 106 L 71 106 L 91 146 L 71 186 L 21 186 L 1 146 Z" fill="#dae8fc" stroke="#6c8ebf" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 146px; margin-left: 2px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Comic Sans MS&quot;; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; font-weight: bold; white-space: normal; overflow-wrap: normal;">
<span style="font-size: 11px;">
S/4
</span>
</div>
</div>
</div>
</foreignObject>
<text x="46" y="149" fill="rgb(0, 0, 0)" font-family="Comic Sans MS" font-size="11px" text-anchor="middle" font-weight="bold">
S/4
</text>
</switch>
</g>
<path d="M 81.27 126.53 L 100.5 115.91" fill="none" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 105.76 113.01 L 100.69 120.38 L 100.5 115.91 L 96.82 113.37 Z" fill="#5c5c5c" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 82.61 162.78 L 98.92 170.25" fill="none" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 104.37 172.75 L 95.43 173.06 L 98.92 170.25 L 98.77 165.79 Z" fill="#5c5c5c" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 177.37 112.26 L 190.38 119.34" fill="none" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 195.65 122.21 L 186.71 121.9 L 190.38 119.34 L 190.53 114.87 Z" fill="#5c5c5c" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="all"/>
<path d="M 207 1 L 257 1 L 277 41 L 257 81 L 207 81 L 187 41 Z" fill="#fff2cc" stroke="#d6b656" stroke-width="2" stroke-miterlimit="10" pointer-events="all"/>
<g transform="translate(-0.5 -0.5)">
<switch>
<foreignObject pointer-events="none" width="100%" height="100%" requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility" style="overflow: visible; text-align: left;">
<div xmlns="http://www.w3.org/1999/xhtml" style="display: flex; align-items: unsafe center; justify-content: unsafe center; width: 88px; height: 1px; padding-top: 44px; margin-left: 188px;">
<div data-drawio-colors="color: rgb(0, 0, 0); " style="box-sizing: border-box; font-size: 0px; text-align: center;">
<div style="display: inline-block; font-size: 11px; font-family: &quot;Comic Sans MS&quot;; color: rgb(0, 0, 0); line-height: 1.2; pointer-events: all; white-space: normal; overflow-wrap: normal;">
<span style="font-size: 11px;">
<b style="font-size: 11px;">
Fiori
</b>
<br style="font-size: 11px;"/>
App
<br style="font-size: 11px;"/>
</span>
</div>
</div>
</div>
</foreignObject>
<text x="232" y="47" fill="rgb(0, 0, 0)" font-family="Comic Sans MS" font-size="11px" text-anchor="middle">
Fiori...
</text>
</switch>
</g>
<path d="M 232 102 L 232 88.12" fill="none" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="stroke"/>
<path d="M 232 82.12 L 236 90.12 L 232 88.12 L 228 90.12 Z" fill="#5c5c5c" stroke="#5c5c5c" stroke-miterlimit="10" pointer-events="all"/>
</g>
<switch>
<g requiredFeatures="http://www.w3.org/TR/SVG11/feature#Extensibility"/>
<a transform="translate(0,-5)" xlink:href="https://www.diagrams.net/doc/faq/svg-export-text-problems" target="_blank">
<text text-anchor="middle" font-size="10px" x="50%" y="100%">
Text is not SVG - cannot display
</text>
</a>
</switch>
</svg>

Before

Width:  |  Height:  |  Size: 18 KiB

View File

@@ -1,29 +0,0 @@
{
"version": "1.1.0",
"options": {
"management": true,
"messagingrest": true,
"messaging": true
},
"rules": {
"topicRules": {
"publishFilter": [
"*"
],
"subscribeFilter": [
"*"
]
},
"queueRules": {
"publishFilter": [
"*"
],
"subscribeFilter": [
"*"
]
}
},
"authorities": [
"$ACCEPT_GRANTED_AUTHORITIES"
]
}

View File

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

View File

@@ -2,20 +2,25 @@ Books = Books
Book = Book
ID = ID
Title = Title
Description = Description
Stock = Stock
Image = Image
Price = Price
Currency = Currency
Authors = Authors
Author = Author
AuthorID = Author''s ID
AuthorID = Author ID
Stock = Stock
Name = Name
AuthorName = Author's Name
DateOfBirth = Date of Birth
DateOfDeath = Date of Death
PlaceOfBirth = Place of Birth
PlaceOfDeath = Place of Death
Age = Age
Authors = Authors
Order = Order
Orders = Orders
Price = Price
Genres = Genres
Genre = Genre
Genres = Genres
SubGenres = Sub Genres
NumCode = Numeric Code
MinorUnit = Minor Unit
Exponent = Exponent

View File

@@ -1,5 +1,14 @@
Books = Bücher
Book = Buch
ID = ID
Title = Titel
Authors = Autoren
Author = Autor
AuthorID = ID des Autors
AuthorName = Name des Autors
Age = Alter
rder = Bestellung
Name = Name
Stock = Bestand
Order = Bestellung
Orders = Bestellungen
Price = Preis

View File

@@ -1,8 +0,0 @@
Age = Age
Lifetime = Lifetime
SubGenres = Subgenre
NumCode = Numeric Code
MinorUnit = Minor Unit
Exponent = Exponent

View File

@@ -1,52 +0,0 @@
using {AdminService} from '@capire/bookshop';
annotate AdminService.Authors with @odata.draft.enabled;
////////////////////////////////////////////////////////////////////////////
//
// Authors Object Page
//
annotate AdminService.Authors with @(UI : {
HeaderInfo : {
TypeName : 'Author',
TypeNamePlural : 'Authors',
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}'
},
]},
});
// Workaround to avoid errors for unknown db-specific calculated fields above
extend sap.capire.bookshop.Authors with {
virtual age : Integer;
virtual lifetime : String;
}
annotate AdminService.Authors with {
age @Common.Label : '{i18n>Age}';
lifetime @Common.Label : '{i18n>Lifetime}'
}
// Workaround for Fiori popup for asking user to enter a new UUID on Create
annotate AdminService.Authors with { ID @Core.Computed; }

View File

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

View File

@@ -1,141 +0,0 @@
{
"_version": "1.28.0",
"sap.app": {
"id": "authors",
"type": "application",
"title": "Manage Authors",
"description": "Sample Application",
"i18n": "i18n/i18n.properties",
"applicationVersion": {
"version": "1.0.0"
},
"dataSources": {
"AdminService": {
"uri": "admin/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
},
"sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"-id": "ui5template.smartTemplate",
"version": "1.40.12"
},
"crossNavigation": {
"inbounds": {
"intent1": {
"signature": {
"parameters": {
"Books.author.ID":{
"renameTo": "ID"
}
},
"additionalParameters": "ignored"
},
"semanticObject": "Authors",
"action": "manage",
"title": "{{appTitle}}",
"info": "{{appInfo}}",
"subTitle": "{{appSubTitle}}",
"icon": "sap-icon://SAP-icons-TNT/user",
"indicatorDataSource": {
"dataSource": "AdminService",
"path": "Authors/$count",
"refresh": 1800
}
}
}
}
},
"sap.ui5": {
"dependencies": {
"minUI5Version": "1.81.0",
"libs": {
"sap.fe.templates": {}
}
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"uri": "i18n/i18n.properties"
},
"": {
"dataSource": "AdminService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect": true,
"earlyRequests": true,
"groupProperties": {
"default": {
"submit": "Auto"
}
}
}
}
},
"routing": {
"routes": [
{
"pattern": ":?query:",
"name": "AuthorsList",
"target": "AuthorsList"
},
{
"pattern": "Authors({key}):?query:",
"name": "AuthorsDetails",
"target": "AuthorsDetails"
}
],
"targets": {
"AuthorsList": {
"type": "Component",
"id": "AuthorsList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"entitySet": "Authors",
"initialLoad": true,
"navigation": {
"Authors": {
"detail": {
"route": "AuthorsDetails"
}
}
}
}
}
},
"AuthorsDetails": {
"type": "Component",
"id": "AuthorsDetailsList",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"entitySet": "Authors"
}
}
}
}
},
"contentDensities": {
"compact": true,
"cozy": true
}
},
"sap.ui": {
"technology": "UI5",
"fullWidth": false,
"deviceTypes":{
"desktop": true,
"tablet": true,
"phone": true
}
},
"sap.fiori": {
"registrationIds": [],
"archeType": "transactional"
}
}

View File

@@ -1,119 +0,0 @@
using { AdminService } from '@capire/bookstore';
using from '../common'; // to help UI linter get the complete annotations
annotate sap.capire.bookshop.Genres with @fiori.draft.enabled;
annotate AdminService.Genres with @odata.draft.enabled;
annotate AdminService.Genres with @odata.draft.bypass;
////////////////////////////////////////////////////////////////////////////
//
// Books Object Page
//
annotate AdminService.Books with @(
UI: {
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Translations}', Target: 'texts/@UI.LineItem'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Contents}', Target: 'contents/@UI.PresentationVariant'}
],
FieldGroup#General: {
Data: [
{Value: title},
{Value: author_ID},
{Value: genre_ID},
{Value: descr},
]
},
FieldGroup#Details: {
Data: [
{Value: stock},
{Value: price},
{Value: currency_code, Label: '{i18n>Currency}'},
]
},
FieldGroup#Admin: {
Data: [
{Value: createdBy},
{Value: createdAt},
{Value: modifiedBy},
{Value: modifiedAt}
]
}
}
);
////////////////////////////////////////////////////////////////////////////
//
// Value Help for Tree Table
//
annotate AdminService.Books with {
genre @(Common: {
Label : 'Genre',
ValueList: {
CollectionPath : 'Genres',
Parameters : [
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty: 'name',
},
{
$Type : 'Common.ValueListParameterInOut',
LocalDataProperty: genre_ID,
ValueListProperty: 'ID',
}
],
}
});
}
// Hide ID because of the ValueHelp
annotate AdminService.Genres with {
ID @UI.Hidden;
};
////////////////////////////////////////////////////////////
//
// Draft for Localized Data
//
annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
annotate AdminService.Books with @odata.draft.enabled;
annotate AdminService.Books.texts with @(
UI: {
Identification: [{Value:title}],
SelectionFields: [ locale, title ],
LineItem: [
{Value: locale, Label: 'Locale'},
{Value: title, Label: 'Title'},
{Value: descr, Label: 'Description'},
]
}
);
annotate AdminService.Books.texts with {
ID @UI.Hidden;
ID_texts @UI.Hidden;
};
// Add Value Help for Locales
annotate AdminService.Books.texts {
locale @(
ValueList.entity:'Languages', Common.ValueListWithFixedValues, //show as drop down, not a dialog
)
}
// In addition we need to expose Languages through AdminService as a target for ValueList
using { sap } from '@sap/cds/common';
extend service AdminService {
@readonly entity Languages as projection on sap.common.Languages;
}
// Workaround for Fiori popup for asking user to enter a new UUID on Create
annotate AdminService.Books with { ID @Core.Computed; }
using from './tree-view';

View File

@@ -1,42 +0,0 @@
using { AdminService } from '@capire/bookstore';
////////////////////////////////////////////////////////////////////////////
//
// Contents Tree Table Annotations
//
// Tell Fiori about the structure of the hierarchy
annotate AdminService.Contents with @Aggregation.RecursiveHierarchy #ContentsHierarchy : {
ParentNavigationProperty : parent, // navigates to a node's parent
NodeProperty : ID, // identifies a node, usually the key
};
// Fiori expects the following to be defined explicitly, even though they're always the same
extend AdminService.Contents with @(
// The columns expected by Fiori to be present in hierarchy entities
Hierarchy.RecursiveHierarchy #ContentsHierarchy : {
LimitedDescendantCount : LimitedDescendantCount,
DistanceFromRoot : DistanceFromRoot,
DrillState : DrillState,
LimitedRank : LimitedRank
},
// Disallow filtering on these properties from Fiori UIs
Capabilities.FilterRestrictions.NonFilterableProperties: [
'LimitedDescendantCount',
'DistanceFromRoot',
'DrillState',
'LimitedRank'
],
// Disallow sorting on these properties from Fiori UIs
Capabilities.SortRestrictions.NonSortableProperties : [
'LimitedDescendantCount',
'DistanceFromRoot',
'DrillState',
'LimitedRank'
],
) columns { // Ensure we can query these fields from database
null as LimitedDescendantCount : Int16,
null as DistanceFromRoot : Int16,
null as DrillState : String,
null as LimitedRank : Int16,
};

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,14 +1,14 @@
{
"_version": "1.8.0",
"sap.app": {
"id": "books",
"id": "admin",
"type": "application",
"title": "Manage Books",
"description": "Sample Application",
"i18n": "i18n/i18n.properties",
"dataSources": {
"AdminService": {
"uri": "admin/",
"uri": "/admin/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
@@ -19,22 +19,6 @@
"id": "ui5template.basicSAPUI5ApplicationProject",
"-id": "ui5template.smartTemplate",
"-version": "1.40.12"
},
"crossNavigation": {
"inbounds": {
"intent1": {
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"semanticObject": "Books",
"action": "manage",
"title": "{{appTitle}}",
"info": "{{appInfo}}",
"subTitle": "{{appSubTitle}}",
"icon": "sap-icon://course-book"
}
}
}
},
"sap.ui5": {
@@ -79,11 +63,6 @@
"pattern": "Books({key}/author({key2}):?query:",
"name": "AuthorsDetails",
"target": "AuthorsDetails"
},
{
"pattern": "Books({key})/contents({key2}):?query:",
"name": "ContentsDetails",
"target": "ContentsDetails"
}
],
"targets": {
@@ -94,7 +73,6 @@
"options": {
"settings" : {
"entitySet" : "Books",
"initialLoad": true,
"navigation" : {
"Books" : {
"detail" : {
@@ -117,34 +95,11 @@
"detail" : {
"route" : "AuthorsDetails"
}
},
"contents": {
"detail": {
"route": "ContentsDetails"
}
}
},
"controlConfiguration": {
"contents/@com.sap.vocabularies.UI.v1.LineItem": {
"tableSettings": {
"hierarchyQualifier": "ContentsHierarchy",
"type": "TreeTable"
}
}
}
}
}
},
"ContentsDetails": {
"type": "Component",
"id": "ContentsDetails",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"contextPath": "/Books/contents"
}
}
},
"AuthorsDetails": {
"type": "Component",
"id": "AuthorsDetailsList",

View File

@@ -1,167 +0,0 @@
{
"services": {
"LaunchPage": {
"adapter": {
"config": {
"groups": [
{
"id": "Bookshop",
"title": "Bookshop",
"isPreset": true,
"isVisible": true,
"isGroupLocked": false,
"tiles": [
{
"id": "BrowseBooks",
"tileType": "sap.ushell.ui.tile.StaticTile",
"properties": {
"title": "Browse Books",
"targetURL": "#Books-display"
}
}
]
},
{
"id": "Administration",
"title": "Administration",
"isPreset": true,
"isVisible": true,
"isGroupLocked": false,
"tiles": [
{
"id": "ManageBooks",
"tileType": "sap.ushell.ui.tile.StaticTile",
"properties": {
"title": "Manage Books",
"targetURL": "#Books-manage"
}
},
{
"id": "ManageGenres",
"tileType": "sap.ushell.ui.tile.StaticTile",
"properties": {
"title": "Manage Genres",
"targetURL": "#Genres-manage"
}
},
{
"id": "ManageAuthors",
"tileType": "sap.ushell.ui.tile.StaticTile",
"properties": {
"title": "Manage Authors",
"targetURL": "#Authors-manage"
}
},
{
"id": "ManageOrders",
"tileType": "sap.ushell.ui.tile.StaticTile",
"properties": {
"title": "Manage Orders",
"targetURL": "#Orders-manage"
}
}
]
}
]
}
}
},
"NavTargetResolution": {
"config": {
"enableClientSideTargetResolution": true
}
},
"ClientSideTargetResolution": {
"adapter": {
"config": {
"inbounds": {
"BrowseBooks": {
"semanticObject": "Books",
"action": "display",
"title": "Browse Books",
"signature": {
"parameters": {
"Books.ID": {
"renameTo": "ID"
},
"Authors.books.ID": {
"renameTo": "ID"
}
},
"additionalParameters": "ignored"
},
"resolutionResult": {
"applicationType": "SAPUI5",
"additionalInformation": "SAPUI5.Component=bookshop",
"url": "/browse/webapp"
}
},
"BrowseAuthors": {
"semanticObject": "Authors",
"action": "manage",
"title": "Browse Authors",
"signature": {
"parameters": {
"Books.author.ID":{
"renameTo": "ID"
}
},
"additionalParameters": "ignored"
},
"resolutionResult": {
"applicationType": "SAPUI5",
"additionalInformation": "SAPUI5.Component=authors",
"url": "/admin-authors/webapp"
}
},
"ManageGenres": {
"semanticObject": "Genres",
"action": "manage",
"title": "Manage Genres",
"signature": {
"parameters": {
"Genre.ID": {
"renameTo": "ID"
}
},
"additionalParameters": "ignored"
},
"resolutionResult": {
"applicationType": "SAPUI5",
"additionalInformation": "SAPUI5.Component=genres",
"url": "/genres/webapp"
}
},
"ManageBooks": {
"semanticObject": "Books",
"action": "manage",
"title": "Manage Books",
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"resolutionResult": {
"applicationType": "SAPUI5",
"additionalInformation": "SAPUI5.Component=books",
"url": "/admin-books/webapp"
}
},
"ManageOrders": {
"semanticObject": "Orders",
"action": "manage",
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"resolutionResult": {
"applicationType": "SAPUI5",
"additionalInformation": "SAPUI5.Component=orders",
"url": "/orders/webapp"
}
}
}
}
}
}
}
}

View File

@@ -1,57 +1,50 @@
using CatalogService from '@capire/bookstore';
using CatalogService from '@capire/bookshop';
////////////////////////////////////////////////////////////////////////////
//
// Books Object Page
//
annotate CatalogService.Books with @(UI : {
HeaderInfo : {
TypeName : '{i18n>Book}',
TypeNamePlural : '{i18n>Books}',
Description : {Value : author}
},
HeaderFacets : [{
$Type : 'UI.ReferenceFacet',
Label : '{i18n>Description}',
Target : '@UI.FieldGroup#Descr'
}, ],
Facets : [{
$Type : 'UI.ReferenceFacet',
Label : '{i18n>Details}',
Target : '@UI.FieldGroup#Price'
}, ],
FieldGroup #Descr : {Data : [{Value : descr}, ]},
FieldGroup #Price : {Data : [
{Value : price},
{
Value : currency.symbol,
Label : '{i18n>Currency}'
},
]},
});
annotate CatalogService.Books with @(
UI: {
HeaderInfo: {
TypeName: 'Book',
TypeNamePlural: 'Books',
Description: {Value: author}
},
HeaderFacets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Description}', Target: '@UI.FieldGroup#Descr'},
],
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Price'},
],
FieldGroup#Descr: {
Data: [
{Value: descr},
]
},
FieldGroup#Price: {
Data: [
{Value: price},
{Value: currency.symbol, Label: '{i18n>Currency}'},
]
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books List Page
// Books Object Page
//
annotate CatalogService.Books with @(UI : {
SelectionFields : [
ID,
price,
currency_code
],
LineItem : [
{
Value : ID,
Label : '{i18n>Title}'
},
{
Value : author,
Label : '{i18n>Author}'
},
{Value : genre.name},
{Value : price},
{Value : currency.symbol},
]
}, );
annotate CatalogService.Books with @(
UI: {
SelectionFields: [ ID, price, currency_code ],
LineItem: [
{Value: title},
{Value: author, Label:'{i18n>Author}'},
{Value: genre.name},
{Value: price},
{Value: currency.symbol, Label:' '},
]
},
);

View File

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

Some files were not shown because too many files have changed in this diff Show More