Compare commits

..

49 Commits

Author SHA1 Message Date
D051920
ae20e682be Change list object in tests 2020-08-12 11:18:21 +02:00
D051920
53527aac50 Change list object in tests 2020-08-12 10:39:20 +02:00
D051920
04f9f2a06f Change list object in tests 2020-08-12 10:18:12 +02:00
D051920
fff62e68f1 Merge branches 'enable-fixed-tests' and 'master' of https://github.com/SAP-samples/cloud-cap-samples into enable-fixed-tests 2020-08-12 09:56:11 +02:00
Daniel Hutzel
bd65af43eb Merge pull request #125 from SAP-samples/simplified-reviewed
Simplified reviewed/server.js
2020-08-10 23:24:51 +02:00
Daniel
6f9133cd4f Simplified reviewed/server.js 2020-08-06 19:32:07 +02:00
Daniel Hutzel
441c82b4c9 Merge pull request #123 from SAP-samples/Pinning-Fiori-to-last-known-good
Pinning Fiori to last-known-good
2020-08-01 14:47:30 +02:00
Daniel
fa7cff4123 Pinning Fiori to last-known-good 2020-08-01 14:44:13 +02:00
Christian Georgi
1b69064752 Make cuid.ID Core.Computed for now
+ TODO to check w/ Fiori
2020-07-31 17:18:14 +02:00
Christian Georgi
ada05cf279 Cosmetics 2020-07-31 16:28:08 +02:00
Christian Georgi
4b78a8b637 Do no longer use run blocks - removed in cds 4 2020-07-31 16:28:08 +02:00
D051920
ade170367b Enable temporary disabled test 2020-07-30 16:33:28 +02:00
Daniel Hutzel
5c5afd2790 Merge pull request #118 from SAP-samples/fixed-tests
Fxed cwd in tests
2020-07-23 02:02:01 +02:00
Daniel
74ee6f34e4 Fixed jest mocha 2020-07-23 02:00:34 +02:00
Daniel
9a9b7aeb86 Fxed cwd in tests 2020-07-23 01:56:56 +02:00
Daniel Hutzel
cfc01bbc4f Merge pull request #117 from SAP-samples/fixed-tests
Secured cwd in test
2020-07-23 01:26:27 +02:00
Daniel
aaac6cc678 Secured cwd in test 2020-07-23 01:24:39 +02:00
Daniel Hutzel
99fdf0c038 Merge pull request #116 from SAP-samples/using-dot-env
Doing bindings in .env
2020-07-17 16:05:34 +02:00
Daniel
6ccecfecae Doing bindings in .env 2020-07-17 16:04:03 +02:00
Daniel Hutzel
e5f0a7ef73 Merge pull request #115 from SAP-samples/prepare-4
Prepare tests for upcomming rel 4
2020-07-17 01:52:02 +02:00
Daniel
de54f70570 Prepare tests for upcomming rel 4 2020-07-17 01:45:43 +02:00
Daniel Hutzel
e1c6118cb4 Merge pull request #114 from SAP-samples/fixed-messaging-test
Fixed messaging test
2020-07-17 01:05:14 +02:00
Daniel
a3e4865d97 Fixed messaging test 2020-07-17 01:03:30 +02:00
Daniel Hutzel
641df50422 Merge pull request #111 from SAP-samples/Recommending-VSCode-extension-for-CDS
Recommending vscode extension for CDS
2020-07-09 10:40:30 +02:00
Daniel
0f026ed56c It's finally out... 2020-07-09 10:37:35 +02:00
Daniel Hutzel
57a3c5f178 Merge pull request #110 from SAP-samples/simplified-debugging
Simplified debugging
2020-07-07 17:31:34 +02:00
Daniel
522ec8e071 Simplified debugging 2020-07-07 17:29:30 +02:00
Christian Georgi
7b1c3d8b3a More UI annotations
Make sure all entities can be properly displayed in Fiori preview.
Especially UI.LineItems seems to be mandatory in newer Fiori versions.
2020-07-02 19:35:04 +02:00
Christian Georgi
5ba69b5021 Update issue templates 2020-06-30 09:05:27 +02:00
Christian Georgi
fd796b54ef Redirect to community 2020-06-25 09:25:11 +02:00
ianquigley-sap
660344b623 Delete .npmrc
The @sap scoped registry should now point to registry.npmjs.org rather than npm.sap.com.  Please see https://jam4.sapjam.com/feed/item/eOtNGT1RSxVzknBtpSy2My for full details on the migration.  npm.sap.com will not be updated going forward for the CDS library
2020-06-24 12:07:40 +02:00
Daniel Hutzel
d20c29a758 Merge pull request #101 from SAP-samples/mocha-parallel
Running all mocha tests in parallel
2020-06-19 11:40:20 +02:00
Daniel
604cc0514c Running all mocha tests in parallel 2020-06-19 09:51:37 +02:00
I051442
ff351455dd Update for sap.fe usage starting with UI5 1.78 2020-06-18 17:19:02 +02:00
Daniel Hutzel
8f74bd32a9 Merge pull request #91 from SAP-samples/fix-for-mocha
Stick to standard .skip
2020-06-02 11:02:01 +02:00
Daniel
c181afe8c6 Stick to standard .skip 2020-06-02 10:57:57 +02:00
Daniel Hutzel
9ade3e6b6a Merge pull request #90 from SAP-samples/reviewed
Separated reviewed and reviews
2020-05-29 16:58:37 +02:00
Daniel
6f9737ae38 Separated reviewed and reviews 2020-05-29 16:56:03 +02:00
Christian Georgi
0a552b4346 Try with other node 12 version 2020-05-14 21:50:30 +02:00
Christian Georgi
6367081e9d CI badge 2020-05-14 21:19:18 +02:00
Christian Georgi
3e73683d99 Rename job 2020-05-14 21:05:26 +02:00
Christian Georgi
2b345ca447 Remove call to build script 2020-05-14 21:05:26 +02:00
Christian Georgi
20593f2fa2 Workflow file for node.js 2020-05-14 20:46:17 +02:00
Vitaly Kozyura
ca45aa1cf7 Merge pull request #74 from SAP-samples/temporary-skip-test
Temporary disable test
2020-05-07 09:59:39 +02:00
Christian Georgi
e408836c2a Fix typo in bootstrap event 2020-05-06 18:33:16 +02:00
D051920
3000a9e2df Temporary disable test 2020-05-06 18:13:40 +02:00
Heiko Witteborg
b83236de2a Merge pull request #73 from SAP-samples/add-test-for-subselect
add test for subselect in query api as documented
2020-05-06 13:03:50 +02:00
d045778
46b3b8aaec add test for subselect in query api as documented 2020-05-06 11:52:30 +02:00
Iwona Hahn
59f5c82684 Cosmetics 2020-05-05 15:01:53 +02:00
36 changed files with 432 additions and 191 deletions

