Compare commits
38 Commits
preview
...
adding-que
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ae0cfc8c1e | ||
|
|
5c5afd2790 | ||
|
|
74ee6f34e4 | ||
|
|
9a9b7aeb86 | ||
|
|
cfc01bbc4f | ||
|
|
aaac6cc678 | ||
|
|
99fdf0c038 | ||
|
|
6ccecfecae | ||
|
|
e5f0a7ef73 | ||
|
|
de54f70570 | ||
|
|
e1c6118cb4 | ||
|
|
a3e4865d97 | ||
|
|
641df50422 | ||
|
|
0f026ed56c | ||
|
|
57a3c5f178 | ||
|
|
522ec8e071 | ||
|
|
7b1c3d8b3a | ||
|
|
5ba69b5021 | ||
|
|
fd796b54ef | ||
|
|
660344b623 | ||
|
|
d20c29a758 | ||
|
|
604cc0514c | ||
|
|
ff351455dd | ||
|
|
8f74bd32a9 | ||
|
|
c181afe8c6 | ||
|
|
9ade3e6b6a | ||
|
|
6f9737ae38 | ||
|
|
0a552b4346 | ||
|
|
6367081e9d | ||
|
|
3e73683d99 | ||
|
|
2b345ca447 | ||
|
|
20593f2fa2 | ||
|
|
ca45aa1cf7 | ||
|
|
e408836c2a | ||
|
|
3000a9e2df | ||
|
|
b83236de2a | ||
|
|
46b3b8aaec | ||
|
|
59f5c82684 |
10
.github/ISSUE_TEMPLATE/question--feedback-or-bug-.md
vendored
Normal file
10
.github/ISSUE_TEMPLATE/question--feedback-or-bug-.md
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
---
|
||||
name: Question, feedback or bug?
|
||||
about: Use our community!
|
||||
title: ''
|
||||
labels: ''
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
Please use our community on https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce
|
||||
28
.github/workflows/node.js.yml
vendored
Normal file
28
.github/workflows/node.js.yml
vendored
Normal file
@@ -0,0 +1,28 @@
|
||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
||||
|
||||
name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [10.x, 12.16.2]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v2
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v1
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
- run: npm install
|
||||
- run: npm test
|
||||
1
.mocharc.yml
Normal file
1
.mocharc.yml
Normal file
@@ -0,0 +1 @@
|
||||
parallel: true
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -4,7 +4,7 @@
|
||||
|
||||
// List of extensions which should be recommended for users of this workspace.
|
||||
"recommendations": [
|
||||
// "CDS Editor !",
|
||||
"SAPSE.vscode-cds",
|
||||
"dbaeumer.vscode-eslint",
|
||||
"esbenp.prettier-vscode",
|
||||
"mechatroner.rainbow-csv",
|
||||
|
||||
18
.vscode/launch.json
vendored
18
.vscode/launch.json
vendored
@@ -6,24 +6,16 @@
|
||||
"configurations": [
|
||||
{
|
||||
"name": "bookshop",
|
||||
"cwd": "${workspaceFolder}/bookshop",
|
||||
"command": "cds watch bookshop",
|
||||
"request": "launch",
|
||||
"type": "node",
|
||||
"runtimeExecutable": "npx",
|
||||
"runtimeArgs": ["-n"],
|
||||
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"],
|
||||
"console": "integratedTerminal",
|
||||
"type": "node-terminal",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
},
|
||||
{
|
||||
"name": "Fiori App",
|
||||
"cwd": "${workspaceFolder}/fiori",
|
||||
"name": "Fiori app",
|
||||
"command": "cds watch fiori",
|
||||
"request": "launch",
|
||||
"type": "node",
|
||||
"runtimeExecutable": "npx",
|
||||
"runtimeArgs": ["-n"],
|
||||
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"],
|
||||
"console": "integratedTerminal",
|
||||
"type": "node-terminal",
|
||||
"skipFiles": ["<node_internals>/**"]
|
||||
}
|
||||
],
|
||||
|
||||
11
README.md
11
README.md
@@ -2,6 +2,7 @@
|
||||
|
||||
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo). → See [**Overview** of contained samples](samples.md)
|
||||
|
||||

