Compare commits
59 Commits
typed-samp
...
fiori-ext
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c91e6802ac | ||
|
|
e222645935 | ||
|
|
8676f7184d | ||
|
|
fdd1d86ac7 | ||
|
|
f50afdbd69 | ||
|
|
4bdad98165 | ||
|
|
a069f2d7de | ||
|
|
5289f1a8fd | ||
|
|
254362daa1 | ||
|
|
e419fda74e | ||
|
|
6bb51a8807 | ||
|
|
36b339a362 | ||
|
|
47655c5f48 | ||
|
|
aa5fda976a | ||
|
|
b97253f266 | ||
|
|
65379110e2 | ||
|
|
b8e04027d5 | ||
|
|
630838a7b4 | ||
|
|
09c2a40c7d | ||
|
|
88e527adba | ||
|
|
b0d74b0ea4 | ||
|
|
b4f6d5714c | ||
|
|
a17458b572 | ||
|
|
54db241e28 | ||
|
|
18f9f6c3f8 | ||
|
|
8bcc91aa23 | ||
|
|
780a81229d | ||
|
|
0a17fbcb30 | ||
|
|
7819ad0bad | ||
|
|
06b96d0726 | ||
|
|
07a6540477 | ||
|
|
69403de663 | ||
|
|
a5b54b53cf | ||
|
|
c6b8cbfd0e | ||
|
|
21be9d1bbf | ||
|
|
dff3dd4b89 | ||
|
|
66007e2952 | ||
|
|
1e78d115cc | ||
|
|
5bda368169 | ||
|
|
ea989d8496 | ||
|
|
692ea3ddd2 | ||
|
|
a0d5355f7d | ||
|
|
37538a368c | ||
|
|
662f93a995 | ||
|
|
76bed22f40 | ||
|
|
8ab66b4130 | ||
|
|
2a37ef8be1 | ||
|
|
de35537c04 | ||
|
|
e477879d71 | ||
|
|
38593baeb8 | ||
|
|
f0e8575e5e | ||
|
|
b52a9c3a21 | ||
|
|
88cb9c0069 | ||
|
|
42f7e1920b | ||
|
|
dea92b54d8 | ||
|
|
9a98ffd298 | ||
|
|
57cda61b02 | ||
|
|
8d26fc1f45 | ||
|
|
5c4247b14c |
10
.eslintrc
10
.eslintrc
@@ -1,15 +1,15 @@
|
||||
{
|
||||
"extends": "eslint:recommended",
|
||||
"extends": [
|
||||
"eslint:recommended",
|
||||
"plugin:@sap/cds/recommended"
|
||||
],
|
||||
"env": {
|
||||
"browser": true,
|
||||
"es2022": true,
|
||||
"node": true,
|
||||
"es6": true,
|
||||
"jest": true,
|
||||
"mocha": true
|
||||
},
|
||||
"parserOptions": {
|
||||
"ecmaVersion": 2020
|
||||
},
|
||||
"globals": {
|
||||
"SELECT": true,
|
||||
"INSERT": true,
|
||||
|
||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -18,3 +18,5 @@ reviews/msg-box
|
||||
reviews/db/test.db
|
||||
|
||||
*.openapi3.json
|
||||
*.sqlite
|
||||
*.db
|
||||
|
||||
@@ -1,27 +1,34 @@
|
||||
const { exec } = require ('child_process')
|
||||
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 { PORT=4444 } = process.env
|
||||
const [,,port=PORT,scope='@capire'] = process.argv
|
||||
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', (req,res,next) => {
|
||||
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 [, pkg ] = /^\w+-(.+)/.exec(pkgFull)
|
||||
const scope = '@'+pkgFull.substring(0, pkgFull.indexOf('-'))
|
||||
const pkg = pkgFull.substring(pkgFull.indexOf('-')+1)
|
||||
fs.lstat(tarball,(err => {
|
||||
if (err) console.debug (`npm pack ../${pkg}`)
|
||||
if (err) exec(`npm pack ../${pkg}`,{cwd},next)
|
||||
else next()
|
||||
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)
|
||||
@@ -31,14 +38,20 @@ app.use('/-/:tarball', (req,res,next) => {
|
||||
|
||||
app.use('/-', express.static(__dirname))
|
||||
|
||||
app.get('/*', (req,res)=>{
|
||||
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 package = require (`${scpe}/${pkg}/package.json`)
|
||||
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({
|
||||
@@ -53,6 +66,8 @@ app.get('/*', (req,res)=>{
|
||||
"dist": {
|
||||
"tarball": `${server.url}/-/${tarball}`
|
||||
},
|
||||
dependencies: package.dependencies,
|
||||
devDependencies: package.devDependencies
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -64,15 +79,32 @@ app.get('/*', (req,res)=>{
|
||||
|
||||
const server = app.listen(port, ()=>{
|
||||
const url = server.url = `http://localhost:${server.address().port}`
|
||||
console.log (`npm set ${scope}:registry=${url}`)
|
||||
exec(`npm set ${scope}:registry=${url}`)
|
||||
console.log (`${scope} registry listening on ${url}`)
|
||||
|
||||
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()
|
||||
exec(`npm conf rm "${scope}:registry"`, ()=> { process.exit() })
|
||||
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)
|
||||
|
||||
12
.vscode/settings.json
vendored
12
.vscode/settings.json
vendored
@@ -14,5 +14,13 @@
|
||||
"**/odata-v4/okra/**"
|
||||
]
|
||||
},
|
||||
"mochaExplorer.parallel": true
|
||||
}
|
||||
"mochaExplorer.parallel": true,
|
||||
"eslint.validate": [
|
||||
"cds",
|
||||
"csn",
|
||||
"csv",
|
||||
"csv (semicolon)",
|
||||
"tsv",
|
||||
"tab"
|
||||
]
|
||||
}
|
||||
@@ -56,7 +56,7 @@ const books = Vue.createApp ({
|
||||
} catch (err) { books.user = { id: err.message } }
|
||||
},
|
||||
}
|
||||
}).mount("#app")
|
||||
}).mount('#app')
|
||||
|
||||
books.getUserInfo()
|
||||
books.fetch() // initially fill list of books
|
||||
@@ -65,3 +65,25 @@ document.addEventListener('keydown', (event) => {
|
||||
// hide user info on request
|
||||
if (event.key === 'u') books.user = undefined
|
||||
})
|
||||
|
||||
axios.interceptors.request.use(csrfToken)
|
||||
function csrfToken (request) {
|
||||
if (request.method === 'head' || request.method === 'get') return request
|
||||
if ('csrfToken' in document) {
|
||||
request.headers['x-csrf-token'] = document.csrfToken
|
||||
return request
|
||||
}
|
||||
return fetchToken().then(token => {
|
||||
document.csrfToken = token
|
||||
request.headers['x-csrf-token'] = document.csrfToken
|
||||
return request
|
||||
}).catch(_ => {
|
||||
document.csrfToken = null // set mark to not try again
|
||||
return request
|
||||
})
|
||||
|
||||
function fetchToken() {
|
||||
return axios.get('/', { headers: { 'x-csrf-token': 'fetch' } })
|
||||
.then(res => res.headers['x-csrf-token'])
|
||||
}
|
||||
}
|
||||
@@ -2,3 +2,4 @@ namespace sap.capire.bookshop; //> important for reflection
|
||||
using from './db/schema';
|
||||
using from './srv/cat-service';
|
||||
using from './srv/admin-service';
|
||||
using from './srv/user-service';
|
||||
|
||||
@@ -21,9 +21,7 @@
|
||||
},
|
||||
"cds": {
|
||||
"requires": {
|
||||
"db": {
|
||||
"kind": "sql"
|
||||
}
|
||||
"db": "sql"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
const cds = require('@sap/cds')
|
||||
const cds = require('@sap/cds/lib')
|
||||
|
||||
module.exports = cds.service.impl (function(){
|
||||
module.exports = class AdminService extends cds.ApplicationService { init(){
|
||||
this.before ('NEW','Authors', genid)
|
||||
this.before ('NEW','Books', genid)
|
||||
})
|
||||
return super.init()
|
||||
}}
|
||||
|
||||
/** Generate primary keys for target entity in request */
|
||||
async function genid (req) {
|
||||
|
||||
@@ -2,7 +2,8 @@ const cds = require('@sap/cds')
|
||||
|
||||
class CatalogService extends cds.ApplicationService { init(){
|
||||
|
||||
const { Books } = this.entities ('sap.capire.bookshop')
|
||||
const { Books } = cds.entities ('sap.capire.bookshop')
|
||||
const { ListOfBooks } = this.entities
|
||||
|
||||
// Reduce stock of ordered books if available stock suffices
|
||||
this.on ('submitOrder', async req => {
|
||||
@@ -18,7 +19,7 @@ class CatalogService extends cds.ApplicationService { init(){
|
||||
})
|
||||
|
||||
// Add some discount for overstocked books
|
||||
this.after ('READ','ListOfBooks', each => {
|
||||
this.after ('READ', ListOfBooks, each => {
|
||||
if (each.stock > 111) each.title += ` -- 11% discount!`
|
||||
})
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ service UserService {
|
||||
/**
|
||||
* The current user
|
||||
*/
|
||||
@odata.singleton entity me {
|
||||
@odata.singleton entity me @cds.persistence.skip {
|
||||
id : String; // user id
|
||||
locale : String;
|
||||
tenant : String;
|
||||
|
||||
@@ -19,4 +19,4 @@ module.exports = cds.server
|
||||
|
||||
// For didactic reasons in capire
|
||||
const { ReviewsService, OrdersService } = cds.requires
|
||||
if (!ReviewsService.credentials && !OrdersService.credentials) cds.requires.messaging = false
|
||||
if (!ReviewsService?.credentials && !OrdersService?.credentials) cds.requires.messaging = false
|
||||
|
||||
@@ -2,12 +2,12 @@
|
||||
* Exposes data + entity metadata
|
||||
*/
|
||||
@requires:'authenticated-user'
|
||||
service DataService @( path:'-data' ) {
|
||||
@odata service DataService @( path:'-data' ) {
|
||||
|
||||
/**
|
||||
* Metadata like name and columns/elements
|
||||
*/
|
||||
entity Entities {
|
||||
entity Entities @cds.persistence.skip {
|
||||
key name : String;
|
||||
columns: Composition of many {
|
||||
name : String;
|
||||
@@ -19,7 +19,7 @@ service DataService @( path:'-data' ) {
|
||||
/**
|
||||
* The actual data, organized by column name
|
||||
*/
|
||||
entity Data {
|
||||
entity Data @cds.persistence.skip {
|
||||
record : array of {
|
||||
column : String;
|
||||
data : String;
|
||||
|
||||
11
fiori-ext/.vscode/settings.json
vendored
Normal file
11
fiori-ext/.vscode/settings.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"eslint.validate": [
|
||||
"cds",
|
||||
"csn",
|
||||
"csv",
|
||||
"csv",
|
||||
"csv (semicolon)",
|
||||
"tsv",
|
||||
"tab"
|
||||
]
|
||||
}
|
||||
21
fiori-ext/README.md
Normal file
21
fiori-ext/README.md
Normal file
@@ -0,0 +1,21 @@
|
||||
# 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/.
|
||||
25
fiori-ext/app/extension.cds
Normal file
25
fiori-ext/app/extension.cds
Normal file
@@ -0,0 +1,25 @@
|
||||
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 },
|
||||
...
|
||||
];
|
||||
49
fiori-ext/package.json
Normal file
49
fiori-ext/package.json
Normal file
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
3
fiori-ext/test/data/sap.capire.orders-Orders.csv
Normal file
3
fiori-ext/test/data/sap.capire.orders-Orders.csv
Normal file
@@ -0,0 +1,3 @@
|
||||
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
|
||||
|
4
fiori-ext/test/data/x_bookshop.extension-SalesRegion.csv
Normal file
4
fiori-ext/test/data/x_bookshop.extension-SalesRegion.csv
Normal file
@@ -0,0 +1,4 @@
|
||||
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
|
||||
|
12
fiori/app/_router/package.json
Normal file
12
fiori/app/_router/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "approuter",
|
||||
"dependencies": {
|
||||
"@sap/approuter": "^11.0.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node node_modules/@sap/approuter/approuter.js"
|
||||
}
|
||||
}
|
||||
23
fiori/app/_router/xs-app.json
Normal file
23
fiori/app/_router/xs-app.json
Normal file
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
]
|
||||
}
|
||||
24
fiori/app/xs-app.json
Normal file
24
fiori/app/xs-app.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"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
|
||||
}
|
||||
]
|
||||
}
|
||||
5
fiori/db/data/sap.capire.bookshop-Books_texts.csv
Normal file
5
fiori/db/data/sap.capire.bookshop-Books_texts.csv
Normal file
@@ -0,0 +1,5 @@
|
||||
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ë (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.
|
||||
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.
|
||||
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.
|
||||
|
@@ -2,9 +2,14 @@
|
||||
// Add Author.age and .lifetime with a DB-specific function
|
||||
//
|
||||
|
||||
using { AdminService } from '@capire/bookshop';
|
||||
using { AdminService, sap.common } from '@capire/bookshop';
|
||||
|
||||
extend projection AdminService.Authors with {
|
||||
YEARS_BETWEEN(dateOfBirth, dateOfDeath) as age: Integer,
|
||||
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;
|
||||
|
||||
97
fiori/mta.yaml
Normal file
97
fiori/mta.yaml
Normal file
@@ -0,0 +1,97 @@
|
||||
_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
|
||||
@@ -4,19 +4,25 @@
|
||||
"dependencies": {
|
||||
"@capire/bookstore": "*",
|
||||
"@sap/cds": ">=5",
|
||||
"@sap/cds-mtxs": "^1",
|
||||
"@sap/cds-odata-v2-adapter-proxy": "^1.9.0",
|
||||
"@sap/xssec": "^3",
|
||||
"express": "^4.17.1",
|
||||
"hdb": "^0.19.5",
|
||||
"passport": ">=0.4.1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cds run --in-memory?",
|
||||
"watch": "cds watch"
|
||||
},
|
||||
"engines": {
|
||||
"node": "^16"
|
||||
},
|
||||
"cds": {
|
||||
"features": {
|
||||
"deploy_data_onconflict": "replace"
|
||||
},
|
||||
"requires": {
|
||||
"auth": {
|
||||
"kind": "dummy-auth"
|
||||
},
|
||||
"ReviewsService": {
|
||||
"kind": "odata",
|
||||
"model": "@capire/reviews"
|
||||
@@ -37,7 +43,7 @@
|
||||
}
|
||||
},
|
||||
"db": {
|
||||
"kind": "sql"
|
||||
"kind": "sql-mt"
|
||||
},
|
||||
"db-ext": {
|
||||
"[development]": {
|
||||
@@ -47,9 +53,40 @@
|
||||
"model": "db/hana"
|
||||
}
|
||||
},
|
||||
"hana": {
|
||||
"deploy-format": "hdbtable"
|
||||
"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": {
|
||||
"kind": "cloudfoundry"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -5,4 +5,11 @@ const proxy = require('@sap/cds-odata-v2-adapter-proxy')
|
||||
const proxyOpts = global.it ? { target:'auto' } : {} // for tests, set 'auto' to detect port dynamically
|
||||
cds.on('bootstrap', app => app.use(proxy(proxyOpts)))
|
||||
|
||||
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
|
||||
}
|
||||
1
fiori/srv/index.cds
Normal file
1
fiori/srv/index.cds
Normal file
@@ -0,0 +1 @@
|
||||
using from '@capire/bookstore';
|
||||
51
fiori/xs-security.json
Normal file
51
fiori/xs-security.json
Normal file
@@ -0,0 +1,51 @@
|
||||
{
|
||||
"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"
|
||||
]
|
||||
}
|
||||
@@ -12,23 +12,8 @@
|
||||
"devDependencies": {
|
||||
"@types/jest": "*",
|
||||
"@types/node": "*",
|
||||
"ts-jest": "^27.0.2",
|
||||
"typescript": "^4.3.5"
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "node",
|
||||
"preset": "ts-jest",
|
||||
"globals": {
|
||||
"ts-jest": {
|
||||
"diagnostics": {
|
||||
"_comment": "see https://githubmemory.com/repo/kulshekhar/ts-jest/issues/2722",
|
||||
"ignoreCodes": [
|
||||
151001
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"eslintConfig": {
|
||||
"extends": "eslint:recommended",
|
||||
"env": {
|
||||
|
||||
13
hello/test/hello-world-test.js
Normal file
13
hello/test/hello-world-test.js
Normal file
@@ -0,0 +1,13 @@
|
||||
const cds = require ('@sap/cds')
|
||||
describe('Hello world!', () => {
|
||||
|
||||
beforeAll (()=> process.env.CDS_TYPESCRIPT = true)
|
||||
afterAll (()=> delete process.env.CDS_TYPESCRIPT)
|
||||
const {GET} = cds.test.in(__dirname,'../srv').run('serve', 'world.cds')
|
||||
|
||||
it('should say hello with class impl', async () => {
|
||||
const {data} = await GET`/say/hello(to='world')`
|
||||
expect(data.value).toMatch(/Hello world.*typescript.*/i)
|
||||
})
|
||||
|
||||
})
|
||||
@@ -1,15 +0,0 @@
|
||||
process.env.CDS_TYPESCRIPT = 'true';
|
||||
import * as cds from '@sap/cds';
|
||||
|
||||
//@ts-ignore
|
||||
const {GET} = cds.test.in(__dirname,'../srv').run('serve', 'world.cds');
|
||||
|
||||
describe('Hello world!', () => {
|
||||
afterAll(() => { delete process.env.CDS_TYPESCRIPT; });
|
||||
|
||||
it('should say hello with class impl from a typescript file', async () => {
|
||||
const {data} = await GET`/say/hello(to='world')`
|
||||
expect(data.value).toMatch(/Hello world.*typescript.*/i)
|
||||
})
|
||||
|
||||
})
|
||||
8451
package-lock.json
generated
8451
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,5 @@
|
||||
subject;rating;reviewer;title;text
|
||||
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;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.
|
||||
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.
|
||||
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.
|
||||
ID;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.
|
||||
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.
|
||||
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.
|
||||
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.
|
||||
|
@@ -26,10 +26,10 @@ service ReviewsService {
|
||||
|
||||
// Access control restrictions
|
||||
annotate ReviewsService.Reviews with @restrict:[
|
||||
{ grant:'GET', to:'any' }, // everybody can read reviews
|
||||
{ grant:'READ', to:'any' }, // everybody can read reviews
|
||||
{ grant:'CREATE', to:'authenticated-user' }, // users must login to add reviews
|
||||
{ grant:'UPDATE', to:'authenticated-user', where:'reviewer=$user' },
|
||||
{ grant:'DELET', to:'admin' },
|
||||
{ grant:'DELETE', to:'admin' },
|
||||
];
|
||||
|
||||
annotate ReviewsService with @restrict:[
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { expect } = cds.test
|
||||
const { cdr } = cds.ql
|
||||
const Foo = { name: 'Foo' }
|
||||
const Books = { name: 'capire.bookshop.Books' }
|
||||
|
||||
|
||||
const STAR = cdr ? '*' : { ref: ['*'] }
|
||||
const skip = {to:{eql:()=>skip}}
|
||||
const srv = new cds.Service
|
||||
let cqn
|
||||
|
||||
expect.plain = (cqn) => !cqn.SELECT.one && !cqn.SELECT.distinct ? expect(cqn) : skip
|
||||
expect.one = (cqn) => !cqn.SELECT.distinct ? expect(cqn) : skip
|
||||
|
||||
describe('cds.ql → cqn', () => {
|
||||
//
|
||||
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { expect } = cds.test
|
||||
const { cdr } = cds.ql
|
||||
const Foo = { name: 'Foo' }
|
||||
const Books = { name: 'capire.bookshop.Books' }
|
||||
|
||||
const STAR = cdr ? '*' : { ref: ['*'] }
|
||||
const skip = {to:{eql:()=>skip}}
|
||||
const srv = new cds.Service
|
||||
let cqn
|
||||
|
||||
expect.plain = (cqn) => !cqn.SELECT.one && !cqn.SELECT.distinct ? expect(cqn) : skip
|
||||
expect.one = (cqn) => !cqn.SELECT.distinct ? expect(cqn) : skip
|
||||
|
||||
describe.each(['SELECT', 'SELECT one', 'SELECT distinct'])(`%s...`, (each) => {
|
||||
|
||||
@@ -413,18 +411,67 @@ describe('cds.ql → cqn', () => {
|
||||
]
|
||||
}})
|
||||
|
||||
expect (
|
||||
SELECT.from(Foo).where({x:1,or:{y:2}})
|
||||
).to.eql (
|
||||
CQL`SELECT from Foo where x=1 or y=2`
|
||||
).to.eql ({ SELECT: {
|
||||
from: {ref:['Foo']},
|
||||
where: [
|
||||
{ref:['x']}, '=', {val:1},
|
||||
'or',
|
||||
{ref:['y']}, '=', {val:2}
|
||||
]
|
||||
}})
|
||||
const ql_with_groups_fix = !!cds.ql.Query.prototype.flat
|
||||
if (ql_with_groups_fix) {
|
||||
|
||||
expect (
|
||||
SELECT.from(Foo).where({x:1}).or({y:2}).and({z:3})
|
||||
).to.eql ({ SELECT: {
|
||||
from: {ref:['Foo']},
|
||||
where: [
|
||||
{ref:['x']}, '=', {val:1},
|
||||
'or',
|
||||
{ref:['y']}, '=', {val:2},
|
||||
'and',
|
||||
{ref:['z']}, '=', {val:3},
|
||||
]
|
||||
}})
|
||||
|
||||
expect (
|
||||
SELECT.from(Foo).where({x:1,or:{y:2}}).and({z:3})
|
||||
).to.eql ({ SELECT: {
|
||||
from: {ref:['Foo']},
|
||||
where: [
|
||||
{xpr:[
|
||||
{ref:['x']}, '=', {val:1},
|
||||
'or',
|
||||
{ref:['y']}, '=', {val:2},
|
||||
]},
|
||||
'and',
|
||||
{ref:['z']}, '=', {val:3},
|
||||
]
|
||||
}})
|
||||
|
||||
expect (
|
||||
SELECT.from(Foo).where({a:1}).or({x:1,or:{y:2}}).and({z:3})
|
||||
).to.eql ({ SELECT: {
|
||||
from: {ref:['Foo']},
|
||||
where: [
|
||||
{ref:['a']}, '=', {val:1},
|
||||
'or',
|
||||
{xpr:[
|
||||
{ref:['x']}, '=', {val:1},
|
||||
'or',
|
||||
{ref:['y']}, '=', {val:2},
|
||||
]},
|
||||
'and',
|
||||
{ref:['z']}, '=', {val:3},
|
||||
]
|
||||
}})
|
||||
|
||||
expect (
|
||||
{ SELECT: SELECT.from(Foo).where({x:1,or:{y:2}}).SELECT }
|
||||
).to.eql ({ SELECT: {
|
||||
from: {ref:['Foo']},
|
||||
where: [
|
||||
{ref:['x']}, '=', {val:1},
|
||||
'or',
|
||||
{ref:['y']}, '=', {val:2},
|
||||
]
|
||||
}})
|
||||
|
||||
}
|
||||
|
||||
|
||||
expect (
|
||||
SELECT.from(Foo).where({x:1,and:{y:2}}).or({z:3})
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { expect } = cds.test ('@capire/bookshop')
|
||||
|
||||
describe('cap/samples - Consuming Services locally', () => {
|
||||
//
|
||||
|
||||
const { expect } = cds.test ('@capire/bookshop')
|
||||
|
||||
it('bootstrapped the database successfully', ()=>{
|
||||
const { AdminService } = cds.services
|
||||
const { Authors } = AdminService.entities
|
||||
@@ -14,7 +15,7 @@ describe('cap/samples - Consuming Services locally', () => {
|
||||
const AdminService = await cds.connect.to('AdminService')
|
||||
const { Authors } = AdminService.entities
|
||||
expect (await SELECT.from(Authors))
|
||||
.to.eql(await SELECT.from('Authors'))
|
||||
// .to.eql(await SELECT.from('Authors'))
|
||||
.to.eql(await AdminService.read(Authors))
|
||||
.to.eql(await AdminService.read('Authors'))
|
||||
.to.eql(await AdminService.run(SELECT.from(Authors)))
|
||||
|
||||
@@ -1,10 +1,11 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { GET, POST, expect } = cds.test(__dirname+'/../bookshop')
|
||||
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
|
||||
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
|
||||
|
||||
describe('cap/samples - Custom Handlers', () => {
|
||||
|
||||
const { GET, POST, expect } = cds.test(__dirname+'/../bookshop')
|
||||
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
|
||||
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
|
||||
|
||||
it('should reject out-of-stock orders', async () => {
|
||||
await POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`
|
||||
await POST `/browse/submitOrder ${{ book: 201, quantity: 5 }}`
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { GET, expect, axios } = cds.test ('@capire/fiori', '--with-mocks')
|
||||
axios.defaults.auth = { username: 'alice', password: 'admin' }
|
||||
|
||||
describe('cap/samples - Fiori APIs - v2', () => {
|
||||
|
||||
const { GET, expect, axios } = cds.test ('@capire/fiori', '--with-mocks')
|
||||
axios.defaults.auth = { username: 'alice', password: 'admin' }
|
||||
|
||||
it('serves $metadata documents in v2', async () => {
|
||||
const { headers, data } = await GET `/v2/browse/$metadata`
|
||||
expect(headers).to.contain({
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { GET, expect } = cds.test (__dirname+'/../hello')
|
||||
|
||||
describe('cap/samples - Hello world!', () => {
|
||||
|
||||
const { GET, expect } = cds.test (__dirname+'/../hello')
|
||||
|
||||
it('should say hello with class impl', async () => {
|
||||
const {data} = await GET `/say/hello(to='world')`
|
||||
expect(data.value).to.eql('Hello world!')
|
||||
|
||||
@@ -1,20 +1,18 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
const {expect} = cds.test
|
||||
|
||||
// should become cds.compile(...) when cds5 is released
|
||||
const model = cds.compile.to.csn (`
|
||||
entity Categories {
|
||||
key ID : Integer;
|
||||
name : String;
|
||||
children : Composition of many Categories on children.parent = $self;
|
||||
parent : Association to Categories;
|
||||
}
|
||||
`)
|
||||
const {Categories:Cats} = model.definitions
|
||||
|
||||
|
||||
describe('cap/samples - Hierarchical Data', ()=>{
|
||||
|
||||
const model = CDL`
|
||||
entity Categories {
|
||||
key ID : Integer;
|
||||
name : String;
|
||||
children : Composition of many Categories on children.parent = $self;
|
||||
parent : Association to Categories;
|
||||
}
|
||||
`
|
||||
const {Categories:Cats} = model.definitions
|
||||
const {expect} = cds.test
|
||||
|
||||
before ('bootstrap sqlite in-memory db...', async()=>{
|
||||
await cds.deploy (model) .to ('sqlite::memory:')
|
||||
expect (cds.db) .to.exist
|
||||
|
||||
@@ -1,23 +1,12 @@
|
||||
const { GET, expect, cds } = require('@sap/cds/lib').test (__dirname)
|
||||
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
|
||||
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
|
||||
|
||||
describe('cap/samples - Localized Data', () => {
|
||||
|
||||
it('serves localized $metadata documents', async () => {
|
||||
const { data } = await GET`/browse/$metadata?sap-language=de`
|
||||
expect(data).to.contain('<Annotation Term="Common.Label" String="Währung"/>')
|
||||
})
|
||||
const { GET, expect, cds } = require('@sap/cds/lib').test (__dirname)
|
||||
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
|
||||
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
|
||||
|
||||
it('supports sap-language param', async () => {
|
||||
const { data } = await GET(`/browse/Books?$select=title,author` + '&sap-language=de')
|
||||
expect(data.value).to.containSubset([
|
||||
{ title: 'Sturmhöhe', author: 'Emily Brontë' },
|
||||
{ title: 'Jane Eyre', author: 'Charlotte Brontë' },
|
||||
{ title: 'The Raven', author: 'Edgar Allen Poe' },
|
||||
{ title: 'Eleonora', author: 'Edgar Allen Poe' },
|
||||
{ title: 'Catweazle', author: 'Richard Carpenter' },
|
||||
])
|
||||
it('serves localized $metadata documents', async () => {
|
||||
const { data } = await GET(`/browse/$metadata?sap-language=de`, { headers: { 'accept-language': 'de' }})
|
||||
expect(data).to.contain('<Annotation Term="Common.Label" String="Währung"/>')
|
||||
})
|
||||
|
||||
it('supports accept-language header', async () => {
|
||||
|
||||
@@ -1,14 +1,20 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { expect } = cds.test
|
||||
const _model = '@capire/reviews'
|
||||
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
|
||||
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
|
||||
const {resolve} = require('path')
|
||||
|
||||
describe('cap/samples - Messaging', ()=>{
|
||||
|
||||
const { expect } = cds.test
|
||||
const _model = '@capire/reviews'
|
||||
const Reviews = 'sap.capire.reviews.Reviews'
|
||||
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
|
||||
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
|
||||
|
||||
beforeAll(() => { cds.root = resolve(__dirname, '..') })
|
||||
afterAll(() => { cds.root = process.cwd() })
|
||||
|
||||
it ('should bootstrap sqlite in-memory db', async()=>{
|
||||
const db = await cds.deploy (_model) .to ('sqlite::memory:')
|
||||
await db.delete('Reviews')
|
||||
await db.delete(Reviews)
|
||||
expect (db.model) .not.undefined
|
||||
})
|
||||
|
||||
|
||||
@@ -1,8 +1,8 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { GET, expect, axios } = cds.test ('@capire/bookshop')
|
||||
axios.defaults.auth = { username: 'alice', password: 'admin' }
|
||||
|
||||
describe('cap/samples - Bookshop APIs', () => {
|
||||
const { GET, expect, axios } = cds.test ('@capire/bookshop')
|
||||
axios.defaults.auth = { username: 'alice', password: 'admin' }
|
||||
|
||||
// Genres
|
||||
const Drama = {
|
||||
|
||||
@@ -1,12 +1,14 @@
|
||||
|
||||
const cds = require('@sap/cds/lib')
|
||||
const { expect } = cds.test
|
||||
const { fork } = require('child_process')
|
||||
const { resolve } = require('path')
|
||||
const verbose = process.env.CDS_TEST_VERBOSE
|
||||
// ||true
|
||||
|
||||
describe('cap/samples - Local NPM registry', () => {
|
||||
|
||||
const { expect } = cds.test
|
||||
// ||true
|
||||
|
||||
let registry
|
||||
let axios
|
||||
const cwd = resolve(__dirname, '..')
|
||||
|
||||
Reference in New Issue
Block a user