1
.env
View File

@@ -1,2 +1 @@
cds.features.snapi = y
cds.cdsc.beta.aspectCompositions = true

View 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
View 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
View File

@@ -0,0 +1 @@
parallel: true

1
.npmrc
View File

@@ -1 +0,0 @@
@sap:registry=https://npm.sap.com

View File

@@ -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
View File

@@ -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>/**"]
}
],

View File

@@ -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). &rarr; See [**Overview** of contained samples](samples.md)
![](https://github.com/SAP-samples/cloud-cap-samples/workflows/CI/badge.svg)
### 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.

View 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
1 ID parent_ID name
11 19 10 Fairy Tale
12 20 Non-Fiction
13 21 20 Biography
14 22 20 21 Autobiography
15 23 20 Essay
16 24 20 Speech

View File

@@ -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
1 code symbol name descr numcode minor exponent
2 EUR Euro European Euro 978 Cent 2
3 USD $ US Dollar United States Dollar 840 Cent 2
4 CAD $ Canadian Dollar Canadian Dollar 124 Cent 2
5 AUD $ Australian Dollar Canadian Dollar Australian Dollar 036 Cent 2
6 GBP £ British Pound Great Britain Pound 826 Penny 2
7 ILS Shekel Israeli New Shekel 376 Agorat 2
8 INR Rupee Indian Rupee 356 Paise 2

View File

@@ -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

View File

@@ -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 */

View File