|
||||
|
||||
### Preliminaries
|
||||
|
||||
@@ -39,19 +40,19 @@ After that open this link in your browser: [http://localhost:4004](http://localh
|
||||
|
||||
### Testing
|
||||
|
||||
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), e.g.:
|
||||
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example:
|
||||
```sh
|
||||
npx jest
|
||||
```
|
||||
> While mocha is a bit smaller and faster, jest runs tests in parallel and isolation which allows to run all tests.
|
||||
> While mocha is a bit smaller and faster, jest runs tests in parallel and isolation, which allows to run all tests.
|
||||
|
||||
|
||||
## Get Support
|
||||
|
||||
Check out the cap docs at [https://cap.cloud.sap](https://cap.cloud.sap). <br>
|
||||
In case you find a bug or need support, please [open an issue in here](https://github.com/SAP-samples/cloud-cap-samples/issues/new).
|
||||
Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br>
|
||||
In case you have a question, find a bug, or otherwise need support, please use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce).
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](/LICENSE) file.
|
||||
Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](/LICENSE) file.
|
||||
|
||||
@@ -11,6 +11,6 @@ ID;parent_ID;name
|
||||
19;10;Fairy Tale
|
||||
20;;Non-Fiction
|
||||
21;20;Biography
|
||||
22;20;Autobiography
|
||||
22;21;Autobiography
|
||||
23;20;Essay
|
||||
24;20;Speech
|
||||
|
||||
|
@@ -2,7 +2,7 @@ code;symbol;name;descr;numcode;minor;exponent
|
||||
EUR;€;Euro;European Euro;978;Cent;2
|
||||
USD;$;US Dollar;United States Dollar;840;Cent;2
|
||||
CAD;$;Canadian Dollar;Canadian Dollar;124;Cent;2
|
||||
AUD;$;Australian Dollar;Canadian Dollar;036;Cent;2
|
||||
AUD;$;Australian Dollar;Australian Dollar;036;Cent;2
|
||||
GBP;£;British Pound;Great Britain Pound;826;Penny;2
|
||||
ILS;₪;Shekel;Israeli New Shekel;376;Agorat;2
|
||||
INR;₹;Rupee;Indian Rupee;356;Paise;2
|
||||
|
||||
|
@@ -7,7 +7,19 @@ AuthorID = Author ID
|
||||
Stock = Stock
|
||||
Name = Name
|
||||
AuthorName = Author's Name
|
||||
DateOfBirth = Date of Birth
|
||||
DateOfDeath = Date of Death
|
||||
PlaceOfBirth = Place of Birth
|
||||
PlaceOfDeath = Place of Death
|
||||
Authors = Authors
|
||||
Order = Order
|
||||
Orders = Orders
|
||||
Price = Price
|
||||
|
||||
Genre = Genre
|
||||
Genres = Genres
|
||||
SubGenres = Sub Genres
|
||||
|
||||
NumCode = Numeric Code
|
||||
MinorUnit = Minor Unit
|
||||
Exponent = Exponent
|
||||
@@ -1,5 +1,8 @@
|
||||
sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("admin.Component", {
|
||||
metadata:{ manifest:'json' }
|
||||
}))
|
||||
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
|
||||
"use strict";
|
||||
return AppComponent.extend("admin.Component", {
|
||||
metadata: { manifest: "json" }
|
||||
});
|
||||
});
|
||||
|
||||
/* eslint no-undef:0 */
|
||||
@@ -24,7 +24,7 @@
|
||||
"sap.ui5": {
|
||||
"dependencies": {
|
||||
"libs": {
|
||||
"sap.fe": {}
|
||||
"sap.fe.templates": {}
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
|
||||
@@ -1,5 +1,7 @@
|
||||
sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("bookshop.Component", {
|
||||
metadata:{ manifest:'json' }
|
||||
}))
|
||||
|
||||
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
|
||||
"use strict";
|
||||
return AppComponent.extend("bookshop.Component", {
|
||||
metadata: { manifest: "json" }
|
||||
});
|
||||
});
|
||||
/* eslint no-undef:0 */
|
||||
@@ -24,7 +24,7 @@
|
||||
"sap.ui5": {
|
||||
"dependencies": {
|
||||
"libs": {
|
||||
"sap.fe": {}
|
||||
"sap.fe.templates": {}
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
*/
|
||||
|
||||
using { sap.capire.bookshop as my } from '@capire/bookshop';
|
||||
|
||||
using { sap.common } from '@capire/common';
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -28,13 +28,6 @@ annotate my.Books with @(
|
||||
author @ValueList.entity:'Authors';
|
||||
};
|
||||
|
||||
annotate my.Authors with @(
|
||||
UI: {
|
||||
Identification: [{Value:name}],
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Books Details
|
||||
@@ -66,15 +59,199 @@ annotate my.Books with {
|
||||
descr @UI.MultiLineText;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Genres List
|
||||
//
|
||||
annotate my.Genres with @(
|
||||
Common.SemanticKey: [name],
|
||||
UI: {
|
||||
SelectionFields: [ name ],
|
||||
LineItem:[
|
||||
{Value: name},
|
||||
{Value: parent.name, Label: 'Main Genre'},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Genre Details
|
||||
//
|
||||
annotate my.Genres with @(
|
||||
UI: {
|
||||
Identification: [{Value:name}],
|
||||
HeaderInfo: {
|
||||
TypeName: '{i18n>Genre}',
|
||||
TypeNamePlural: '{i18n>Genres}',
|
||||
Title: {Value: name},
|
||||
Description: {Value: ID}
|
||||
},
|
||||
Facets: [
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>SubGenres}', Target: 'children/@UI.LineItem'},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Genres Elements
|
||||
//
|
||||
annotate my.Genres with {
|
||||
ID @title: '{i18n>ID}';
|
||||
name @title: '{i18n>Genre}';
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Authors List
|
||||
//
|
||||
annotate my.Authors with @(
|
||||
Common.SemanticKey: [name],
|
||||
UI: {
|
||||
Identification: [{Value:name}],
|
||||
SelectionFields: [ name ],
|
||||
LineItem:[
|
||||
{Value: ID},
|
||||
{Value: name},
|
||||
{Value: dateOfBirth},
|
||||
{Value: dateOfDeath},
|
||||
{Value: placeOfBirth},
|
||||
{Value: placeOfDeath},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Author Details
|
||||
//
|
||||
annotate my.Authors with @(
|
||||
UI: {
|
||||
HeaderInfo: {
|
||||
TypeName: '{i18n>Author}',
|
||||
TypeNamePlural: '{i18n>Authors}',
|
||||
Title: {Value: name},
|
||||
Description: {Value: dateOfBirth}
|
||||
},
|
||||
Facets: [
|
||||
{$Type: 'UI.ReferenceFacet', Target: 'books/@UI.LineItem'},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Authors Elements
|
||||
//
|
||||
annotate my.Authors with {
|
||||
ID @title:'{i18n>ID}' @UI.HiddenFilter;
|
||||
name @title:'{i18n>AuthorName}';
|
||||
name @title:'{i18n>Name}';
|
||||
dateOfBirth @title:'{i18n>DateOfBirth}';
|
||||
dateOfDeath @title:'{i18n>DateOfDeath}';
|
||||
placeOfBirth @title:'{i18n>PlaceOfBirth}';
|
||||
placeOfDeath @title:'{i18n>PlaceOfDeath}';
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Languages List
|
||||
//
|
||||
annotate common.Languages with @(
|
||||
Common.SemanticKey: [code],
|
||||
Identification: [{Value:code}],
|
||||
UI: {
|
||||
SelectionFields: [ name, descr ],
|
||||
LineItem:[
|
||||
{Value: code},
|
||||
{Value: name},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Language Details
|
||||
//
|
||||
annotate common.Languages with @(
|
||||
UI: {
|
||||
HeaderInfo: {
|
||||
TypeName: '{i18n>Language}',
|
||||
TypeNamePlural: '{i18n>Languages}',
|
||||
Title: {Value: name},
|
||||
Description: {Value: descr}
|
||||
},
|
||||
Facets: [
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
||||
],
|
||||
FieldGroup#Details: {
|
||||
Data: [
|
||||
{Value: code},
|
||||
{Value: name},
|
||||
{Value: descr}
|
||||
]
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Currencies List
|
||||
//
|
||||
annotate common.Currencies with @(
|
||||
Common.SemanticKey: [code],
|
||||
Identification: [{Value:code}],
|
||||
UI: {
|
||||
SelectionFields: [ name, descr ],
|
||||
LineItem:[
|
||||
{Value: descr},
|
||||
{Value: symbol},
|
||||
{Value: code},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Currency Details
|
||||
//
|
||||
annotate common.Currencies with @(
|
||||
UI: {
|
||||
HeaderInfo: {
|
||||
TypeName: '{i18n>Currency}',
|
||||
TypeNamePlural: '{i18n>Currencies}',
|
||||
Title: {Value: descr},
|
||||
Description: {Value: code}
|
||||
},
|
||||
Facets: [
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Extended}', Target: '@UI.FieldGroup#Extended'},
|
||||
],
|
||||
FieldGroup#Details: {
|
||||
Data: [
|
||||
{Value: name},
|
||||
{Value: symbol},
|
||||
{Value: code},
|
||||
{Value: descr}
|
||||
]
|
||||
},
|
||||
FieldGroup#Extended: {
|
||||
Data: [
|
||||
{Value: numcode},
|
||||
{Value: minor},
|
||||
{Value: exponent}
|
||||
]
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Currencies Elements
|
||||
//
|
||||
annotate common.Currencies with {
|
||||
numcode @title:'{i18n>NumCode}';
|
||||
minor @title:'{i18n>MinorUnit}';
|
||||
exponent @title:'{i18n>Exponent}';
|
||||
}
|
||||
|
||||
@@ -1,5 +1,8 @@
|
||||
sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("orders.Component", {
|
||||
metadata:{ manifest:'json' }
|
||||
}))
|
||||
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
|
||||
"use strict";
|
||||
return AppComponent.extend("orders.Component", {
|
||||
metadata: { manifest: "json" }
|
||||
});
|
||||
});
|
||||
|
||||
/* eslint no-undef:0 */
|
||||
@@ -24,7 +24,7 @@
|
||||
"sap.ui5": {
|
||||
"dependencies": {
|
||||
"libs": {
|
||||
"sap.fe": {}
|
||||
"sap.fe.templates": {}
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
|
||||
@@ -19,6 +19,8 @@
|
||||
"sqlite3": "^4"
|
||||
},
|
||||
"scripts": {
|
||||
"bookshop": "cds watch bookshop",
|
||||
"fiori": "cds watch fiori",
|
||||
"mocha": "npx mocha || echo",
|
||||
"jest": "npx jest --verbose",
|
||||
"test": "npm run jest -s"
|
||||
|
||||
1
reviewed/.env
Normal file
1
reviewed/.env
Normal file
@@ -0,0 +1 @@
|
||||
cds.requires.messaging.kind = file-based-messaging
|
||||
@@ -2,8 +2,8 @@
|
||||
"name": "@capire/bookshop-with-reviews",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@capire/bookshop": "../../../bookshop",
|
||||
"@capire/reviews": "..",
|
||||
"@capire/bookshop": "../bookshop",
|
||||
"@capire/reviews": "../reviews",
|
||||
"@sap/cds": "^3.33.1",
|
||||
"express": "^4.17.1"
|
||||
},
|
||||
@@ -14,9 +14,6 @@
|
||||
},
|
||||
"ReviewsService": {
|
||||
"kind": "odata", "model": "@capire/reviews"
|
||||
},
|
||||
"messaging": {
|
||||
"kind": "file-based-messaging"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -12,9 +12,13 @@ cds.once('served', async()=>{
|
||||
// react on event messages from reviews service
|
||||
const ReviewsService = await cds.connect.to ('ReviewsService')
|
||||
const db = await cds.connect.to ('db')
|
||||
|
||||
// reflect entities required below...
|
||||
const { Books } = db.entities('sap.capire.bookshop')
|
||||
const { Reviews } = ReviewsService.entities
|
||||
|
||||
ReviewsService.on ('reviewed', (msg) => {
|
||||
console.debug ('> received:', msg.event, msg.data)
|
||||
const { Books } = db.entities('sap.capire.bookshop')
|
||||
const { subject, rating } = msg.data
|
||||
const tx = db.tx (msg) // TODO: db.tx(msg) fully implemented?
|
||||
return tx.update (Books,subject) .with ({rating})
|
||||
@@ -24,17 +28,17 @@ cds.once('served', async()=>{
|
||||
const CatalogService = await cds.connect.to ('CatalogService')
|
||||
CatalogService.impl (srv => srv.on ('READ', 'Books/reviews', (req) => {
|
||||
console.debug ('> delegating to ReviewsService')
|
||||
const { Reviews } = ReviewsService.entities
|
||||
const [ subject ] = req.params
|
||||
const [ id ] = req.params
|
||||
const tx = ReviewsService.tx(req)
|
||||
return tx.read (Reviews) .where({subject}) .columns (req.query.SELECT.columns)
|
||||
return tx.read (Reviews) .where ({ subject: String(id) })
|
||||
.columns (req.query.SELECT.columns)
|
||||
}))
|
||||
|
||||
})
|
||||
|
||||
// Other bootstrapping events you could hook in to...
|
||||
/* eslint-disable no-unused-vars */
|
||||
cds.on('boostrap',(app) => {/* ... */})
|
||||
cds.on('bootstrap',(app) => {/* ... */})
|
||||
cds.on('loaded', (model) => {/* ... */})
|
||||
cds.on('connect', (srv) => {/* ... */})
|
||||
cds.on('serving', (srv) => {/* ... */})
|
||||
@@ -1,13 +1,16 @@
|
||||
#################################################
|
||||
#
|
||||
# To ReviewsService mocked in bookshop process
|
||||
# To ReviewsService
|
||||
#
|
||||
# move the right down:
|
||||
@reviews-service = http://localhost:4004/reviews
|
||||
@reviews-service = http://localhost:5005/reviews
|
||||
|
||||
GET http://localhost:4004/reviews/Reviews?
|
||||
### Get all reviews
|
||||
GET {{reviews-service}}/Reviews
|
||||
|
||||
###
|
||||
|
||||
POST http://localhost:4004/reviews/Reviews
|
||||
### Add a new review (with random rating)
|
||||
POST {{reviews-service}}/Reviews
|
||||
Content-Type: application/json;IEEE754Compatible=true
|
||||
|
||||
{"subject":"201", "title":"boo"}
|
||||
@@ -20,17 +23,18 @@ Content-Type: application/json;IEEE754Compatible=true
|
||||
# (both in-process as well as separate one)
|
||||
#
|
||||
|
||||
@bookshop = http://localhost:4004
|
||||
|
||||
### Request to CatalogService > delegated to ReviewsService
|
||||
GET http://localhost:4004/browse/Books(201)/reviews?
|
||||
GET {{bookshop}}/browse/Books(201)/reviews?
|
||||
&$select=rating,date,reviewer,title
|
||||
|
||||
### Alternative OData URL
|
||||
GET http://localhost:4004/browse/Books/201/reviews?
|
||||
GET {{bookshop}}/browse/Books/201/reviews?
|
||||
&$select=rating,date,reviewer,title
|
||||
|
||||
###
|
||||
GET http://localhost:4004/browse/Books(201)?
|
||||
GET {{bookshop}}/browse/Books(201)?
|
||||
&$select=ID,title,rating
|
||||
&$expand=reviews
|
||||
# Note: the $expand only works in case of ReviewsService in same process
|
||||
@@ -1 +1,2 @@
|
||||
cds.requires.messaging.kind = file-based-messaging
|
||||
PORT = 5005
|
||||
@@ -12,15 +12,12 @@
|
||||
},
|
||||
"scripts": {
|
||||
"reviews-service": "cds watch",
|
||||
"bookshop": "cds watch test/bookshop"
|
||||
"books-reviewed": "cds watch ../reviewed"
|
||||
},
|
||||
"cds": {
|
||||
"requires": {
|
||||
"db": {
|
||||
"kind": "sql"
|
||||
},
|
||||
"messaging": {
|
||||
"kind": "file-based-messaging"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +0,0 @@
|
||||
#################################################
|
||||
#
|
||||
# To ReviewsService running as separate process
|
||||
#
|
||||
|
||||
GET http://localhost:5005/reviews/Reviews?
|
||||
|
||||
###
|
||||
|
||||
POST http://localhost:5005/reviews/Reviews
|
||||
Content-Type: application/json;IEEE754Compatible=true
|
||||
|
||||
{"subject":"201", "title":"boo"}
|
||||
@@ -261,7 +261,7 @@ describe('cds.ql → cqn', () => {
|
||||
// same for works distinct
|
||||
})
|
||||
|
||||
xtest('where ( ... cql | {x:y} )', () => {
|
||||
test.skip('where ( ... cql | {x:y} )', () => {
|
||||
const args = [`foo`, "'bar'", 3]
|
||||
const ID = 11
|
||||
|
||||
@@ -359,6 +359,11 @@ describe('cds.ql → cqn', () => {
|
||||
CQL`SELECT from Foo where x in (SELECT y from Bar)`
|
||||
)
|
||||
|
||||
// using query api
|
||||
expect(SELECT.from('Books').where(
|
||||
`author.name in`, SELECT('name').from('Authors'))).to.eql(CQL`SELECT from Books where author.name in (SELECT name from Authors)`
|
||||
)
|
||||
|
||||
// in classical semi joins
|
||||
expect(
|
||||
SELECT('x').from(Foo) .where ( `exists`,
|
||||
@@ -461,47 +466,38 @@ describe('cds.ql → cqn', () => {
|
||||
UPDATE.with allows to pass in plain data payloads, e.g. as obtained from REST clients.
|
||||
In addition, UPDATE.with supports specifying expressions, either in CQL fragements
|
||||
notation or as simple expression objects.
|
||||
|
||||
UPDATE.data allows to pass in plain data payloads, e.g. as obtained from REST clients.
|
||||
The passed in object can be modified subsequently, e.g. by adding or modifying values
|
||||
before the query is finally executed.
|
||||
*/
|
||||
test('with', () => {
|
||||
expect(UPDATE(Foo).with(`foo=`, 11, `bar-=`, 22))
|
||||
test('with + data', () => {
|
||||
if (cds.version < '4.1.0') return
|
||||
const o = {}
|
||||
const q = UPDATE(Foo).data(o).with(`bar-=`, 22)
|
||||
o.foo = 11
|
||||
expect(q)
|
||||
.to.eql(UPDATE(Foo).with(`foo=`, 11, `bar-=`, 22))
|
||||
.to.eql(UPDATE(Foo).with({ foo: 11, bar: { '-=': 22 } }))
|
||||
.to.eql({
|
||||
UPDATE: {
|
||||
entity: 'Foo',
|
||||
data: { foo: 11 },
|
||||
with: {
|
||||
foo: { val: 11 },
|
||||
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
// some more
|
||||
// expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
|
||||
// UPDATE: {
|
||||
// entity: 'Foo',
|
||||
// with: {
|
||||
// bar: { func: 'coalesce', args: [{ ref: ['x'] }, { ref: ['y'] }] },
|
||||
// car: { val: "foo's bar, car" },
|
||||
// },
|
||||
// },
|
||||
// })
|
||||
})
|
||||
|
||||
/*
|
||||
UPDATE.data allows to pass in plain data payloads, e.g. as obtained from REST clients.
|
||||
The passed in object can be modified subsequently, e.g. by adding or modifying values
|
||||
before the query is finally executed.
|
||||
*/
|
||||
test('data', () => {
|
||||
const o = {}
|
||||
const q = UPDATE(Foo).data(o).with(`bar-=`, 22)
|
||||
o.foo = 11
|
||||
expect(q).to.eql({
|
||||
expect(UPDATE(Foo).with(`bar = coalesce(x,y), car = 'foo''s bar, car'`)).to.eql({
|
||||
UPDATE: {
|
||||
entity: 'Foo',
|
||||
data: { foo: 11 },
|
||||
data: {
|
||||
car: "foo's bar, car",
|
||||
},
|
||||
with: {
|
||||
bar: { xpr: [{ ref: ['bar'] }, '-', { val: 22 }] },
|
||||
bar: { func: 'coalesce', args: [{ ref: ['x'] }, { ref: ['y'] }] },
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,10 @@
|
||||
const { expect } = require('./capire')
|
||||
const cds = require('@sap/cds')
|
||||
|
||||
const cwd = process.cwd()
|
||||
before (()=> process.chdir(__dirname))
|
||||
after(()=> process.chdir(cwd))
|
||||
|
||||
describe('Consuming Services locally', () => {
|
||||
//
|
||||
before('bootstrap db and services', async () => {
|
||||
|
||||
@@ -1,3 +1,12 @@
|
||||
const cwd = process.cwd()
|
||||
const is_jest = !!global.test
|
||||
if (is_jest) { // it's jest
|
||||
global.before = (msg,fn) => global.beforeAll(fn||msg)
|
||||
global.after = (msg,fn) => global.afterAll(fn||msg)
|
||||
}
|
||||
before (()=> process.chdir(__dirname))
|
||||
after (()=> process.chdir(cwd))
|
||||
|
||||
describe('Custom Handlers', () => {
|
||||
const { GET, POST, expect } = require('./capire').launch('bookshop')
|
||||
|
||||
|
||||
@@ -72,7 +72,7 @@ describe('Localized Data', () => {
|
||||
])
|
||||
})
|
||||
|
||||
it('supports @cds.localized:false', async ()=>{
|
||||
xit('supports @cds.localized:false', async ()=>{
|
||||
const { data } = await GET(`/browse/BooksSans?&$select=title,localized_title&$expand=currency&$filter=locale eq 'de' or locale eq null`, {
|
||||
headers: { 'Accept-Language': 'de' },
|
||||
})
|
||||
|
||||
@@ -21,7 +21,7 @@ describe('Messaging', ()=>{
|
||||
|
||||
let N=0, received=[], M=0
|
||||
it ('should add messaging event handlers', ()=>{
|
||||
srv.on('reviewed', (msg)=> received.push(msg))
|
||||
srv.on('reviewed', (msg)=> {received.push(msg)})
|
||||
})
|
||||
|
||||
it ('should add more messaging event handlers', ()=>{
|
||||
|
||||
71
test/querying.test.js
Normal file
71
test/querying.test.js
Normal file
@@ -0,0 +1,71 @@
|
||||
describe('Querying', () => {
|
||||
const { GET, expect } = require('./capire').launch('bookshop')
|
||||
const Authors = {name:'sap.capire.bookshop.Authors'}
|
||||
|
||||
it('should SELECT from Authors', async () => {
|
||||
const authors = await SELECT.from (Authors, a => {
|
||||
a.name
|
||||
})
|
||||
expect(authors).to.eql([
|
||||
{ name: 'Emily Brontë' },
|
||||
{ name: 'Charlotte Brontë' },
|
||||
{ name: 'Edgar Allen Poe' },
|
||||
{ name: 'Richard Carpenter' },
|
||||
])
|
||||
})
|
||||
|
||||
it('should SELECT name from Authors', async () => {
|
||||
const authors = await SELECT.from (Authors, a => {
|
||||
a.name
|
||||
})
|
||||
expect(authors).to.eql([
|
||||
{ name: 'Emily Brontë' },
|
||||
{ name: 'Charlotte Brontë' },
|
||||
{ name: 'Edgar Allen Poe' },
|
||||
{ name: 'Richard Carpenter' },
|
||||
])
|
||||
})
|
||||
|
||||
it('should GET /Authors?$select=name&$expand=books($select=title)', async () => {
|
||||
const {data:{value:authors}} = await GET ('/admin/Authors?$select=name&$expand=books($select=title)')
|
||||
expect(authors).to.containSubset([
|
||||
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
|
||||
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
|
||||
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
|
||||
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
|
||||
])
|
||||
})
|
||||
|
||||
it('should SELECT from Authors { name, books { title } }', async () => {
|
||||
const authors = await SELECT.from(Authors, a => { a.name, a.books(b=>{ b.title }) })
|
||||
expect(authors).to.eql([
|
||||
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
|
||||
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
|
||||
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
|
||||
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
|
||||
])
|
||||
})
|
||||
|
||||
it('should GET /Authors?$expand=books', async () => {
|
||||
const {data:{value:authors}} = await GET ('/admin/Authors?$select=name&$expand=books($select=title)')
|
||||
expect(authors).to.containSubset([
|
||||
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
|
||||
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
|
||||
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
|
||||
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
|
||||
])
|
||||
})
|
||||
|
||||
it('should SELECT from Authors { *, books{*} }', async () => {
|
||||
const authors = await SELECT.from(Authors, a => { a('*'), a.books(b => b('*')) })
|
||||
// const authors = await SELECT.from(Authors, ['*',{ref:['books'], expand:['*']}])
|
||||
// const authors = await SELECT.from(Authors, [{ref:['*']},{ref:['books'], expand:[{ref:['*']}]}])
|
||||
expect(authors).to.eql([
|
||||
{ name: 'Emily Brontë', books: [{ title: 'Wuthering Heights' }] },
|
||||
{ name: 'Charlotte Brontë', books: [{ title: 'Jane Eyre' }] },
|
||||
{ name: 'Edgar Allen Poe', books: [{ title: 'The Raven' }, { title: 'Eleonora' }] },
|
||||
{ name: 'Richard Carpenter', books: [{ title: 'Catweazle' }] },
|
||||
])
|
||||
})
|
||||
|
||||
})
|
||||
Reference in New Issue
Block a user