Compare commits

..

86 Commits

Author SHA1 Message Date
D050513
673fec080a more 2023-07-30 22:49:59 +02:00
D050513
6f7b786ca0 first set of adjustments 2023-07-30 22:30:12 +02:00
Koch
313e595a91 billing data 2023-05-17 14:48:22 +02:00
Koch
f872286440 gdpr 2023-04-21 14:59:10 +02:00
Koch
57d7d66533 audit log corr 2023-04-21 11:49:58 +02:00
Koch
54c7969f58 audit log 2023-04-20 14:44:13 +02:00
Koch
578448c2fa audit log 2023-04-20 14:38:19 +02:00
Daniel Hutzel
534af7ffee Merge branch 'main' into gdpr 2023-02-26 07:58:50 +01:00
Daniel
535981dc7e Fixing cds lint issues 2023-02-22 13:39:05 +01:00
Daniel
d15d0535d9 Updated package-lock.json -> fixed vulnerability 2023-02-17 17:39:23 +01:00
dependabot[bot]
430d3a46c4 Bump axios from 1.3.1 to 1.3.2
Bumps [axios](https://github.com/axios/axios) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-06 17:55:15 +01:00
dependabot[bot]
308e6b932a Bump http-cache-semantics from 4.1.0 to 4.1.1
Bumps [http-cache-semantics](https://github.com/kornelski/http-cache-semantics) from 4.1.0 to 4.1.1.
- [Release notes](https://github.com/kornelski/http-cache-semantics/releases)
- [Commits](https://github.com/kornelski/http-cache-semantics/commits)

---
updated-dependencies:
- dependency-name: http-cache-semantics
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-02 13:38:42 +01:00
dependabot[bot]
703d45fab0 Bump axios from 1.3.0 to 1.3.1
Bumps [axios](https://github.com/axios/axios) from 1.3.0 to 1.3.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.3.0...v1.3.1)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-02 13:29:01 +01:00
dependabot[bot]
63c21c5a96 Bump axios from 1.2.6 to 1.3.0
Bumps [axios](https://github.com/axios/axios) from 1.2.6 to 1.3.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.2.6...v1.3.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-02-01 13:22:51 +01:00
dependabot[bot]
e0c6b16b15 Bump axios from 1.2.4 to 1.2.6
Bumps [axios](https://github.com/axios/axios) from 1.2.4 to 1.2.6.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.2.4...v1.2.6)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-30 17:16:47 +01:00
dependabot[bot]
0771fc06e6 Bump @sap/cds from 6.4.0 to 6.5.0
Bumps [@sap/cds](https://cap.cloud.sap/) from 6.4.0 to 6.5.0.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-30 17:14:23 +01:00
dependabot[bot]
dc90cad8f4 Bump axios from 1.2.3 to 1.2.4
Bumps [axios](https://github.com/axios/axios) from 1.2.3 to 1.2.4.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.2.3...v1.2.4)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-25 13:34:30 +01:00
Christian Georgi
f731a95bd1 Fix indentation 2023-01-18 14:57:15 +01:00
dependabot[bot]
2cd092be10 Bump axios from 1.2.2 to 1.2.3
Bumps [axios](https://github.com/axios/axios) from 1.2.2 to 1.2.3.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/1.2.2...v1.2.3)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-18 13:19:55 +01:00
Daniel Hutzel
a1c2f32408 Cleanup (#440)
* Cleanup

* minor cleanups
2023-01-13 09:08:20 +01:00
dependabot[bot]
8a6a42f109 Bump axios from 1.2.1 to 1.2.2
Bumps [axios](https://github.com/axios/axios) from 1.2.1 to 1.2.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.2.1...1.2.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2023-01-09 11:11:29 +01:00
Daniel
28cfceeaf0 Avoid dangling IO resources 2022-12-20 13:30:17 +01:00
dependabot[bot]
879546829a Bump @sap/cds from 6.3.2 to 6.4.0
Bumps [@sap/cds](https://cap.cloud.sap/) from 6.3.2 to 6.4.0.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-16 13:10:09 +01:00
dependabot[bot]
b7e435d611 Bump sqlite3 from 5.1.3 to 5.1.4
Bumps [sqlite3](https://github.com/TryGhost/node-sqlite3) from 5.1.3 to 5.1.4.
- [Release notes](https://github.com/TryGhost/node-sqlite3/releases)
- [Commits](https://github.com/TryGhost/node-sqlite3/compare/v5.1.3...v5.1.4)

---
updated-dependencies:
- dependency-name: sqlite3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-13 17:06:51 +01:00
dependabot[bot]
4f5f075697 Bump sqlite3 from 5.1.2 to 5.1.3
Bumps [sqlite3](https://github.com/TryGhost/node-sqlite3) from 5.1.2 to 5.1.3.
- [Release notes](https://github.com/TryGhost/node-sqlite3/releases)
- [Commits](https://github.com/TryGhost/node-sqlite3/compare/v5.1.2...v5.1.3)

---
updated-dependencies:
- dependency-name: sqlite3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-12 14:33:59 +01:00
dependabot[bot]
710c83aa1c Bump axios from 1.2.0 to 1.2.1
Bumps [axios](https://github.com/axios/axios) from 1.2.0 to 1.2.1.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.2.0...v1.2.1)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-06 13:39:02 +01:00
dependabot[bot]
e9fbba9f46 Bump axios from 1.1.3 to 1.2.0
Bumps [axios](https://github.com/axios/axios) from 1.1.3 to 1.2.0.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.1.3...v1.2.0)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 13:41:22 +01:00
dependabot[bot]
0d96447d4b Bump @sap/cds from 6.3.1 to 6.3.2
Bumps [@sap/cds](https://cap.cloud.sap/) from 6.3.1 to 6.3.2.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-12-05 13:39:50 +01:00
Daniel
ed1510d173 sorting loggers 2022-11-30 17:37:51 +01:00
Daniel
d07b20a689 Added loggers sample 2022-11-28 17:54:32 +01:00
dependabot[bot]
e6584c4c15 Bump chai from 4.3.6 to 4.3.7
Bumps [chai](https://github.com/chaijs/chai) from 4.3.6 to 4.3.7.
- [Release notes](https://github.com/chaijs/chai/releases)
- [Changelog](https://github.com/chaijs/chai/blob/4.x.x/History.md)
- [Commits](https://github.com/chaijs/chai/compare/v4.3.6...v4.3.7)

---
updated-dependencies:
- dependency-name: chai
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-09 16:17:30 +01:00
dependabot[bot]
2a6fca177b Bump axios from 1.1.2 to 1.1.3
Bumps [axios](https://github.com/axios/axios) from 1.1.2 to 1.1.3.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v1.1.2...v1.1.3)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-08 14:43:24 +01:00
Pierre Fritsch
d9058f9b51 Manage Orders: Switch to translatable texts (#424)
Co-authored-by: Christian Georgi <chgeo@users.noreply.github.com>
2022-11-08 14:38:41 +01:00
Pierre Fritsch
affd1718ef Fiori app *Manage Orders*: Hide technical UUID's (#423)
* Hide Order ID
* Hide Order Item ID

Co-authored-by: Christian Georgi <chgeo@users.noreply.github.com>
2022-11-08 13:33:25 +00:00
Pierre Fritsch
5ff72c3f02 Hide ID and ID_Texts for Books.texts (#422)
Co-authored-by: Christian Georgi <chgeo@users.noreply.github.com>
2022-11-08 13:30:28 +00:00
Pierre Fritsch
666001d564 Filter criteria: Display the Genre name (#421)
Co-authored-by: Christian Georgi <chgeo@users.noreply.github.com>
2022-11-08 13:28:36 +00:00
Pierre Fritsch
e4214b0e40 Fiori app *Browse Books*: Add missing column labels (#420)
* Add labels for `rating` and `numberOfReviews`
* Add label for `currency.symbol`
* Add label for `descr`
* Add label for `image`

Co-authored-by: Christian Georgi <chgeo@users.noreply.github.com>
2022-11-08 13:26:48 +00:00
Pierre Fritsch
ad76c699c9 Add column labels for age and lifetime (#419)
Co-authored-by: Christian Georgi <chgeo@users.noreply.github.com>
2022-11-08 13:23:44 +00:00
dependabot[bot]
0ad8baefc7 Bump @sap/cds from 6.2.2 to 6.3.1
Bumps [@sap/cds](https://cap.cloud.sap/) from 6.2.2 to 6.3.1.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
2022-11-08 14:21:28 +01:00
Daniel
3f7b43346f Buffer() is deprecated 2022-11-06 18:03:38 +01:00
Christian Georgi
6981841d86 Delete question--feedback-or-bug-.md 2022-10-24 09:54:48 +02:00
Christian Georgi
18c29d8b67 Create config.yml 2022-10-24 09:54:18 +02:00
Daniel
02fc1a89a1 Fixed expected tenants in odata.test.js 2022-10-19 13:00:20 +02:00
dependabot[bot]
94b74d76ad Bump @sap/cds from 6.2.1 to 6.2.2 (#413)
Bumps [@sap/cds](https://cap.cloud.sap/) from 6.2.1 to 6.2.2.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-14 15:41:17 +02:00
Daniel
b0b4412c7a Fixed genre for Eleonora 2022-10-12 14:59:10 +02:00
dependabot[bot]
70cfa82a35 Bump axios from 0.27.2 to 1.1.2 (#410)
Bumps [axios](https://github.com/axios/axios) from 0.27.2 to 1.1.2.
- [Release notes](https://github.com/axios/axios/releases)
- [Changelog](https://github.com/axios/axios/blob/v1.x/CHANGELOG.md)
- [Commits](https://github.com/axios/axios/compare/v0.27.2...v1.1.2)

---
updated-dependencies:
- dependency-name: axios
  dependency-type: direct:development
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 14:03:08 +00:00
dependabot[bot]
d17d74f1ec Bump @sap/cds from 6.1.3 to 6.2.1 (#411)
Bumps [@sap/cds](https://cap.cloud.sap/) from 6.1.3 to 6.2.1.

---
updated-dependencies:
- dependency-name: "@sap/cds"
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-10 15:52:35 +02:00
dependabot[bot]
9fbf7c69c9 Bump semver from 7.3.7 to 7.3.8 (#408)
Bumps [semver](https://github.com/npm/node-semver) from 7.3.7 to 7.3.8.
- [Release notes](https://github.com/npm/node-semver/releases)
- [Changelog](https://github.com/npm/node-semver/blob/main/CHANGELOG.md)
- [Commits](https://github.com/npm/node-semver/compare/v7.3.7...v7.3.8)

---
updated-dependencies:
- dependency-name: semver
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-05 16:15:26 +02:00
Daniel
f54a8cecb7 Revert "Extensibility Walkthrough in capire (#405)"
This reverts commit 608e16090c.
2022-10-04 12:12:30 +02:00
Daniel Hutzel
608e16090c Extensibility Walkthrough in capire (#405)
* Extension project template

* SalesRegion ->  x_SalesRegion

* enhanced order repo

* updated package-lock.json
2022-10-04 12:05:57 +02:00
Daniel Hutzel
bd0f514026 Updated workspace setup (#406) 2022-10-04 11:59:22 +02:00
Daniel
ee2fdd0989 cosmetics 2022-10-04 11:48:01 +02:00
dependabot[bot]
92f7489def Bump sqlite3 from 5.1.1 to 5.1.2 (#404)
Bumps [sqlite3](https://github.com/TryGhost/node-sqlite3) from 5.1.1 to 5.1.2.
- [Release notes](https://github.com/TryGhost/node-sqlite3/releases)
- [Commits](https://github.com/TryGhost/node-sqlite3/compare/v5.1.1...v5.1.2)

---
updated-dependencies:
- dependency-name: sqlite3
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2022-10-04 10:36:43 +02:00
Koch
a8d3ac2796 audit log 2022-09-01 15:49:32 +02:00
sjvans
2f5eff8d96 Update data-privacy.cds (#321) 2022-02-09 08:56:41 +01:00
Wolfgang Koch
1c54732b8b audit log test 2021-08-13 11:22:29 +02:00
Wolfgang Koch
0d48719ecd test audit log 2021-08-10 11:39:17 +02:00
Wolfgang Koch
0daff04a41 pdm annotations 2021-02-24 17:42:58 +01:00
Wolfgang Koch
6d317b0266 pdm annotations 2021-02-24 17:35:56 +01:00
Wolfgang Koch
92b69f3825 pdm annotations 2021-02-19 11:58:08 +01:00
Wolfgang Koch
52f15159f9 pdm annotations 2021-02-19 11:55:36 +01:00
Wolfgang Koch
0405152d81 pdm annotations 2021-02-12 12:00:38 +01:00
Wolfgang Koch
cd3f139544 pdm annotations 2021-02-12 11:52:36 +01:00
Christian Georgi
4ee2d9a3f1 Fix wrong element name 2021-02-02 15:53:59 +01:00
Christian Georgi
8a10c88fbc Remove package lock 2021-02-02 15:52:01 +01:00
wmkoch
81e99b3a46 Update pdm-service.cds
entity semantics
2021-01-20 16:45:21 +01:00
Wolfgang Koch
caab8ed120 pdm annotations 2020-11-17 10:10:51 +01:00
Wolfgang Koch
a14c0e4eeb pdm binding 2020-11-13 12:02:01 +01:00
Wolfgang Koch
ba015431bd pdm binding 2020-11-13 09:35:03 +01:00
Wolfgang Koch
5cc9ca25f7 pdm annotations communication 2020-11-12 14:00:36 +01:00
Wolfgang Koch
be343d7f6c pdm annotations 2020-11-12 12:15:49 +01:00
Wolfgang Koch
1a20672401 pdm config 2020-11-11 16:38:06 +01:00
Wolfgang Koch
baa2b4eaf1 pdm config 2020-11-11 16:34:55 +01:00
Wolfgang Koch
3b274089c2 cleanup 2020-11-10 14:26:23 +01:00
Wolfgang Koch
5fd9ee3993 cleanup 2020-11-10 11:24:05 +01:00
Wolfgang Koch
8160997eec cleanup 2020-11-10 11:22:10 +01:00
Wolfgang Koch
9a81962a75 enhance remarks 2020-11-10 11:20:43 +01:00
Wolfgang Koch
a9dfe57ce1 enhance remarks 2020-11-10 11:19:56 +01:00
Wolfgang Koch
77aa129bbf enhance example 2020-11-10 11:15:16 +01:00
Wolfgang Koch
b8f0f5e2f9 pdm service annotations 2020-10-16 10:44:47 +02:00
Wolfgang Koch
d0b06879fc pdm service 2020-10-16 09:58:59 +02:00
Wolfgang Koch
311ad304dc pdm service 2020-10-16 09:49:07 +02:00
Wolfgang Koch
752e0c884b pdm service 2020-10-15 16:06:57 +02:00
Wolfgang Koch
8575b75edb pdm service 2020-10-15 14:15:51 +02:00
Wolfgang Koch
bde5cd9818 pdm service 2020-10-13 17:04:59 +02:00
Wolfgang Koch
c7e1bb1d0a pdm service 2020-10-13 16:42:19 +02:00
87 changed files with 3846 additions and 5275 deletions

View File

@@ -1,7 +1,7 @@
{ {
"extends": [ "extends": [
"eslint:recommended", "plugin:@sap/cds/recommended",
"plugin:@sap/cds/recommended" "eslint:recommended"
], ],
"env": { "env": {
"browser": true, "browser": true,
@@ -13,10 +13,13 @@
"globals": { "globals": {
"SELECT": true, "SELECT": true,
"INSERT": true, "INSERT": true,
"UPSERT": true,
"UPDATE": true, "UPDATE": true,
"DELETE": true, "DELETE": true,
"CREATE": true, "CREATE": true,
"DROP": true, "DROP": true,
"CDL": true,
"CQL": true,
"cds": true "cds": true
}, },
"rules": { "rules": {

5
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@@ -0,0 +1,5 @@
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

@@ -1,10 +0,0 @@
---
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 +0,0 @@
*.tgz

View File

@@ -1,113 +0,0 @@
const { exec, execSync } = require ('child_process')
const isWin = process.platform === 'win32'
const express = require ('express')
const fs = require ('fs')
const { dirname, relative } = require('path')
const axios = require('axios')
const app = express()
const cwd = __dirname
const port=process.env.PORT || 4444
let scopes = process.argv.filter(a => a.startsWith('@'))
if (!scopes.length) scopes = ['@capire']
// clean up on start (exit handler might not complete on Windows)
exec(isWin ? 'del *.tgz' : 'rm *.tgz', {cwd})
app.use('/-/:tarball', async (req,res,next) => {
console.debug ('GET', req.params)
try {
const { tarball } = req.params
const pkgFull = tarball.substring(0, tarball.lastIndexOf('-'))
const scope = '@'+pkgFull.substring(0, pkgFull.indexOf('-'))
const pkg = pkgFull.substring(pkgFull.indexOf('-')+1)
fs.lstat(tarball,(err => {
if (err) { // no tgz yet
const loc = dirname(require.resolve(`${scope}/${pkg}/package.json`))
console.debug (`npm pack ${relative(cwd, loc)}`)
exec(`npm pack ${loc}`,{cwd},next)
}
else next() //> express.static below
}))
} catch (e) {
console.error(e)
res.sendStatus(500)
}
})
app.use('/-', express.static(__dirname))
app.get('/*', async (req,res)=>{
const urlRegex = /^\/(@[\w-]+)\/(.+)/
const url = decodeURIComponent(req.url)
console.debug ('GET',url)
try {
if (!urlRegex.test(url)) return res.sendStatus(404)
const [, scpe, pkg ] = urlRegex.exec(url)
const packageName = `${scpe}/${pkg}`
// delegate to default registry for @sap/non-cds packages
if (scpe === ('@sap') && !packageName.startsWith('@sap/cds')) {
return forward(req, res)
}
const package = require (`${packageName}/package.json`)
const tarball = `${scpe.slice(1)}-${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": `${server.url}/-/${tarball}`
},
dependencies: package.dependencies,
devDependencies: package.devDependencies
}
},
})
} catch (e) {
if (e.code === 'MODULE_NOT_FOUND') return res.sendStatus(404)
console.error(e); throw e
}
})
const server = app.listen(port, ()=>{
const url = server.url = `http://localhost:${server.address().port}`
for (const scope of scopes) {
console.log (`npm set ${scope}:registry=${url}`)
execSync(`npm set ${scope}:registry=${url}`)
}
console.log (`registry listening on ${url}`)
})
const _exit = ()=>{
server.close()
for (const scope of scopes) {
execSync(`npm conf rm "${scope}:registry"`)
}
process.exit()
}
async function forward(req, res) {
try {
const url = `https://registry.npmjs.org${req.url}`
const resAxios = await axios.get(url)
console.debug('->', decodeURI(url), resAxios.status)
return res.json(resAxios.data)
} catch (e) {
return res.sendStatus(e.response.status)
}
}
process.on ('SIGTERM',_exit)
process.on ('SIGHUP',_exit)
process.on ('SIGINT',_exit)
process.on ('SIGUSR2',_exit)

20
.vscode/launch.json vendored
View File

@@ -13,7 +13,7 @@
"<node_internals>/**", "<node_internals>/**",
"**/node_modules/**", "**/node_modules/**",
"**/cds/lib/lazy.js", "**/cds/lib/lazy.js",
"**/cds/lib/req/cls.js", "**/cds/lib/req/cds-context.js",
"**/odata-v4/okra/**" "**/odata-v4/okra/**"
] ]
}, },
@@ -26,10 +26,24 @@
"<node_internals>/**", "<node_internals>/**",
"**/node_modules/**", "**/node_modules/**",
"**/cds/lib/lazy.js", "**/cds/lib/lazy.js",
"**/cds/lib/req/cls.js", "**/cds/lib/req/cds-context.js",
"**/odata-v4/okra/**" "**/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": [ "inputs": [
{ {

View File

@@ -10,10 +10,11 @@
"<node_internals>/**", "<node_internals>/**",
"**/node_modules/**", "**/node_modules/**",
"**/cds/lib/lazy.js", "**/cds/lib/lazy.js",
"**/cds/lib/req/cls.js", "**/cds/lib/req/cds-context.js",
"**/odata-v4/okra/**" "**/odata-v4/okra/**"
] ]
}, },
"mochaExplorer.debuggerConfig": "Debug Mocha Tests",
"mochaExplorer.parallel": true, "mochaExplorer.parallel": true,
"eslint.validate": [ "eslint.validate": [
"cds", "cds",

2
bookshop/app/index.cds Normal file
View File

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

View File

@@ -0,0 +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;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;15
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 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 11
4 251 The Raven "The Raven" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word "Nevermore". The poem makes use of folk, mythological, religious, and classical references. 150 333 13.13 USD 16
5 252 Eleonora "Eleonora" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively "happy" ending. 150 555 14 USD 15
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 13

View File

@@ -4,21 +4,21 @@
* currencies, if not obtained through @capire/common. * currencies, if not obtained through @capire/common.
*/ */
module.exports = async (db)=>{ module.exports = async (tx)=>{
const has_common = db.model.definitions['sap.common.Currencies'].elements.numcode const has_common = tx.model.definitions['sap.common.Currencies']?.elements.numcode
if (has_common) return if (has_common) return
const already_filled = await db.exists('sap.common.Currencies',{code:'EUR'}) const already_filled = await tx.exists('sap.common.Currencies',{code:'EUR'})
if (already_filled) return if (already_filled) return
await INSERT.into ('sap.common.Currencies') .columns ( await tx.run (INSERT.into ('sap.common.Currencies') .columns (
'code','symbol','name' [ 'code', 'symbol', 'name' ]
) .rows ( ) .rows (
[ 'EUR','€','Euro' ], [ 'EUR', '€', 'Euro' ],
[ 'USD','$','US Dollar' ], [ 'USD', '$', 'US Dollar' ],
[ 'GBP','£','British Pound' ], [ 'GBP', '£', 'British Pound' ],
[ 'ILS','₪','Shekel' ], [ 'ILS', '₪', 'Shekel' ],
[ 'JPY','¥','Yen' ], [ 'JPY', '¥', 'Yen' ],
) ))
} }

View File

@@ -10,7 +10,12 @@ service CatalogService @(path:'/browse') {
author.name as author author.name as author
} excluding { createdBy, modifiedBy }; } excluding { createdBy, modifiedBy };
<<<<<<< HEAD
@requires_: 'authenticated-user'
action submitOrder (book : Integer, amount: Integer);
=======
@requires: 'authenticated-user' @requires: 'authenticated-user'
action submitOrder ( book: Books:ID, quantity: Integer ) returns { stock: Integer }; action submitOrder ( book: Books:ID, quantity: Integer ) returns { stock: Integer };
event OrderedBook : { book: Books:ID; quantity: Integer; buyer: String }; event OrderedBook : { book: Books:ID; quantity: Integer; buyer: String };
>>>>>>> 534af7ffee60e086c563dbaa450e86e5fca5cf2b
} }

View File

@@ -12,7 +12,11 @@ using { sap.capire.bookshop.Books } from '@capire/bookshop';
using { ReviewsService.Reviews } from '@capire/reviews'; using { ReviewsService.Reviews } from '@capire/reviews';
extend Books with { extend Books with {
reviews : Composition of many Reviews on reviews.subject = $self.ID; reviews : Composition of many Reviews on reviews.subject = $self.ID;
@Common.Label : '{i18n>Rating}'
rating : Decimal; rating : Decimal;
@Common.Label : '{i18n>NumberOfReviews}'
numberOfReviews : Integer; numberOfReviews : Integer;
} }

View File

@@ -1,11 +0,0 @@
{
"eslint.validate": [
"cds",
"csn",
"csv",
"csv",
"csv (semicolon)",
"tsv",
"tab"
]
}

View File

@@ -1,21 +0,0 @@
# Welcome to your Extension Project for the CAP Bookshop Fiori App
It contains these folders and files, following our recommended project layout:
| File or Folder | Purpose |
|----------------|------------------------------------|
| `app/` | content for UI frontends goes here |
| `test/` | contet for local tests |
| `package.json` | project metadata and configuration |
| `readme.md` | this getting started guide |
## Next Steps
- Runs `cds login ...`
- Runs `cds pull ...`
## Learn More
Learn more at https://cap.cloud.sap/docs/get-started/.

View File

@@ -1,25 +0,0 @@
using { OrdersService, sap, sap.capire.orders.Orders } from '@capire/fiori';
namespace x_bookshop.extension;
// Adding 2 new fields for Orders
extend Orders with {
x_priority : String @assert.range enum {high; medium; low} default 'medium' ;
x_salesRegion : Association to SalesRegion;
}
/** Value Help for x_salesRegion */
entity SalesRegion : sap.common.CodeList {
key code : String(11);
}
// --- UI ---
annotate Orders:x_priority with @title : 'Priority';
annotate SalesRegion:name with @title : 'Sales Region';
annotate OrdersService.Orders with @UI.LineItem : [
... up to { Value: OrderNo },
{ Value : x_priority },
{ Value : x_salesRegion.name },
...
];

View File

@@ -1,49 +0,0 @@
{
"name": "@capire/fiori-ext",
"version": "1.0.0",
"description": "A simple CAP project.",
"repository": "<Add your repository here>",
"license": "UNLICENSED",
"private": true,
"dependencies": {
"@sap/cds": "^6"
},
"devDependencies": {
"sqlite3": "^5.0.4",
"@sap/eslint-plugin-cds": "^2.5.0"
},
"scripts": {
"start": "cds run"
},
"cds": {
"extends": "@capire/fiori"
},
"eslintConfig": {
"extends": [
"eslint:recommended",
"plugin:@sap/cds/recommended"
],
"env": {
"es2020": true,
"node": true,
"jest": true,
"mocha": true
},
"globals": {
"SELECT": true,
"INSERT": true,
"UPDATE": true,
"DELETE": true,
"CREATE": true,
"DROP": true,
"CDL": true,
"CQL": true,
"CXL": true,
"cds": true
},
"rules": {
"no-console": "off",
"require-atomic-updates": "off"
}
}
}

View File

@@ -1,3 +0,0 @@
ID;createdAt;createdBy;buyer;OrderNo;currency_code;Z_priority;Z_SalesRegion_regionCode
7e2f2640-6866-4dcf-8f4d-3027aa831cad;2019-01-31;john.doe@test.com;john.doe@test.com;1;EUR;high;AMER
64e718c9-ff99-47f1-8ca3-950c850777d4;2019-01-30;jane.doe@test.com;jane.doe@test.com;2;EUR;low;APJ
1 ID createdAt createdBy buyer OrderNo currency_code Z_priority Z_SalesRegion_regionCode
2 7e2f2640-6866-4dcf-8f4d-3027aa831cad 2019-01-31 john.doe@test.com john.doe@test.com 1 EUR high AMER
3 64e718c9-ff99-47f1-8ca3-950c850777d4 2019-01-30 jane.doe@test.com jane.doe@test.com 2 EUR low APJ

View File

@@ -1,4 +0,0 @@
code;name;descr
AMER;Americas;North, Central and South America
EMEA;Europe, the Middle East and Africa;Europe, the Middle East and Africa
APJ;Asia Pacific and Japan;Asia Pacific and Japan
1 code name descr
2 AMER Americas North, Central and South America
3 EMEA Europe, the Middle East and Africa Europe, the Middle East and Africa
4 APJ Asia Pacific and Japan Asia Pacific and Japan

View File

@@ -6,16 +6,33 @@ Author = Author
AuthorID = Author ID AuthorID = Author ID
Stock = Stock Stock = Stock
Name = Name Name = Name
Description = Description
Image = Image
AuthorName = Author's Name AuthorName = Author's Name
DateOfBirth = Date of Birth DateOfBirth = Date of Birth
DateOfDeath = Date of Death DateOfDeath = Date of Death
PlaceOfBirth = Place of Birth PlaceOfBirth = Place of Birth
PlaceOfDeath = Place of Death PlaceOfDeath = Place of Death
Age = Age Age = Age
Lifetime = Lifetime
Authors = Authors Authors = Authors
Order = Order Order = Order
Orders = Orders Orders = Orders
OrderNo = Order Number
OrderItems = Order Items
Customer = Customer
Product = Product
ProductID = Product ID
ProductTitle = Product Title
UnitPrice = Unit Price
Quantity = Quantity
Price = Price Price = Price
Currency = Currency
Date = Date
Rating = Rating
NumberOfReviews = Number of Reviews
Genre = Genre Genre = Genre
Genres = Genres Genres = Genres

View File

@@ -1,12 +0,0 @@
{
"name": "approuter",
"dependencies": {
"@sap/approuter": "^11.0.0"
},
"engines": {
"node": "^16"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
}
}

View File

@@ -1,23 +0,0 @@
{
"authenticationMethod": "route",
"routes": [
{
"source": "^/app/(.*)$",
"target": "$1",
"localDir": ".",
"authenticationType": "xsuaa",
"cacheControl": "no-cache, no-store, must-revalidate"
},
{
"source": "^/-/cds/.*",
"destination": "mtx-api",
"authenticationType": "none"
},
{
"source": "^/(.*)$",
"target": "$1",
"destination": "srv-api",
"authenticationType": "xsuaa"
}
]
}

View File

@@ -43,5 +43,10 @@ extend sap.capire.bookshop.Authors with {
virtual lifetime : String; 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 // Workaround for Fiori popup for asking user to enter a new UUID on Create
annotate AdminService.Authors with { ID @Core.Computed; } annotate AdminService.Authors with { ID @Core.Computed; }

View File

@@ -62,6 +62,11 @@ annotate AdminService.Books.texts with @(
} }
); );
annotate AdminService.Books.texts with {
ID @UI.Hidden;
ID_texts @UI.Hidden;
};
// Add Value Help for Locales // Add Value Help for Locales
annotate AdminService.Books.texts { annotate AdminService.Books.texts {
locale @( locale @(

View File

@@ -33,7 +33,7 @@ annotate CatalogService.Books with @(UI : {
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Books Object Page // Books List Page
// //
annotate CatalogService.Books with @(UI : { annotate CatalogService.Books with @(UI : {
SelectionFields : [ SelectionFields : [
@@ -52,9 +52,6 @@ annotate CatalogService.Books with @(UI : {
}, },
{Value : genre.name}, {Value : genre.name},
{Value : price}, {Value : price},
{ {Value : currency.symbol},
Value : currency.symbol,
Label : ' '
},
] ]
}, ); }, );

View File

@@ -4,6 +4,7 @@
using { sap.capire.bookshop as my } from '@capire/bookstore'; using { sap.capire.bookshop as my } from '@capire/bookstore';
using { sap.common } from '@capire/common'; using { sap.common } from '@capire/common';
using { sap.common.Currencies } from '@sap/cds/common';
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
@@ -12,7 +13,7 @@ using { sap.common } from '@capire/common';
annotate my.Books with @( annotate my.Books with @(
Common.SemanticKey : [ID], Common.SemanticKey : [ID],
UI : { UI : {
Identification : [{Value : title}], Identification : [{ Value: title }],
SelectionFields : [ SelectionFields : [
ID, ID,
author_ID, author_ID,
@@ -20,21 +21,12 @@ annotate my.Books with @(
currency_code currency_code
], ],
LineItem : [ LineItem : [
{ { Value: ID, Label: '{i18n>Title}' },
Value : ID, { Value: author.ID, Label: '{i18n>Author}' },
Label : '{i18n>Title}' { Value: genre.name },
}, { Value: stock },
{ { Value: price },
Value : author.ID, { Value: currency.symbol },
Label : '{i18n>Author}'
},
{Value : genre.name},
{Value : stock},
{Value : price},
{
Value : currency.symbol,
Label : ' '
},
] ]
} }
) { ) {
@@ -46,6 +38,10 @@ annotate my.Books with @(
author @ValueList.entity : 'Authors'; author @ValueList.entity : 'Authors';
}; };
annotate Currencies with {
symbol @Common.Label : '{i18n>Currency}';
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Books Details // Books Details
@@ -53,8 +49,8 @@ annotate my.Books with @(
annotate my.Books with @(UI : {HeaderInfo : { annotate my.Books with @(UI : {HeaderInfo : {
TypeName : '{i18n>Book}', TypeName : '{i18n>Book}',
TypeNamePlural : '{i18n>Books}', TypeNamePlural : '{i18n>Books}',
Title : {Value : title}, Title : { Value: title },
Description : {Value : author.name} Description : { Value: author.name }
}, }); }, });
@@ -63,19 +59,14 @@ annotate my.Books with @(UI : {HeaderInfo : {
// Books Elements // Books Elements
// //
annotate my.Books with { annotate my.Books with {
ID @title : '{i18n>ID}'; ID @title: '{i18n>ID}';
title @title : '{i18n>Title}'; title @title: '{i18n>Title}';
genre @title : '{i18n>Genre}' @Common : { genre @title: '{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
Text : genre.name, author @title: '{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly };
TextArrangement : #TextOnly price @title: '{i18n>Price}' @Measures.ISOCurrency : currency_code;
}; stock @title: '{i18n>Stock}';
author @title : '{i18n>Author}' @Common : { descr @title: '{i18n>Description}' @UI.MultiLineText;
Text : author.name, image @title: '{i18n>Image}';
TextArrangement : #TextOnly
};
price @title : '{i18n>Price}' @Measures.ISOCurrency : currency_code;
stock @title : '{i18n>Stock}';
descr @UI.MultiLineText;
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@@ -87,26 +78,30 @@ annotate my.Genres with @(
UI : { UI : {
SelectionFields : [name], SelectionFields : [name],
LineItem : [ LineItem : [
{Value : name}, { Value: name },
{ {
Value : parent.name, Value : parent.name,
Label : 'Main Genre' Label: 'Main Genre'
}, },
], ],
} }
); );
annotate my.Genres with {
ID @Common.Text : name @Common.TextArrangement : #TextOnly;
}
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Genre Details // Genre Details
// //
annotate my.Genres with @(UI : { annotate my.Genres with @(UI : {
Identification : [{Value : name}], Identification : [{ Value: name}],
HeaderInfo : { HeaderInfo : {
TypeName : '{i18n>Genre}', TypeName : '{i18n>Genre}',
TypeNamePlural : '{i18n>Genres}', TypeNamePlural : '{i18n>Genres}',
Title : {Value : name}, Title : { Value: name },
Description : {Value : ID} Description : { Value: ID }
}, },
Facets : [{ Facets : [{
$Type : 'UI.ReferenceFacet', $Type : 'UI.ReferenceFacet',
@@ -120,8 +115,8 @@ annotate my.Genres with @(UI : {
// Genres Elements // Genres Elements
// //
annotate my.Genres with { annotate my.Genres with {
ID @title : '{i18n>ID}'; ID @title: '{i18n>ID}';
name @title : '{i18n>Genre}'; name @title: '{i18n>Genre}';
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@@ -131,14 +126,14 @@ annotate my.Genres with {
annotate my.Authors with @( annotate my.Authors with @(
Common.SemanticKey : [ID], Common.SemanticKey : [ID],
UI : { UI : {
Identification : [{Value : name}], Identification : [{ Value: name}],
SelectionFields : [name], SelectionFields : [name],
LineItem : [ LineItem : [
{Value : ID}, { Value: ID },
{Value : dateOfBirth}, { Value: dateOfBirth },
{Value : dateOfDeath}, { Value: dateOfDeath },
{Value : placeOfBirth}, { Value: placeOfBirth },
{Value : placeOfDeath}, { Value: placeOfDeath },
], ],
} }
) { ) {
@@ -157,8 +152,8 @@ annotate my.Authors with @(UI : {
HeaderInfo : { HeaderInfo : {
TypeName : '{i18n>Author}', TypeName : '{i18n>Author}',
TypeNamePlural : '{i18n>Authors}', TypeNamePlural : '{i18n>Authors}',
Title : {Value : name}, Title : { Value: name },
Description : {Value : dateOfBirth} Description : { Value: dateOfBirth }
}, },
Facets : [{ Facets : [{
$Type : 'UI.ReferenceFacet', $Type : 'UI.ReferenceFacet',
@@ -172,12 +167,12 @@ annotate my.Authors with @(UI : {
// Authors Elements // Authors Elements
// //
annotate my.Authors with { annotate my.Authors with {
ID @title : '{i18n>ID}'; ID @title: '{i18n>ID}';
name @title : '{i18n>Name}'; name @title: '{i18n>Name}';
dateOfBirth @title : '{i18n>DateOfBirth}'; dateOfBirth @title: '{i18n>DateOfBirth}';
dateOfDeath @title : '{i18n>DateOfDeath}'; dateOfDeath @title: '{i18n>DateOfDeath}';
placeOfBirth @title : '{i18n>PlaceOfBirth}'; placeOfBirth @title: '{i18n>PlaceOfBirth}';
placeOfDeath @title : '{i18n>PlaceOfDeath}'; placeOfDeath @title: '{i18n>PlaceOfDeath}';
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
@@ -186,15 +181,15 @@ annotate my.Authors with {
// //
annotate common.Languages with @( annotate common.Languages with @(
Common.SemanticKey : [code], Common.SemanticKey : [code],
Identification : [{Value : code}], Identification : [{ Value: code}],
UI : { UI : {
SelectionFields : [ SelectionFields : [
name, name,
descr descr
], ],
LineItem : [ LineItem : [
{Value : code}, { Value: code },
{Value : name}, { Value: name },
], ],
} }
); );
@@ -207,8 +202,8 @@ annotate common.Languages with @(UI : {
HeaderInfo : { HeaderInfo : {
TypeName : '{i18n>Language}', TypeName : '{i18n>Language}',
TypeNamePlural : '{i18n>Languages}', TypeNamePlural : '{i18n>Languages}',
Title : {Value : name}, Title : { Value: name },
Description : {Value : descr} Description : { Value: descr }
}, },
Facets : [{ Facets : [{
$Type : 'UI.ReferenceFacet', $Type : 'UI.ReferenceFacet',
@@ -216,9 +211,9 @@ annotate common.Languages with @(UI : {
Target : '@UI.FieldGroup#Details' Target : '@UI.FieldGroup#Details'
}, ], }, ],
FieldGroup #Details : {Data : [ FieldGroup #Details : {Data : [
{Value : code}, { Value: code },
{Value : name}, { Value: name },
{Value : descr} { Value: descr }
]}, ]},
}); });
@@ -228,16 +223,16 @@ annotate common.Languages with @(UI : {
// //
annotate common.Currencies with @( annotate common.Currencies with @(
Common.SemanticKey : [code], Common.SemanticKey : [code],
Identification : [{Value : code}], Identification : [{ Value: code}],
UI : { UI : {
SelectionFields : [ SelectionFields : [
name, name,
descr descr
], ],
LineItem : [ LineItem : [
{Value : descr}, { Value: descr },
{Value : symbol}, { Value: symbol },
{Value : code}, { Value: code },
], ],
} }
); );
@@ -250,8 +245,8 @@ annotate common.Currencies with @(UI : {
HeaderInfo : { HeaderInfo : {
TypeName : '{i18n>Currency}', TypeName : '{i18n>Currency}',
TypeNamePlural : '{i18n>Currencies}', TypeNamePlural : '{i18n>Currencies}',
Title : {Value : descr}, Title : { Value: descr },
Description : {Value : code} Description : { Value: code }
}, },
Facets : [ Facets : [
{ {
@@ -266,15 +261,15 @@ annotate common.Currencies with @(UI : {
}, },
], ],
FieldGroup #Details : {Data : [ FieldGroup #Details : {Data : [
{Value : name}, { Value: name },
{Value : symbol}, { Value: symbol },
{Value : code}, { Value: code },
{Value : descr} { Value: descr }
]}, ]},
FieldGroup #Extended : {Data : [ FieldGroup #Extended : {Data : [
{Value : numcode}, { Value: numcode },
{Value : minor}, { Value: minor },
{Value : exponent} { Value: exponent }
]}, ]},
}); });
@@ -283,7 +278,7 @@ annotate common.Currencies with @(UI : {
// Currencies Elements // Currencies Elements
// //
annotate common.Currencies with { annotate common.Currencies with {
numcode @title : '{i18n>NumCode}'; numcode @title: '{i18n>NumCode}';
minor @title : '{i18n>MinorUnit}'; minor @title: '{i18n>MinorUnit}';
exponent @title : '{i18n>Exponent}'; exponent @title: '{i18n>Exponent}';
} }

View File

@@ -1,24 +0,0 @@
{
"authenticationMethod": "route",
"routes": [
{
"source": "^/-/cds/.*",
"destination": "mtx-api",
"authenticationType": "none"
},
{
"source": "^/app/(.*)$",
"target": "$1",
"localDir": ".",
"authenticationType": "xsuaa",
"cacheControl": "no-cache, no-store, must-revalidate"
},
{
"source": "^/(.*)$",
"target": "$1",
"destination": "srv-api",
"authenticationType": "xsuaa",
"csrfProtection": true
}
]
}

View File

@@ -1,5 +0,0 @@
ID_texts;ID;locale;title;descr
201_de;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;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;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
207_fr;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 201_de 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 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 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 207_fr 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

@@ -2,14 +2,9 @@
// Add Author.age and .lifetime with a DB-specific function // Add Author.age and .lifetime with a DB-specific function
// //
using { AdminService, sap.common } from '@capire/bookshop'; using { AdminService } from '@capire/bookshop';
extend projection AdminService.Authors with { extend projection AdminService.Authors with {
YEARS_BETWEEN(dateOfBirth, dateOfDeath) as age: Integer, YEARS_BETWEEN(dateOfBirth, dateOfDeath) as age: Integer,
YEAR(dateOfBirth) || ' ' || YEAR(dateOfDeath) as lifetime : String YEAR(dateOfBirth) || ' ' || YEAR(dateOfDeath) as lifetime : String
} }
// Workaround: include Countries table because csv files point to it
// TODO fix by ignoring hdbtabledata generation for unused entities
annotate common.Countries with @cds.persistence.skip : false;

1
fiori/index.cds Normal file
View File

@@ -0,0 +1 @@
using from './db/common';

View File

@@ -1,97 +0,0 @@
_schema-version: '3.1'
ID: capire.fiori
version: 1.0.0
description: "fiori"
parameters:
enable-parallel-deployments: true
build-parameters:
before-all:
- builder: custom
commands:
- npx -p @sap/cds-dk cds build --production
modules:
- name: fiori-srv
type: nodejs
path: gen/srv
parameters:
buildpack: nodejs_buildpack
build-parameters:
builder: npm
provides:
- name: srv-api # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
- name: mtx-api # potentially required by approuter
properties:
mtx-url: ${default-url}
requires:
- name: fiori-db
- name: fiori-registry
- name: fiori-auth
- name: app-api
properties:
SUBSCRIPTION_URL: ~{app-protocol}://\${tenant_subdomain}-~{app-uri}
- name: fiori
type: approuter.nodejs
path: app/_router # from cds.env.folders. Consider also cds.env.build.target -> gen/app
parameters:
keep-existing-routes: true
disk-quota: 256M
memory: 256M
properties:
TENANT_HOST_PATTERN: "^(.*)-${default-uri}"
requires:
- name: srv-api
group: destinations
properties:
name: srv-api # must be used in xs-app.json as well
url: ~{srv-url}
forwardAuthToken: true
- name: mtx-api
group: destinations
properties:
name: mtx-api # must be used in xs-app.json as well
url: ~{mtx-url}
- name: fiori-auth
provides:
- name: app-api
properties:
app-protocol: ${protocol}
app-uri: ${default-uri}
resources:
- name: fiori-db
type: org.cloudfoundry.managed-service
parameters:
service: service-manager
service-plan: container
- name: fiori-registry
type: org.cloudfoundry.managed-service
requires:
- name: mtx-api
parameters:
service: saas-registry
service-plan: application
config:
xsappname: fiori-${org}-${space}
appName: fiori-${org}-${space}
displayName: fiori
description: A simple CAP project.
category: 'Category'
appUrls:
getDependencies: ~{mtx-api/mtx-url}/-/cds/saas-provisioning/dependencies
onSubscription: ~{mtx-api/mtx-url}/-/cds/saas-provisioning/tenant/{tenantId}
onSubscriptionAsync: false
onUnSubscriptionAsync: false
callbackTimeoutMillis: 300000
- name: fiori-auth
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: application
path: ./xs-security.json
config:
xsappname: fiori-${org}-${space}
tenant-mode: shared

View File

@@ -4,24 +4,15 @@
"dependencies": { "dependencies": {
"@capire/bookstore": "*", "@capire/bookstore": "*",
"@sap/cds": ">=5", "@sap/cds": ">=5",
"@sap/cds-mtxs": "^1",
"@sap/cds-odata-v2-adapter-proxy": "^1.9.0", "@sap/cds-odata-v2-adapter-proxy": "^1.9.0",
"@sap/xssec": "^3",
"express": "^4.17.1", "express": "^4.17.1",
"hdb": "^0.19.5",
"passport": ">=0.4.1" "passport": ">=0.4.1"
}, },
"scripts": { "scripts": {
"start": "cds run --in-memory?", "start": "cds run --in-memory?",
"watch": "cds watch" "watch": "cds watch"
}, },
"engines": {
"node": "^16"
},
"cds": { "cds": {
"features": {
"deploy_data_onconflict": "replace"
},
"requires": { "requires": {
"ReviewsService": { "ReviewsService": {
"kind": "odata", "kind": "odata",
@@ -43,7 +34,7 @@
} }
}, },
"db": { "db": {
"kind": "sql-mt" "kind": "sql"
}, },
"db-ext": { "db-ext": {
"[development]": { "[development]": {
@@ -52,41 +43,10 @@
"[production]": { "[production]": {
"model": "db/hana" "model": "db/hana"
} }
},
"multitenancy": true,
"toggles": true,
"extensibility": true,
"cds.xt.ExtensibilityService": {
"element-prefix": [
"x_"
],
"extension-allowlist": [
{
"for": [
"sap.capire.orders"
],
"kind": "entity",
"new-fields": 3
},
{
"for": [
"OrdersService"
],
"new-entities": 2
}
]
},
"[production]": {
"auth": {
"kind": "xsuaa"
},
"db": {
"kind": "hana-mt"
} }
}, },
"approuter": { "hana": {
"kind": "cloudfoundry" "deploy-format": "hdbtable"
}
} }
} }
} }

View File

@@ -1,15 +1,8 @@
const cds = require("@sap/cds")
// install OData v2 adapter // install OData v2 adapter
const cds = require("@sap/cds")
const proxy = require('@sap/cds-odata-v2-adapter-proxy') const proxy = require('@sap/cds-odata-v2-adapter-proxy')
const proxyOpts = global.it ? { target:'auto' } : {} // for tests, set 'auto' to detect port dynamically const opts = global.it ? { target:'auto' } : {} // for tests, set 'auto' to detect port dynamically
cds.on('bootstrap', app => app.use(proxy(proxyOpts))) cds.on('bootstrap', app => app.use(proxy(opts))) // install proxy
cds.log('cov2ap','silent') // suppress anoying log outpout, e.g. for `npm run mocha -- --reporter nyan`
module.exports = require('@capire/bookstore/server.js') module.exports = require('@capire/bookstore/server.js')
// For didactic reasons in capire, run below services embedded
// TODO find a better way to switch this
if (cds.requires.multitenancy) {
delete cds.env.requires.OrdersService
delete cds.env.requires.ReviewsService
}

View File

@@ -1 +0,0 @@
using from '@capire/bookstore';

View File

@@ -1,51 +0,0 @@
{
"scopes": [
{
"name": "$XSAPPNAME.admin",
"description": "admin"
},
{
"name": "$XSAPPNAME.mtcallback",
"description": "Subscription via SaaS Registry",
"grant-as-authority-to-apps": [
"$XSAPPNAME(application,sap-provisioning,tenant-onboarding)"
]
},
{
"name": "$XSAPPNAME.cds.Subscriber",
"description": "Subscribe to applications"
},
{
"name": "$XSAPPNAME.cds.ExtensionDeveloper",
"description": "Extend CAP applications via extension projects"
},
{
"name": "$XSAPPNAME.cds.UIFlexDeveloper",
"description": "Extend CAP applications via UIFlex extensibility"
}
],
"attributes": [],
"role-templates": [
{
"name": "admin",
"description": "admin",
"scope-references": [
"$XSAPPNAME.admin"
]
},
{
"name": "ExtensionDeveloper",
"description": "Extension development including UIFlex extensibility",
"scope-references": [
"$XSAPPNAME.cds.ExtensionDeveloper",
"$XSAPPNAME.cds.UIFlexDeveloper"
]
}
],
"authorities-inheritance": false,
"authorities": [
"$XSAPPNAME.cds.Subscriber",
"$XSAPPNAME.cds.ExtensionDeveloper",
"$XSAPPNAME.cds.UIFlexDeveloper"
]
}

18
gdpr/.pdm/pdm-config.json Normal file
View File

@@ -0,0 +1,18 @@
{
"fullyQualifiedApplicationName": "gdpr-bookshop",
"fullyQualifiedModuleName": "gdpr-srv",
"applicationTitle": "PDM Bookshop",
"applicationTitleKey": "PDM Bookshop",
"applicationURL": "https://gdpr-srv.cfapps.sap.hana.ondemand.com/",
"endPoints": [
{
"type": "odatav4",
"serviceName": "pdm-service",
"serviceTitle": "GDPR",
"serviceTitleKey": "GDPR",
"serviceURI": "pdm",
"hasGdprV4Annotations": true,
"cacheControl": "no-cache"
}
]
}

View File

@@ -0,0 +1,8 @@
{
"xs-security": {
"xsappname": "gdpr-bookshop",
"authorities": ["$ACCEPT_GRANTED_AUTHORITIES"]
},
"fullyQualifiedApplicationName": "gdpr-bookshop",
"appConsentServiceEnabled": true
}

28
gdpr/db/AuditLogStore.cds Normal file
View File

@@ -0,0 +1,28 @@
using {
managed,
cuid,
sap.common.CodeList
} from '@sap/cds/common';
namespace sap.capire.auditLog;
entity AuditLogStore : cuid {
Action : String enum {
DataAccess;
DataModification
};
User : String;
Timestamp : Timestamp;
Tenant : String;
Channel : String;
DataSubjectType : String; // Bussiness Partner
DataSubjectRole : String; // Customer // Employee // ...
DataSubjectID : LargeString; // key value pair as JSON
ObjectType : String; // like SalesOrder
ObjectKey : LargeString; // key value pair as JSON
Blob : LargeString; // Payload: DataModification or Data Access as BLOB
}

38
gdpr/db/data-privacy.cds Normal file
View File

@@ -0,0 +1,38 @@
// Proxy for importing schema from bookshop sample
using {sap.capire.bookshop} from './schema';
// annotations for Data Privacy
annotate bookshop.Customers with @PersonalData: {
DataSubjectRole: 'Customer',
EntitySemantics: 'DataSubject'
} {
ID @PersonalData.FieldSemantics : 'DataSubjectID';
email @PersonalData.IsPotentiallyPersonal;
firstName @PersonalData.IsPotentiallyPersonal;
lastName @PersonalData.IsPotentiallyPersonal;
dateOfBirth @PersonalData.IsPotentiallyPersonal;
}
annotate bookshop.BillingData with @PersonalData: {
DataSubjectRole: 'Customer',
EntitySemantics: 'DataSubjectDetails'
} {
customer @PersonalData.FieldSemantics : 'DataSubjectID';
creditCardNo @PersonalData.IsPotentiallySensitive;
}
annotate bookshop.Addresses with @PersonalData: {
DataSubjectRole: 'Customer',
EntitySemantics: 'DataSubjectDetails'
} {
customer @PersonalData.FieldSemantics : 'DataSubjectID';
street @PersonalData.IsPotentiallyPersonal;
town @PersonalData.IsPotentiallyPersonal;
country @PersonalData.IsPotentiallyPersonal;
}
annotate bookshop.Orders with @PersonalData.EntitySemantics: 'Other' {
ID @PersonalData.FieldSemantics : 'ContractRelatedID';
customer @PersonalData.FieldSemantics : 'DataSubjectID';
personalComment @PersonalData.IsPotentiallyPersonal;
}

View File

@@ -0,0 +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
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

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

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

@@ -0,0 +1,3 @@
ID;modifiedAt;createdAt;createdBy;modifiedBy;Customer_ID;creditCardNo
1e2f2640-6866-4dcf-8f4d-3027aa831cad;2019-04-04;2019-01-31;admin@business.com;admin@business.com;8e2f2640-6866-4dcf-8f4d-3027aa831cad;2222-1111-6666-7777
24e718c9-ff99-47f1-8ca3-950c850777d4;2019-04-04;2019-01-30;admin@business.com;admin@business.com;74e718c9-ff99-47f1-8ca3-950c850777d4;3333-2222-5555-8888
1 ID modifiedAt createdAt createdBy modifiedBy Customer_ID creditCardNo
2 1e2f2640-6866-4dcf-8f4d-3027aa831cad 2019-04-04 2019-01-31 admin@business.com admin@business.com 8e2f2640-6866-4dcf-8f4d-3027aa831cad 2222-1111-6666-7777
3 24e718c9-ff99-47f1-8ca3-950c850777d4 2019-04-04 2019-01-30 admin@business.com admin@business.com 74e718c9-ff99-47f1-8ca3-950c850777d4 3333-2222-5555-8888

View File

@@ -0,0 +1,3 @@
ID;modifiedAt;createdAt;createdBy;modifiedBy;Customer_ID;street;town;country_code;someOtherField
1e2f2640-6866-4dcf-8f4d-3027aa831cad;2019-04-04;2019-01-31;admin@business.com;admin@business.com;8e2f2640-6866-4dcf-8f4d-3027aa831cad;Hauptstrasse 11;Berlin;DE;Eine Bemerkung
24e718c9-ff99-47f1-8ca3-950c850777d4;2019-04-04;2019-01-30;admin@business.com;admin@business.com;74e718c9-ff99-47f1-8ca3-950c850777d4;Main Street 22;London;GB;Some Remark
1 ID modifiedAt createdAt createdBy modifiedBy Customer_ID street town country_code someOtherField
2 1e2f2640-6866-4dcf-8f4d-3027aa831cad 2019-04-04 2019-01-31 admin@business.com admin@business.com 8e2f2640-6866-4dcf-8f4d-3027aa831cad Hauptstrasse 11 Berlin DE Eine Bemerkung
3 24e718c9-ff99-47f1-8ca3-950c850777d4 2019-04-04 2019-01-30 admin@business.com admin@business.com 74e718c9-ff99-47f1-8ca3-950c850777d4 Main Street 22 London GB Some Remark

View File

@@ -0,0 +1,3 @@
ID;modifiedAt;createdAt;createdBy;modifiedBy;email;firstName;lastName;dateOfBirth
8e2f2640-6866-4dcf-8f4d-3027aa831cad;2019-04-04;2019-01-31;admin@business.com;admin@business.com;john.doe@test.com;John;Doe;1970-01-01
74e718c9-ff99-47f1-8ca3-950c850777d4;2019-04-04;2019-01-30;admin@business.com;admin@business.com;jane.doe@sap.com;Jane;Doe;1980-11-11
1 ID modifiedAt createdAt createdBy modifiedBy email firstName lastName dateOfBirth
2 8e2f2640-6866-4dcf-8f4d-3027aa831cad 2019-04-04 2019-01-31 admin@business.com admin@business.com john.doe@test.com John Doe 1970-01-01
3 74e718c9-ff99-47f1-8ca3-950c850777d4 2019-04-04 2019-01-30 admin@business.com admin@business.com jane.doe@sap.com Jane Doe 1980-11-11

View File

@@ -0,0 +1,16 @@
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 10 Fiction
3 11 10 Drama
4 12 10 Poetry
5 13 10 Fantasy
6 14 10 Science Fiction
7 15 10 Romance
8 16 10 Mystery
9 17 10 Thriller
10 18 10 Dystopia
11 19 10 Fairy Tale
12 20 Non-Fiction
13 21 20 Biography
14 22 21 Autobiography
15 23 20 Essay
16 24 20 Speech

View File

@@ -0,0 +1,4 @@
ID;amount;parent_ID;book_ID;netAmount
78040e66-1dcd-4ffb-ab10-fdce32028b79;1;5e2f2640-6866-4dcf-8f4d-3027aa831cad;201;11.11
84e718c9-ff99-47f1-8ca3-950c850777d4;1;5e2f2640-6866-4dcf-8f4d-3027aa831cad;271;15
f9641166-e050-4261-bfee-d1e797e6cb7f;2;44e718c9-ff99-47f1-8ca3-950c850777d4;252;28
1 ID amount parent_ID book_ID netAmount
2 78040e66-1dcd-4ffb-ab10-fdce32028b79 1 5e2f2640-6866-4dcf-8f4d-3027aa831cad 201 11.11
3 84e718c9-ff99-47f1-8ca3-950c850777d4 1 5e2f2640-6866-4dcf-8f4d-3027aa831cad 271 15
4 f9641166-e050-4261-bfee-d1e797e6cb7f 2 44e718c9-ff99-47f1-8ca3-950c850777d4 252 28

View File

@@ -0,0 +1,3 @@
ID;modifiedAt;createdAt;createdBy;modifiedBy;OrderNo;currency_code;Customer_ID
5e2f2640-6866-4dcf-8f4d-3027aa831cad;2019-04-04;2019-01-31;john.doe@test.com;john.doe@test.com;1;USD;8e2f2640-6866-4dcf-8f4d-3027aa831cad
44e718c9-ff99-47f1-8ca3-950c850777d4;2019-04-04;2019-01-30;jane.doe@test.com;jane.doe@test.com;2;USD;74e718c9-ff99-47f1-8ca3-950c850777d4
1 ID modifiedAt createdAt createdBy modifiedBy OrderNo currency_code Customer_ID
2 5e2f2640-6866-4dcf-8f4d-3027aa831cad 2019-04-04 2019-01-31 john.doe@test.com john.doe@test.com 1 USD 8e2f2640-6866-4dcf-8f4d-3027aa831cad
3 44e718c9-ff99-47f1-8ca3-950c850777d4 2019-04-04 2019-01-30 jane.doe@test.com jane.doe@test.com 2 USD 74e718c9-ff99-47f1-8ca3-950c850777d4

41
gdpr/db/schema.cds Normal file
View File

@@ -0,0 +1,41 @@
// Proxy for importing schema from bookshop sample
using {sap.capire.bookshop.Books} from '../../bookshop/db/schema';
using {sap.capire.orders.Orders} from '../../orders/db/schema';
using {sap.capire.orders.OrderItems} from '../../orders/db/schema';
using {
Country,
managed,
cuid
} from '@sap/cds/common';
namespace sap.capire.bookshop;
extend Orders with {
customer : Association to Customers;
personalComment : String;
}
entity Customers : cuid, managed {
email : String;
firstName : String;
lastName : String;
dateOfBirth : Date;
billingData : Composition of BillingData
on billingData.customer = $self;
addresses : Composition of Addresses
on addresses.customer = $self;
}
entity Addresses : cuid, managed {
customer : Association to one Customers;
street : String(128);
town : String(128);
country : Country;
someOtherField : String(128);
};
entity BillingData : cuid, managed {
customer : Association to one Customers;
creditCardNo : String;
};

136
gdpr/db/src/.hdiconfig Normal file
View File

@@ -0,0 +1,136 @@
{
"file_suffixes": {
"csv": {
"plugin_name": "com.sap.hana.di.tabledata.source"
},
"hdbafllangprocedure": {
"plugin_name": "com.sap.hana.di.afllangprocedure"
},
"hdbanalyticprivilege": {
"plugin_name": "com.sap.hana.di.analyticprivilege"
},
"hdbcalculationview": {
"plugin_name": "com.sap.hana.di.calculationview"
},
"hdbcollection": {
"plugin_name": "com.sap.hana.di.collection"
},
"hdbconstraint": {
"plugin_name": "com.sap.hana.di.constraint"
},
"hdbdropcreatetable": {
"plugin_name": "com.sap.hana.di.dropcreatetable"
},
"hdbflowgraph": {
"plugin_name": "com.sap.hana.di.flowgraph"
},
"hdbfunction": {
"plugin_name": "com.sap.hana.di.function"
},
"hdbgraphworkspace": {
"plugin_name": "com.sap.hana.di.graphworkspace"
},
"hdbhadoopmrjob": {
"plugin_name": "com.sap.hana.di.virtualfunctionpackage.hadoop"
},
"hdbindex": {
"plugin_name": "com.sap.hana.di.index"
},
"hdblibrary": {
"plugin_name": "com.sap.hana.di.library"
},
"hdbmigrationtable": {
"plugin_name": "com.sap.hana.di.table.migration"
},
"hdbprocedure": {
"plugin_name": "com.sap.hana.di.procedure"
},
"hdbprojectionview": {
"plugin_name": "com.sap.hana.di.projectionview"
},
"hdbprojectionviewconfig": {
"plugin_name": "com.sap.hana.di.projectionview.config"
},
"hdbreptask": {
"plugin_name": "com.sap.hana.di.reptask"
},
"hdbresultcache": {
"plugin_name": "com.sap.hana.di.resultcache"
},
"hdbrole": {
"plugin_name": "com.sap.hana.di.role"
},
"hdbroleconfig": {
"plugin_name": "com.sap.hana.di.role.config"
},
"hdbsearchruleset": {
"plugin_name": "com.sap.hana.di.searchruleset"
},
"hdbsequence": {
"plugin_name": "com.sap.hana.di.sequence"
},
"hdbstatistics": {
"plugin_name": "com.sap.hana.di.statistics"
},
"hdbstructuredprivilege": {
"plugin_name": "com.sap.hana.di.structuredprivilege"
},
"hdbsynonym": {
"plugin_name": "com.sap.hana.di.synonym"
},
"hdbsynonymconfig": {
"plugin_name": "com.sap.hana.di.synonym.config"
},
"hdbsystemversioning": {
"plugin_name": "com.sap.hana.di.systemversioning"
},
"hdbtable": {
"plugin_name": "com.sap.hana.di.table"
},
"hdbtabledata": {
"plugin_name": "com.sap.hana.di.tabledata"
},
"hdbtabletype": {
"plugin_name": "com.sap.hana.di.tabletype"
},
"hdbtrigger": {
"plugin_name": "com.sap.hana.di.trigger"
},
"hdbview": {
"plugin_name": "com.sap.hana.di.view"
},
"hdbvirtualfunction": {
"plugin_name": "com.sap.hana.di.virtualfunction"
},
"hdbvirtualfunctionconfig": {
"plugin_name": "com.sap.hana.di.virtualfunction.config"
},
"hdbvirtualpackagehadoop": {
"plugin_name": "com.sap.hana.di.virtualpackage.hadoop"
},
"hdbvirtualpackagesparksql": {
"plugin_name": "com.sap.hana.di.virtualpackage.sparksql"
},
"hdbvirtualprocedure": {
"plugin_name": "com.sap.hana.di.virtualprocedure"
},
"hdbvirtualprocedureconfig": {
"plugin_name": "com.sap.hana.di.virtualprocedure.config"
},
"hdbvirtualtable": {
"plugin_name": "com.sap.hana.di.virtualtable"
},
"hdbvirtualtableconfig": {
"plugin_name": "com.sap.hana.di.virtualtable.config"
},
"properties": {
"plugin_name": "com.sap.hana.di.tabledata.properties"
},
"tags": {
"plugin_name": "com.sap.hana.di.tabledata.properties"
},
"txt": {
"plugin_name": "com.sap.hana.di.copyonly"
}
}
}

4
gdpr/index.cds Normal file
View File

@@ -0,0 +1,4 @@
namespace sap.capire.gdpr; //> important for reflection
using from './db/schema';
using from './srv/pdm-service';
using from './srv/log-service';

31
gdpr/manifest.yml Normal file
View File

@@ -0,0 +1,31 @@
# Generated manifest.yml based on template version 0.1.0
# appName = gdpr
# language=nodejs
# multiTenant=false
---
applications:
# -----------------------------------------------------------------------------------
# Backend Service
# -----------------------------------------------------------------------------------
- name: gdpr-srv
path: gen/srv
memory: 256M
buildpack: nodejs_buildpack
services:
- gdpr-db
- uaa
# - name: pdm
# parameters: ./pdm-config.json
# -----------------------------------------------------------------------------------
# HANA Database Content Deployer App
# -----------------------------------------------------------------------------------
- name: gdpr-db-deployer
path: gen/db
no-route: true
health-check-type: process
memory: 256M
instances: 1
buildpack: nodejs_buildpack
services:
- gdpr-db

72
gdpr/mta.yaml Normal file
View File

@@ -0,0 +1,72 @@
## Generated mta.yaml based on template version 0.4.0
## appName = gdpr
## language=nodejs; multitenant=false
## approuter=
_schema-version: '3.1'
ID: capire.gdpr
version: 1.0.0
description: "gdpr"
parameters:
enable-parallel-deployments: true
build-parameters:
before-all:
- builder: custom
commands:
- npm install --production
- npx -p @sap/cds-dk cds build --production
modules:
# --------------------- SERVER MODULE ------------------------
- name: gdpr-srv
# ------------------------------------------------------------
type: nodejs
path: gen/srv
parameters:
buildpack: nodejs_buildpack
requires:
# Resources extracted from CAP configuration
- name: gdpr-db
- name: gdpr-uaa
provides:
- name: srv-api # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
# -------------------- SIDECAR MODULE ------------------------
- name: gdpr-db-deployer
# ------------------------------------------------------------
type: hdb
path: gen/db
parameters:
buildpack: nodejs_buildpack
requires:
# 'hana' and 'xsuaa' resources extracted from CAP configuration
- name: gdpr-db
- name: gdpr-uaa
resources:
# services extracted from CAP configuration
# 'service-plan' can be configured via 'cds.requires.<name>.vcap.plan'
# ------------------------------------------------------------
- name: gdpr-db
# ------------------------------------------------------------
type: com.sap.xs.hdi-container
parameters:
service: hana # or 'hanatrial' on trial landscapes
service-plan: hdi-shared
properties:
hdi-service-name: ${service-name}
# ------------------------------------------------------------
- name: gdpr-uaa
# ------------------------------------------------------------
type: org.cloudfoundry.managed-service
parameters:
service: xsuaa
service-plan: application
config:
xsappname: gdpr-${space} # name + space dependency
tenant-mode: dedicated

2248
gdpr/package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

33
gdpr/package.json Normal file
View File

@@ -0,0 +1,33 @@
{
"name": "@capire/gdpr",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "../bookshop",
"@capire/common": "../common",
"@capire/orders": "../orders",
"@sap/cds": "^5",
"@sap/hana-client": "^2.4.177",
"@sap/xsenv": "^3.1.0",
"@sap/xssec": "^3.1.1",
"express": "^4.17.1",
"passport": "^0.4.1"
},
"scripts": {
"start": "cds run --in-memory?",
"watch": "cds watch"
},
"cds": {
"requires": {
"db": {
"kind": "sql"
},
"uaa": {
"kind": "xsuaa"
},
"audit-log": {
"impl": "srv/customAuditLog.js"
}
},
"features": {"audit_personal_data": true}
}
}

View File

@@ -0,0 +1,18 @@
# Generated services-manifest.yml based on template version 0.1.0
# appName = gdpr
---
create-services:
# ------------------------------------------------------------
- name: gdpr-db
broker: hana # 'hanatrial' on trial landscapes
plan: "hdi-shared"
- name: pdm
broker: personal-data-manager-service
plan: standard
parameters: ./.pdm/pdm-instance-config.json
- name: uaa
broker: xsuaa
plan: application
parameters: xs-security.json

View File

@@ -0,0 +1,70 @@
const cds = require('@sap/cds')
// FIXME: no longer works like this with new audit logging plugin
module.exports = class MyAuditLogService extends cds.AuditLogService {
async init() {
// console.log('My Audit Log');
// call AuditLogService's init
await super.init()
const db = await cds.connect.to('db')
const { AuditLogStore } = db.entities('sap.capire.auditLog')
// register custom handlers
this.on('dataAccessLog', async req => {
const logs = []
const action = 'DataAccess'
const user = req.user.id
const timestamp = req.timestamp
const tenant = req.tenant
const channel = req.channel
req.data.accesses.forEach(dataAccess => {
logs.push({
Action: action,
User: user,
Timestamp: timestamp,
Tenant: tenant,
Channel: channel,
DataSubjectType: dataAccess.data_subject.type,
DataSubjectRole: dataAccess.data_subject.role,
DataSubjectID: JSON.stringify(dataAccess.data_subject.id),
ObjectType: dataAccess.object.type,
ObjectKey: JSON.stringify(dataAccess.object.id),
Blob: JSON.stringify(dataAccess)
})
})
await INSERT.into(AuditLogStore).entries(logs)
})
this.on('dataModificationLog', async req => {
const mods = []
const action = 'DataModification'
const user = req.user.id
const timestamp = req.timestamp
const tenant = req.tenant
const channel = req.channel
req.data.modifications.forEach(dataModification => {
mods.push({
Action: action,
User: user,
Timestamp: timestamp,
Tenant: tenant,
Channel: channel,
DataSubjectType: dataModification.data_subject.type,
DataSubjectRole: dataModification.data_subject.role,
DataSubjectID: JSON.stringify(dataModification.data_subject.id),
ObjectType: dataModification.object.type,
ObjectKey: JSON.stringify(dataModification.object.id),
Blob: JSON.stringify(dataModification)
})
})
await INSERT.into(AuditLogStore).entries(mods)
})
}
}

13
gdpr/srv/log-service.cds Normal file
View File

@@ -0,0 +1,13 @@
using {sap.capire.bookshop as db} from '../db/data-privacy';
using {sap.capire.orders as dbo} from '../db/data-privacy';
using {sap.capire.auditLog as log} from '../db/AuditLogStore.cds';
//@requires: 'PersonalDataManagerUser' // security check
service LogService {
entity Customers as projection on db.Customers;
entity Addresses as projection on db.Addresses;
entity Orders as projection on dbo.Orders;
entity AuditLogStore as projection on log.AuditLogStore;
};

43
gdpr/srv/pdm-service.cds Normal file
View File

@@ -0,0 +1,43 @@
using {sap.capire.bookshop as db} from '../db/data-privacy';
using {sap.capire.bookshop.Books} from '../db/data-privacy';
using {sap.capire.orders.Orders} from '../db/data-privacy';
using {sap.capire.orders.OrderItems} from '../db/data-privacy';
//@requires: 'PersonalDataManagerUser' // security check
service PDMService {
// Data Privacy annotations on 'Customers', 'Addresses', and 'BillingData' are derived from original entity definitions
entity Customers as projection on db.Customers;
entity Addresses as projection on db.Addresses;
entity BillingData as projection on db.BillingData;
// create view on Orders and Items as flat projection
entity OrderItemView as
select from Orders {
ID,
key Items.ID as item_ID,
OrderNo,
customer.ID as customer_ID,
customer.email as customer_email,
Items.book.ID as item_Book_ID,
Items.amount as item_Amount,
Items.netAmount as item_NetAmount
};
// annotate new view
annotate PDMService.OrderItemView with @(PersonalData.EntitySemantics: 'Other') {
item_ID @PersonalData.FieldSemantics: 'ContractRelatedID';
customer_ID @PersonalData.FieldSemantics: 'DataSubjectID';
customer_email @PersonalData.IsPotentiallyPersonal;
};
// annotations for Personal Data Manager - Search Fields
annotate Customers with @(Communication.Contact: {
n : {
surname: lastName,
given : firstName
},
bday: dateOfBirth
});
};

25
gdpr/test.http Normal file
View File

@@ -0,0 +1,25 @@
###
get http://localhost:4004/log/AuditLogStore
###
get http://localhost:4004/log/Customers
###
post http://localhost:4004/log/Customers
Content-Type: application/json
{
"ID": "22e718c9-ff99-47f1-8ca3-950c850777d4",
"createdAt": "2019-01-30T00:00:00.000Z",
"createdBy": "admin@business.com",
"modifiedAt": "2019-04-04T00:00:00.000Z",
"modifiedBy": "admin@business.com",
"email": "johanna.doe@company.org",
"firstName": "Queen Johanna",
"lastName": "Doe",
"creditCardNo": "1313-7171-5656-7878",
"dateOfBirth": "2001-11-11"
}

13
gdpr/xs-security.json Normal file
View File

@@ -0,0 +1,13 @@
{
"xsappname": "gdpr-bookshop",
"tenant-mode": "shared",
"scopes": [
{
"name": "$XSAPPNAME.PersonalDataManagerUser",
"description": "Authority for Personal Data Manager",
"grant-as-authority-to-apps": [
"$XSSERVICENAME(pdm)"
]
}
]
}

View File

@@ -1,3 +1,7 @@
module.exports = class say { module.exports = class say {
hello(req) { return `Hello ${req.data.to}!` } hello(req) {
let {to} = req.data
if (to === 'me') to = require('os').userInfo().username
return `Hello ${to}!`
}
} }

76
loggers/app/loggers.html Normal file
View File

@@ -0,0 +1,76 @@
<!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>

22
loggers/package.json Normal file
View File

@@ -0,0 +1,22 @@
{
"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 run",
"watch": "cds watch"
},
"cds": {
"requires": {
"db": "sql"
}
}
}

11
loggers/readme.md Normal file
View File

@@ -0,0 +1,11 @@
# 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`

3
loggers/srv/dummy.cds Normal file
View File

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

20
loggers/srv/loggers.cds Normal file
View File

@@ -0,0 +1,20 @@
@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;
}

56
loggers/srv/loggers.js Normal file
View File

@@ -0,0 +1,56 @@
const cds = require ('@sap/cds/lib')
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

@@ -0,0 +1,18 @@
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

@@ -40,7 +40,7 @@ module.exports = srv => {
req.reject(404, 'Media not found for the ID') req.reject(404, 'Media not found for the ID')
return return
} }
const decodedMedia = new Buffer( const decodedMedia = Buffer.from(
mediaObj.media.split(';base64,').pop(), mediaObj.media.split(';base64,').pop(),
'base64' 'base64'
) )

View File

@@ -16,23 +16,24 @@ using { OrdersService } from '../srv/orders-service';
@odata.draft.enabled @odata.draft.enabled
annotate OrdersService.Orders with @( annotate OrdersService.Orders with @(
UI: { UI: {
SelectionFields: [ createdAt, createdBy ], SelectionFields: [ createdBy ],
LineItem: [ LineItem: [
{Value: OrderNo, Label:'OrderNo'}, {Value: OrderNo, Label:'{i18n>OrderNo}'},
{Value: buyer, Label:'Customer'}, {Value: buyer, Label:'{i18n>Customer}'},
{Value: createdAt, Label:'Date'} {Value: currency.symbol, Label:'{i18n>Currency}'},
{Value: createdAt, Label:'{i18n>Date}'},
], ],
HeaderInfo: { HeaderInfo: {
TypeName: 'Order', TypeNamePlural: 'Orders', TypeName: '{i18n>Order}', TypeNamePlural: '{i18n>Orders}',
Title: { Title: {
Label: 'Order number ', //A label is possible but it is not considered on the ObjectPage yet Label: '{i18n>OrderNo}', //A label is possible but it is not considered on the ObjectPage yet
Value: OrderNo Value: OrderNo
}, },
Description: {Value: createdBy} Description: {Value: createdBy}
}, },
Identification: [ //Is the main field group Identification: [ //Is the main field group
{Value: createdBy, Label:'Customer'}, {Value: createdBy, Label:'{i18n>Customer}'},
{Value: createdAt, Label:'Date'}, {Value: createdAt, Label:'{i18n>Date}'},
{Value: OrderNo }, {Value: OrderNo },
], ],
HeaderFacets: [ HeaderFacets: [
@@ -45,7 +46,7 @@ annotate OrdersService.Orders with @(
], ],
FieldGroup#Details: { FieldGroup#Details: {
Data: [ Data: [
{Value: currency.code, Label:'Currency'} {Value: currency.code, Label:'{i18n>Currency}'}
] ]
}, },
FieldGroup#Created: { FieldGroup#Created: {
@@ -64,6 +65,7 @@ annotate OrdersService.Orders with @(
) { ) {
createdAt @UI.HiddenFilter:false; createdAt @UI.HiddenFilter:false;
createdBy @UI.HiddenFilter:false; createdBy @UI.HiddenFilter:false;
ID @UI.Hidden;
}; };
@@ -71,15 +73,15 @@ annotate OrdersService.Orders with @(
annotate OrdersService.Orders.Items with @( annotate OrdersService.Orders.Items with @(
UI: { UI: {
LineItem: [ LineItem: [
{Value: product_ID, Label:'Product ID'}, {Value: product_ID, Label:'{i18n>ProductID}'},
{Value: title, Label:'Product Title'}, {Value: title, Label:'{i18n>ProductTitle}'},
{Value: price, Label:'Unit Price'}, {Value: price, Label:'{i18n>UnitPrice}'},
{Value: quantity, Label:'Quantity'}, {Value: quantity, Label:'{i18n>Quantity}'},
], ],
Identification: [ //Is the main field group Identification: [ //Is the main field group
{Value: quantity, Label:'Quantity'}, {Value: quantity, Label:'{i18n>Quantity}'},
{Value: title, Label:'Product'}, {Value: title, Label:'{i18n>Product}'},
{Value: price, Label:'Unit Price'}, {Value: price, Label:'{i18n>UnitPrice}'},
], ],
Facets: [ Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: '@UI.Identification'}, {$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: '@UI.Identification'},
@@ -89,4 +91,7 @@ annotate OrdersService.Orders.Items with @(
quantity @( quantity @(
Common.FieldControl: #Mandatory Common.FieldControl: #Mandatory
); );
ID @UI.Hidden;
up_ @UI.Hidden;
}; };

View File

@@ -13,7 +13,7 @@
applications: { applications: {
"manage-orders": { "manage-orders": {
title: "Manage Orders", title: "Manage Orders",
description: "... testing FE v42", description: "CAP Sample App",
additionalInformation: "SAPUI5.Component=orders", additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL", applicationType : "URL",
url: "/orders/webapp", url: "/orders/webapp",

View File

@@ -3,8 +3,8 @@
"sap.app": { "sap.app": {
"id": "orders", "id": "orders",
"type": "application", "type": "application",
"title": "Order Books", "title": "Order Management",
"description": "Sample Application", "description": "CAP Sample Application",
"i18n": "i18n/i18n.properties", "i18n": "i18n/i18n.properties",
"dataSources": { "dataSources": {
"OrdersService": { "OrdersService": {

View File

@@ -1,19 +1,22 @@
using { Currency, User, managed, cuid } from '@sap/cds/common'; using { sap.capire.bookshop.Books } from '../../bookshop/db/schema';
using { User, Currency, managed, cuid } from '../../common';
namespace sap.capire.orders; namespace sap.capire.orders;
entity Orders : cuid, managed { entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key OrderNo : String(22) @title:'Order Number'; //> readable key
Items : Composition of many { Items : Composition of many OrderItems;
key ID : UUID;
product : Association to Products;
quantity : Integer;
title : String; //> intentionally replicated as snapshot from product.title
price : Double; //> materialized calculated field
};
buyer : User; buyer : User;
currency : Currency; currency : Currency;
} }
entity OrderItems : cuid, managed {
book : Association to Products;
quantity : Integer;
title : String; //> intentionally replicated as snapshot from product.title
amount : Double; //> materialized calculated field
netAmount : Double;
}
/** This is a stand-in for arbitrary ordered Products */ /** This is a stand-in for arbitrary ordered Products */
entity Products @(cds.persistence.skip:'always') { entity Products @(cds.persistence.skip:'always') {
key ID : String; key ID : String;
@@ -21,4 +24,4 @@ entity Products @(cds.persistence.skip:'always') {
// this is to ensure we have filled-in currencies // this is to ensure we have filled-in currencies
using from '@capire/common'; // using from '@capire/common';

4706
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,13 +5,29 @@
"repository": "https://github.com/sap-samples/cloud-cap-samples.git", "repository": "https://github.com/sap-samples/cloud-cap-samples.git",
"author": "daniel.hutzel@sap.com", "author": "daniel.hutzel@sap.com",
"dependencies": { "dependencies": {
"@capire/bookshop": "./bookshop",
"@capire/common": "./common",
"@capire/fiori": "./fiori",
"@capire/media": "./media",
"@capire/orders": "./orders",
"@capire/reviews": "./reviews",
"@sap/cds": ">=5.5.3" "@sap/cds": ">=5.5.3"
}, },
"workspaces": [ "workspaces": [
"./*/" "./bookshop",
"./bookstore",
"./common",
"./data-viewer",
"./fiori",
"./hello",
"./media",
"./orders",
"./loggers",
"./reviews"
], ],
"devDependencies": { "devDependencies": {
"axios": "^0", "@sap/eslint-plugin-cds": "^2.6.1",
"axios": "^1",
"chai": "^4.3.4", "chai": "^4.3.4",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0", "chai-subset": "^1.6.0",
@@ -20,13 +36,12 @@
}, },
"scripts": { "scripts": {
"cleanup": "rm -rf node_modules && rm -rf */node_modules && rm -rf */*/node_modules", "cleanup": "rm -rf node_modules && rm -rf */node_modules && rm -rf */*/node_modules",
"registry": "node .registry/server.js",
"bookshop": "cds watch bookshop", "bookshop": "cds watch bookshop",
"fiori": "cds watch fiori", "fiori": "cds watch fiori",
"hello": "cds watch hello", "hello": "cds watch hello",
"media": "cds watch media", "media": "cds watch media",
"mocha": "npx mocha || echo", "mocha": "CDS_TEST_SILENT=y npx mocha",
"jest": "npx jest", "jest": "npx jest --silent",
"start": "cds watch fiori", "start": "cds watch fiori",
"test": "npm run jest -- --silent", "test": "npm run jest -- --silent",
"test:hello": "cd hello && npm test" "test:hello": "cd hello && npm test"
@@ -39,7 +54,8 @@
}, },
"mocha": { "mocha": {
"recursive": true, "recursive": true,
"parallel": true "parallel": true,
"timeout": 6666
}, },
"license": "SAP SAMPLE CODE LICENSE", "license": "SAP SAMPLE CODE LICENSE",
"private": true "private": true

View File

@@ -1,5 +1,5 @@
ID;subject;rating;reviewer;title;text subject;rating;reviewer;title;text
5de47328-2ad2-4449-bb8c-e4000586b687;201;5;bob;Intriguing;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. 201;5;bob;Intriguing;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
f77ea7a8-01c8-469d-bf9e-80988758a2ee;201;4;bob;Fascinating;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Id diam maecenas ultricies mi eget mauris pharetra et. Risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Pulvinar mattis nunc sed blandit libero. Facilisis magna etiam tempor orci eu. Nec sagittis aliquam malesuada bibendum arcu. Eu consequat ac felis donec. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Tortor posuere ac ut consequat semper viverra nam libero. Amet nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Scelerisque purus semper eget duis at tellus. Elementum tempus egestas sed sed risus pretium. Arcu dictum varius duis at. Amet luctus venenatis lectus magna fringilla urna. Eget velit aliquet sagittis id consectetur purus ut faucibus. Vitae auctor eu augue ut lectus. Fermentum iaculis eu non diam phasellus vestibulum. 201;4;bob;Fascinating;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Id diam maecenas ultricies mi eget mauris pharetra et. Risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Pulvinar mattis nunc sed blandit libero. Facilisis magna etiam tempor orci eu. Nec sagittis aliquam malesuada bibendum arcu. Eu consequat ac felis donec. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Tortor posuere ac ut consequat semper viverra nam libero. Amet nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Scelerisque purus semper eget duis at tellus. Elementum tempus egestas sed sed risus pretium. Arcu dictum varius duis at. Amet luctus venenatis lectus magna fringilla urna. Eget velit aliquet sagittis id consectetur purus ut faucibus. Vitae auctor eu augue ut lectus. Fermentum iaculis eu non diam phasellus vestibulum.
6b7ee8c9-0a18-4716-b333-eb95b7570f4e;207;2;bob;What is this?;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero justo laoreet sit amet cursus sit amet dictum. Nunc faucibus a pellentesque sit. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Enim nunc faucibus a pellentesque. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Cras ornare arcu dui vivamus. Facilisi etiam dignissim diam quis enim lobortis. Et molestie ac feugiat sed. Urna neque viverra justo nec ultrices dui. Ullamcorper a lacus vestibulum sed arcu non. Volutpat ac tincidunt vitae semper quis. Dignissim sodales ut eu sem. Feugiat in fermentum posuere urna nec. At augue eget arcu dictum varius. 207;2;bob;What is this?;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero justo laoreet sit amet cursus sit amet dictum. Nunc faucibus a pellentesque sit. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Enim nunc faucibus a pellentesque. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Cras ornare arcu dui vivamus. Facilisi etiam dignissim diam quis enim lobortis. Et molestie ac feugiat sed. Urna neque viverra justo nec ultrices dui. Ullamcorper a lacus vestibulum sed arcu non. Volutpat ac tincidunt vitae semper quis. Dignissim sodales ut eu sem. Feugiat in fermentum posuere urna nec. At augue eget arcu dictum varius.
10403669-7e56-4668-bfb0-2071bbc947f3;251;3;bob;It's dark...;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit tellus mauris a diam. Velit aliquet sagittis id consectetur purus ut. Viverra adipiscing at in tellus integer. Vitae elementum curabitur vitae nunc. Mattis ullamcorper velit sed ullamcorper morbi. Diam quis enim lobortis scelerisque. Auctor neque vitae tempus quam pellentesque nec nam aliquam. Semper auctor neque vitae tempus. Quis eleifend quam adipiscing vitae proin. Neque convallis a cras semper auctor neque vitae. Imperdiet massa tincidunt nunc pulvinar sapien et ligula. Sit amet consectetur adipiscing elit ut aliquam purus. Pretium quam vulputate dignissim suspendisse. 251;3;bob;It's dark...;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit tellus mauris a diam. Velit aliquet sagittis id consectetur purus ut. Viverra adipiscing at in tellus integer. Vitae elementum curabitur vitae nunc. Mattis ullamcorper velit sed ullamcorper morbi. Diam quis enim lobortis scelerisque. Auctor neque vitae tempus quam pellentesque nec nam aliquam. Semper auctor neque vitae tempus. Quis eleifend quam adipiscing vitae proin. Neque convallis a cras semper auctor neque vitae. Imperdiet massa tincidunt nunc pulvinar sapien et ligula. Sit amet consectetur adipiscing elit ut aliquam purus. Pretium quam vulputate dignissim suspendisse.
1 ID subject rating reviewer title text
2 5de47328-2ad2-4449-bb8c-e4000586b687 201 5 bob Intriguing Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
3 f77ea7a8-01c8-469d-bf9e-80988758a2ee 201 4 bob Fascinating Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Id diam maecenas ultricies mi eget mauris pharetra et. Risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Pulvinar mattis nunc sed blandit libero. Facilisis magna etiam tempor orci eu. Nec sagittis aliquam malesuada bibendum arcu. Eu consequat ac felis donec. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Tortor posuere ac ut consequat semper viverra nam libero. Amet nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Scelerisque purus semper eget duis at tellus. Elementum tempus egestas sed sed risus pretium. Arcu dictum varius duis at. Amet luctus venenatis lectus magna fringilla urna. Eget velit aliquet sagittis id consectetur purus ut faucibus. Vitae auctor eu augue ut lectus. Fermentum iaculis eu non diam phasellus vestibulum.
4 6b7ee8c9-0a18-4716-b333-eb95b7570f4e 207 2 bob What is this? Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero justo laoreet sit amet cursus sit amet dictum. Nunc faucibus a pellentesque sit. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Enim nunc faucibus a pellentesque. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Cras ornare arcu dui vivamus. Facilisi etiam dignissim diam quis enim lobortis. Et molestie ac feugiat sed. Urna neque viverra justo nec ultrices dui. Ullamcorper a lacus vestibulum sed arcu non. Volutpat ac tincidunt vitae semper quis. Dignissim sodales ut eu sem. Feugiat in fermentum posuere urna nec. At augue eget arcu dictum varius.
5 10403669-7e56-4668-bfb0-2071bbc947f3 251 3 bob It's dark... Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit tellus mauris a diam. Velit aliquet sagittis id consectetur purus ut. Viverra adipiscing at in tellus integer. Vitae elementum curabitur vitae nunc. Mattis ullamcorper velit sed ullamcorper morbi. Diam quis enim lobortis scelerisque. Auctor neque vitae tempus quam pellentesque nec nam aliquam. Semper auctor neque vitae tempus. Quis eleifend quam adipiscing vitae proin. Neque convallis a cras semper auctor neque vitae. Imperdiet massa tincidunt nunc pulvinar sapien et ligula. Sit amet consectetur adipiscing elit ut aliquam purus. Pretium quam vulputate dignissim suspendisse.

View File

@@ -1,7 +1,7 @@
# Overview of Samples # Overview of Samples
The following list gives an overview of the samples provided in subdirectories. The following list gives an overview of the samples provided in subdirectories.
Each sub directory essentially is an individual npm package arranged in an [all-in-one monorepo](all-in-one-monorepo) umbrella setup. Each sub directory essentially is an individual npm package arranged in an [all-in-one monorepo](#all-in-one-monorepo) umbrella setup.
## [@capire/hello-world](hello) ## [@capire/hello-world](hello)

View File

@@ -3,8 +3,9 @@ const cds = require('@sap/cds/lib')
describe('cap/samples - Custom Handlers', () => { describe('cap/samples - Custom Handlers', () => {
const { GET, POST, expect } = cds.test(__dirname+'/../bookshop') const { GET, POST, expect } = cds.test(__dirname+'/../bookshop')
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch beforeAll(()=>{
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases cds.User.default = cds.User.Privileged // hard core monkey patch
})
it('should reject out-of-stock orders', async () => { it('should reject out-of-stock orders', async () => {
await POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}` await POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`

View File

@@ -1,10 +1,12 @@
const cds = require('@sap/cds/lib') const cds = require('@sap/cds/lib')
describe('cap/samples - Fiori APIs - v2', () => { describe('cap/samples - Fiori APIs - v2', function() {
const { GET, expect, axios } = cds.test ('@capire/fiori', '--with-mocks') const { GET, expect, axios } = cds.test ('@capire/fiori', '--with-mocks')
axios.defaults.auth = { username: 'alice', password: 'admin' } axios.defaults.auth = { username: 'alice', password: 'admin' }
// if (this.timeout) this.timeout(1e6)
it('serves $metadata documents in v2', async () => { it('serves $metadata documents in v2', async () => {
const { headers, data } = await GET `/v2/browse/$metadata` const { headers, data } = await GET `/v2/browse/$metadata`
expect(headers).to.contain({ expect(headers).to.contain({

View File

@@ -1,8 +1,12 @@
const cds = require('@sap/cds/lib')
describe('cap/samples - Localized Data', () => { describe('cap/samples - Localized Data', () => {
const { GET, expect, cds } = require('@sap/cds/lib').test (__dirname) const { GET, expect } = cds.test (__dirname)
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch beforeAll(()=>{
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases cds.User.default = cds.User.Privileged // hard core monkey patch
})
it('serves localized $metadata documents', async () => { it('serves localized $metadata documents', async () => {
const { data } = await GET(`/browse/$metadata?sap-language=de`, { headers: { 'accept-language': 'de' }}) const { data } = await GET(`/browse/$metadata?sap-language=de`, { headers: { 'accept-language': 'de' }})

View File

@@ -1,16 +1,13 @@
const cds = require('@sap/cds/lib') const cds = require('@sap/cds/lib')
const {resolve} = require('path')
describe('cap/samples - Messaging', ()=>{ describe('cap/samples - Messaging', ()=>{
const { expect } = cds.test const { expect } = cds.test.in(__dirname,'..')
const _model = '@capire/reviews' const _model = '@capire/reviews'
const Reviews = 'sap.capire.reviews.Reviews' const Reviews = 'sap.capire.reviews.Reviews'
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch beforeAll(()=>{
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases cds.User.default = cds.User.Privileged // hard core monkey patch
})
beforeAll(() => { cds.root = resolve(__dirname, '..') })
afterAll(() => { cds.root = process.cwd() })
it ('should bootstrap sqlite in-memory db', async()=>{ it ('should bootstrap sqlite in-memory db', async()=>{
const db = await cds.deploy (_model) .to ('sqlite::memory:') const db = await cds.deploy (_model) .to ('sqlite::memory:')
@@ -35,6 +32,7 @@ describe('cap/samples - Messaging', ()=>{
it ('should add review', async ()=>{ it ('should add review', async ()=>{
const review = { subject: "201", title: "Captivating", rating: ++N } const review = { subject: "201", title: "Captivating", rating: ++N }
cds._debug = 1
const response = await srv.create ('Reviews') .entries (review) const response = await srv.create ('Reviews') .entries (review)
expect (response) .to.containSubset (review) expect (response) .to.containSubset (review)
}) })

View File

@@ -4,47 +4,6 @@ describe('cap/samples - Bookshop APIs', () => {
const { GET, expect, axios } = cds.test ('@capire/bookshop') const { GET, expect, axios } = cds.test ('@capire/bookshop')
axios.defaults.auth = { username: 'alice', password: 'admin' } axios.defaults.auth = { username: 'alice', password: 'admin' }
// Genres
const Drama = {
"name": "Drama",
"descr": null,
"ID": 11,
"parent_ID": 10
}
const Mystery = {
"name": "Mystery",
"descr": null,
"ID": 16,
"parent_ID": 10
}
const Fantasy = {
"name": "Fantasy",
"descr": null,
"ID": 13,
"parent_ID": 10
}
// Currencies
const GBP = {
"name": "British Pound",
"descr": null,
"code": "GBP",
"symbol": "£"
}
const USD = {
"name": "US Dollar",
"descr": null,
"code": "USD",
"symbol": "$"
}
const JPY = {
"name": "Yen",
"descr": null,
"code": "JPY",
"symbol": "¥"
}
it('serves $metadata documents in v4', async () => { it('serves $metadata documents in v4', async () => {
const { headers, status, data } = await GET `/browse/$metadata` const { headers, status, data } = await GET `/browse/$metadata`
expect(status).to.equal(200) expect(status).to.equal(200)
@@ -57,12 +16,15 @@ describe('cap/samples - Bookshop APIs', () => {
}) })
it('serves ListOfBooks?$expand=genre,currency', async () => { it('serves ListOfBooks?$expand=genre,currency', async () => {
const Mystery = { ID: 16, name: 'Mystery', descr: null, parent_ID: 10 }
const Romance = { ID: 15, name: 'Romance', descr: null, parent_ID: 10 }
const USD = { code: 'USD', name: 'US Dollar', descr: null, symbol: '$' }
const { data } = await GET `/browse/ListOfBooks ${{ const { data } = await GET `/browse/ListOfBooks ${{
params: { $search: 'Po', $select: `title,author`, $expand:`genre,currency` }, params: { $search: 'Po', $select: `title,author`, $expand:`genre,currency` },
}}` }}`
expect(data.value).to.eql([ expect(data.value).to.eql([
{ ID: 251, title: 'The Raven', author: 'Edgar Allen Poe', genre:Mystery, currency:USD }, { ID: 251, title: 'The Raven', author: 'Edgar Allen Poe', genre:Mystery, currency:USD },
{ ID: 252, title: 'Eleonora', author: 'Edgar Allen Poe', genre:Mystery, currency:USD }, { ID: 252, title: 'Eleonora', author: 'Edgar Allen Poe', genre:Romance, currency:USD },
]) ])
}) })
@@ -126,14 +88,10 @@ describe('cap/samples - Bookshop APIs', () => {
}) })
it('serves user info', async () => { it('serves user info', async () => {
{ const { data: alice } = await GET `/user/me`
const { data } = await GET (`/user/me`) expect(alice).to.containSubset({ id: 'alice', locale:'en' })
expect(data).to.containSubset({ id: 'alice', locale:'en', tenant: null }) const { data: joe } = await GET (`/user/me`, {auth: { username: 'joe' }})
} expect(joe).to.containSubset({ id: 'joe', locale:'en' })
{
const { data } = await GET (`/user/me`, {auth: { username: 'joe' }})
expect(data).to.containSubset({ id: 'joe', locale:'en', tenant: null })
}
}) })
}) })

View File

@@ -1,57 +0,0 @@
const cds = require('@sap/cds/lib')
const { fork } = require('child_process')
const { resolve } = require('path')
const verbose = process.env.CDS_TEST_VERBOSE
describe('cap/samples - Local NPM registry', () => {
const { expect } = cds.test
// ||true
let registry
let axios
const cwd = resolve(__dirname, '..')
before(async ()=> {
const env = Object.assign(process.env, {PORT:'0'})
const res = await exec (resolve(cwd, '.registry/server.js'), {cwd, stdio: 'pipe', env})
registry = res.cp
axios = require('axios').default.create ({ baseURL: res.url, validateStatus: (status)=>status<500 })
})
after(() => { registry.kill() })
for (const mod of ['bookshop', 'data-viewer', 'fiori','orders','reviews']) {
it(`should serve ${mod}`, async () => {
const resp = await axios.get(`/@capire/${mod}`)
expect(resp.data).to.containSubset({name: `@capire/${mod}`, versions:{}})
const versions = Object.values(resp.data.versions)
await axios.get(versions[0].dist.tarball)
})
}
it(`should return 404 for unknown packages`, async () => {
let resp = await axios.get(`/@capire/foo`)
expect(resp.status).to.equal(404)
resp = await axios.get(`/foo`)
expect(resp.status).to.equal(404)
})
})
function exec (script, opts) {
return new Promise((resolve, reject)=> {
const cp = fork (script, [], opts)
.on('error', err => reject(new Error(err)))
cp.stdout.on('data', chunk => {
if (verbose) console.log(chunk.toString())
if (chunk.toString().match(/listening.*(http:.*:\d+)/i)) {
resolve({cp, url:RegExp.$1})
}
})
cp.stderr.on('data', chunk => {
if (verbose) console.error(chunk.toString())
})
})
}