@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe": {}
"sap.fe.templates": {}
}
},
"models": {

View File

@@ -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 */

View File

@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe": {}
"sap.fe.templates": {}
}
},
"models": {

View File

@@ -1,9 +1,9 @@
/*
Common Annotations shared by all apps
Common Annotations shared by all apps
*/
using { sap.capire.bookshop as my } from '@capire/bookshop';
using { sap.common } from '@capire/common';
////////////////////////////////////////////////////////////////////////////
//
@@ -13,7 +13,7 @@ annotate my.Books with @(
Common.SemanticKey: [title],
UI: {
Identification: [{Value:title}],
SelectionFields: [ ID, author_ID, price, currency_code ],
SelectionFields: [ ID, author_ID, price, currency_code ],
LineItem: [
{Value: ID},
{Value: title},
@@ -28,25 +28,18 @@ annotate my.Books with @(
author @ValueList.entity:'Authors';
};
annotate my.Authors with @(
UI: {
Identification: [{Value:name}],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books Details
//
annotate my.Books with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Book}',
TypeNamePlural: '{i18n>Books}',
Title: {Value: title},
Description: {Value: author.name}
},
HeaderInfo: {
TypeName: '{i18n>Book}',
TypeNamePlural: '{i18n>Books}',
Title: {Value: title},
Description: {Value: author.name}
},
}
);
@@ -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 {
name @title: '{i18n>Genre}';
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}';
}

View File

@@ -40,7 +40,8 @@
</script>
<script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
<!-- <script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js" -->
<script src="https://sapui5.hana.ondemand.com/1.78.6/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"

View File

@@ -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 */

View File

@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe": {}
"sap.fe.templates": {}
}
},
"models": {

View File

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

View File

@@ -4,10 +4,13 @@ namespace sap.capire.bookshop;
entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many {
key pos : Integer;
book : Association to Books;
amount : Integer;
};
Items : Composition of many OrderItems on Items.parent = $self;
currency : Currency;
}
entity OrderItems : cuid {
parent : Association to Orders;
book : Association to Books;
amount : Integer;
netAmount : Decimal(9,2);
}

View File

@@ -6,14 +6,14 @@ module.exports = cds.service.impl(function() {
// Reduce stock of ordered books if available stock suffices
this.before ('CREATE', 'Orders', (req) => {
const { Items: OrderItems } = req.data
return cds.transaction(req) .run (()=> OrderItems.map (order =>
UPDATE (Books) .where ('ID =', order.book_ID)
.and ('stock >=', order.amount)
.set ('stock -=', order.amount)
const { Items: items } = req.data
return cds.transaction(req) .run (items.map (item =>
UPDATE (Books) .where ('ID =', item.book_ID)
.and ('stock >=', item.amount)
.set ('stock -=', item.amount)
)) .then (all => all.forEach ((affectedRows,i) => {
if (affectedRows === 0) req.error (409,
`${OrderItems[i].amount} exceeds stock for book #${OrderItems[i].book_ID}`
`${items[i].amount} exceeds stock for book #${items[i].book_ID}`
)
}))
})

View File

@@ -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
View File

@@ -0,0 +1 @@
cds.requires.messaging.kind = file-based-messaging

View File

@@ -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"
}
}
}

43
reviewed/server.js Normal file
View File

@@ -0,0 +1,43 @@
////////////////////////////////////////////////////////////////////////////
//
// This is an example of using a project-local server.js to intercept
// the default bootstrapping process.
//
const cds = require ('@sap/cds')
// Connect CatalogService and ReviewsService when all are served...
cds.once('served', async ({CatalogService}) => {
// reflect entity definitions used below...
const { Books } = cds.entities('sap.capire.bookshop')
const { Reviews } = cds.entities('ReviewsService')
// prepend the following handler so it overrides the default handler
CatalogService.prepend (srv => srv.on ('READ', 'Books/reviews', (req) => {
console.debug ('> delegating request to ReviewsService')
const [id] = req.params, { columns, limit } = req.query.SELECT
return SELECT(columns).from(Reviews).limit(limit).where({subject:String(id)})
}))
// subscribe to events emitted by ReviewsService
const ReviewsService = await cds.connect.to ('ReviewsService')
ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { subject, rating } = msg.data
return UPDATE(Books,subject).with({rating})
})
})
// Other bootstrapping events you could hook in to...
/* eslint-disable no-unused-vars */
cds.on('bootstrap',(app) => {/* ... */})
cds.on('loaded', (model) => {/* ... */})
cds.on('connect', (srv) => {/* ... */})
cds.on('serving', (srv) => {/* ... */})
cds.once('served', (all) => {/* ... */})
cds.once('listening', ({server,url}) => {/* ... */})
// Delegate bootstrapping to built-in server.js
module.exports = cds.server

View File

@@ -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,19 @@ 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?
&$select=rating,date,reviewer,title
GET {{bookshop}}/browse/Books/201/reviews?
&$select=rating,date,title
&$top=3
###
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

View File

@@ -1 +1,3 @@
cds.requires.messaging.kind = file-based-messaging
cds.odata.skipValidation = true
PORT = 5005

View File

@@ -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"
}
}
}

View File

@@ -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"}

View File

@@ -1,51 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// This is an example of using a project-local server.js to intercept
// the default bootstrapping process.
//
const cds = require ('@sap/cds')
// Mashup services after all are served...
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')
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})
})
// delegate requests to read reviews to ReviewsService
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 tx = ReviewsService.tx (req)
return tx.read (Reviews) .where({subject}) .columns (req.query.SELECT.columns)
}))
})
// Other bootstrapping events you could hook in to...
/* eslint-disable no-unused-vars */
cds.on('boostrap',(app) => {/* ... */})
cds.on('loaded', (model) => {/* ... */})
cds.on('connect', (srv) => {/* ... */})
cds.on('serving', (srv) => {/* ... */})
cds.once('listening', ({server,url}) => {/* ... */})
// Delegate bootstrapping to built-in server.js
module.exports = cds.server
// Monkey-patching older releases
if (cds.version < '3.33.4') cds.once('listening', ()=> cds.emit('served'))
// Launch server if started directly from command-line
if (!module.parent) cds.server()

View File

@@ -261,7 +261,7 @@ describe('cds.ql → cqn', () => {
// same for works distinct
})
xtest('where ( ... cql | {x:y} )', () => {
test('where ( ... cql | {x:y} )', () => {
const args = [`foo`, "'bar'", 3]
const ID = 11
@@ -278,7 +278,6 @@ describe('cds.ql → cqn', () => {
from: { ref: ['Foo'] },
where: cdr
? [
// '(', //> this one is not required
{ ref: ['ID'] },
'=',
{ val: ID },
@@ -287,7 +286,7 @@ describe('cds.ql → cqn', () => {
'in',
{ val: args },
'and',
'(', //> this one is missing, and that's changing the logic -> that's a BUG
'(',
{ ref: ['x'] },
'like',
{ val: '%x%' },
@@ -298,7 +297,6 @@ describe('cds.ql → cqn', () => {
')',
]
: [
'(', //> this one is not required
{ ref: ['ID'] },
'=',
{ val: ID },
@@ -307,7 +305,7 @@ describe('cds.ql → cqn', () => {
'in',
{ val: args },
'and',
// '(', //> this one is missing, and that's changing the logic -> that's a BUG
'(',
{ ref: ['x'] },
'like',
{ val: '%x%' },
@@ -337,11 +335,31 @@ describe('cds.ql → cqn', () => {
{ val: 'bar' },
',',
{ val: 3 },
')',
],
},
')'
]
}
})
expect(SELECT.from(Foo).where(`ID=`, ID, `and x in`, args)).to.eql(cqn)
const cqnFluent = {
SELECT: {
from: { ref: ['Foo'] },
where: [
{ ref: ['ID'] },
'=',
{ val: ID },
'and',
{ ref: ['x'] },
'in',
{ list: [
{ val: 'foo' },
{ val: 'bar' },
{ val: 3 }
] }
]
}
}
expect(SELECT.from(Foo).where(`ID=`, ID, `and x in`, args)).to.eql(cqnFluent)
expect(SELECT.from(Foo).where(`ID=${ID} and x in (${args})`)).to.eql(cqn)
expect(
@@ -359,6 +377,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 +484,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.
*/
test('with', () => {
expect(UPDATE(Foo).with(`foo=`, 11, `bar-=`, 22))
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 + 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'] }] },
},
},
})

View File

@@ -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 () => {

View File

@@ -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')

View File

@@ -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', ()=>{