Compare commits

..

38 Commits

Author SHA1 Message Date
Christian Georgi
2a1a7b1f2b Make test more robust against future model changes
Compare subset of JSON, not full JSON.
2023-04-11 15:38:58 +02:00
Dr. David A. Kunz
a560e12106 Fix locale handling 2022-09-12 10:50:47 +02:00
Christian Georgi
3dccfc30f0 Increase test timeout 2021-04-23 13:58:46 +02:00
Christian Georgi
cc0be09341 Add CF login instructions 2020-04-07 13:58:59 +02:00
Christian Georgi
175a183f27 Update README.md 2020-04-07 13:41:50 +02:00
Christian Georgi
a1d663a717 Update README.md 2020-04-07 13:40:25 +02:00
Christian Georgi
e75cc7a0a5 Add package-lock.json, fix readme 2020-04-02 15:52:37 +02:00
Christian Georgi
37ed881102 Change localhost link in BAS 2020-03-23 09:33:11 +01:00
Harini Gunabalan
a3ab8cd51f Update README.md 2020-03-20 17:35:06 +01:00
Christian Georgi
6c1f06a06c New readme 2020-03-20 16:23:01 +01:00
Christian Georgi
e9a18c9938 Add launch for bookshop 2020-02-11 11:26:28 +00:00
Christian Georgi
eb5334cafc Cosmetics 2 2020-02-11 07:55:46 +00:00
Christian Georgi
d54b16bd94 Cosmetics 2020-02-11 07:54:27 +00:00
Christian Georgi
ba054305a6 Use beforeAll in tests 2020-02-10 22:44:31 +00:00
Christian Georgi
e89fc1d9bb Test cosmetics 2020-02-10 22:24:37 +00:00
Christian Georgi
659fe52509 Cosmetics 2020-02-10 22:19:14 +00:00
Christian Georgi
4ad21a47d6 Some more code, cosmetics 2020-02-10 22:09:14 +01:00
Christian Georgi
7b5b2210ae Bring old HANA driver back for the moment 2020-02-10 10:05:28 +00:00
Christian Georgi
c20c1609a7 Add Jest as dev dependency to bookshop 2020-02-10 09:30:26 +00:00
Christian Georgi
ffc9fbd5f5 Test cosmetics 2020-02-10 08:03:38 +00:00
Christian Georgi
11957c93d8 Remove scripts 2020-02-09 13:44:12 +00:00
Christian Georgi
2ca71b0f7d Cosmetics 2020-02-09 12:03:46 +00:00
Christian Georgi
452b442246 More handlers for demo 2020-02-07 13:14:16 +00:00
Christian Georgi
bf21d0220c Test cosmetics, remove obsolete stuff 2020-02-07 12:32:39 +00:00
Christian Georgi
b65be0cb41 Fix tests to use productive cds model
Also remove strange whitespace
2020-02-07 12:06:08 +00:00
tieyan.fu@sap.com
c3c778b2be Merge branch 'OpenSAP-week2-unit4567' of github.com:SAP-samples/cloud-cap-samples into OpenSAP-week2-unit4567 2020-02-06 13:22:39 +01:00
tieyan.fu@sap.com
c38c5d832c add model into tests folder (avoid?) 2020-02-06 13:17:41 +01:00
Christian Georgi
5929d22f98 Merge branch 'OpenSAP-week2-unit4567' of https://github.com/SAP-samples/cloud-cap-samples into OpenSAP-week2-unit4567 2020-02-05 14:05:41 +00:00
Christian Georgi
4030173bbc Delete unused files, add package-lock.json 2020-02-05 14:04:58 +00:00
tieyan.fu@sap.com
74ce1a5fd1 add odata test 2020-02-05 09:17:51 +01:00
d049740
cbb27132ec upload code for uweek2unit7 testing 2020-02-03 01:13:19 +01:00
d049740
72a17ffcf7 remove auto generated cds requires section 2019-12-19 15:44:27 +01:00
d049740
0e2218a5f1 add hdb depencies 2019-12-19 15:42:14 +01:00
d049740
fa8a7861d5 delete unneeded test files 2019-12-19 15:09:29 +01:00
d049740
ceb5487e75 adapt bookshop for openSAP course 2019-12-19 15:08:34 +01:00
Daniel
c0bce5ae5b Merge pull request #14 from SAP-samples/new-fiori-theme
using sap_fiori_3 theme
2019-12-13 20:09:49 +01:00
Daniel
ad05e2b9db using sap_fiori_3 theme 2019-12-13 20:08:41 +01:00
Christian Georgi
b7c2eee961 Fix broken link to VS Code plugin
Fixes #13
2019-12-13 11:55:52 +01:00
16 changed files with 6478 additions and 87 deletions

2
.gitignore vendored
View File

@@ -13,3 +13,5 @@ target/
connection.properties
default-env.json
packages/messageBox
*.db

2
.npmrc
View File

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

20
.vscode/launch.json vendored
View File

@@ -9,8 +9,16 @@
"request": "launch",
"type": "node",
"runtimeExecutable": "npx",
"runtimeArgs": ["-n"],
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"], // the leading "--" arg ensures it works with as well as without debugging
"runtimeArgs": [
"-n"
],
"args": [
"--",
"cds",
"run",
"--with-mocks",
"--in-memory"
], // the leading "--" arg ensures it works with as well as without debugging
"cwd": "${workspaceFolder}/packages/${input:service}",
"console": "integratedTerminal",
"serverReadyAction": {
@@ -18,7 +26,9 @@
"uriFormat": "http://localhost:%s",
"action": "openExternally"
},
"skipFiles": ["<node_internals>/**"]
"skipFiles": [
"<node_internals>/**"
]
}
],
"inputs": [
@@ -36,7 +46,7 @@
"reviews-service",
"user-service"
],
"default": "bookstore"
"default": "bookshop"
}
]
}
}

View File

@@ -11,7 +11,7 @@ In SAP Business Application Studio, open a terminal.
Then clone the repo with this specific branch:
```sh
git clone https://github.com/sap-samples/cloud-cap-samples projects/cloud-cap-samples -b openSAP-week4-unit1
git clone https://github.com/sap-samples/cloud-cap-samples projects/cloud-cap-samples -b openSAP-week2-unit4567
cd projects/cloud-cap-samples
```
@@ -20,6 +20,16 @@ In the `cloud-cap-samples` folder run:
npm install
```
### Cloud Foundry Login
This is required later in the demo. In Studio's terminal, execute:
```sh
cf login
```
As input, provide
- The Cloud Foundry API endpoint, which is usally `https://api.cf.eu10.hana.ondemand.com`. It can be obtained from the Overview page of your Subaccount in Cloud Cockpit.
- Your user's email address and password
- The name of your trial organization and space
## Run
Now you're ready to run the samples, for example:

6253
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -9,20 +9,18 @@
"install": "(npm -s run lerna) && lerna bootstrap --hoist",
"lerna": "npx --no-install lerna -v > /dev/null || npm i lerna --no-save",
"test": "jest",
"bookshop-enhanced": "cds watch packages/bookshop-enhanced",
"bookshop": "cds watch packages/bookshop",
"bookstore": "cds watch packages/bookstore",
"products-service": "cds watch packages/products-service",
"reviews-service": "cds watch packages/reviews-service"
"bookshop": "cds watch packages/bookshop"
},
"dependencies": {
"@sap/cds": "^3",
"express": "^4"
},
"--add-these-to-devDependencies-for-tests": {
"devDependencies": {
"@types/jest": "*",
"sqlite3": "*",
"jest": "*"
"jest": "*",
"supertest": "^4.0.2",
"@sap/hdi-deploy": "3.7.0"
},
"license": "SAP SAMPLE CODE LICENSE"
}

31
packages/bookshop/.vscode/launch.json vendored Normal file
View File

@@ -0,0 +1,31 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
"version": "0.2.0",
"configurations": [
{
"name": "Run bookshop",
"request": "launch",
"type": "node",
"cwd": "/home/user/projects/cloud-cap-samples/packages/bookshop",
"runtimeExecutable": "npx",
"runtimeArgs": [
"-n"
],
"args": [
"--",
"cds",
"run",
"--in-memory?"
],
"console": "internalConsole",
"internalConsoleOptions": "openOnSessionStart",
"skipFiles": [
"<node_internals>/**"
],
"env": {
"run.config": "{\"handlerId\":\"cap_run_config_handler_id\",\"runnableId\":\"/home/user/projects/cloud-cap-samples/packages/bookshop\"}"
}
}
]
}

View File

@@ -43,7 +43,7 @@
<script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_belize"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-frameOptions="allow"
></script>
<script>

View File

@@ -1,5 +1,3 @@
ID;modifiedAt;createdAt;createdBy;modifiedBy;OrderNo;currency_code;status
da86efd0-4ba1-4078-b7f0-5c9c530297f7;;2019-01-31;ALICE;;1;EUR;processing
2f2f2640-6866-4dcf-8f4d-3027aa831cad;;2019-03-25;ALICE;;10;EUR;completed
64e718c9-ff99-47f1-8ca3-950c850777d4;;2019-01-30;BOB;;2;EUR;processing
1af3322d-3cb1-46be-b312-0ae9ec311537;;2019-03-16;BOB;;9;EUR;completed
ID;modifiedAt;createdAt;createdBy;modifiedBy;OrderNo;currency_code
7e2f2640-6866-4dcf-8f4d-3027aa831cad;;2019-01-31;john.doe@test.com;;1;EUR
64e718c9-ff99-47f1-8ca3-950c850777d4;;2019-01-30;jane.doe@test.com;;2;EUR
1 ID modifiedAt createdAt createdBy modifiedBy OrderNo currency_code status
2 da86efd0-4ba1-4078-b7f0-5c9c530297f7 7e2f2640-6866-4dcf-8f4d-3027aa831cad 2019-01-31 ALICE john.doe@test.com 1 EUR processing
3 2f2f2640-6866-4dcf-8f4d-3027aa831cad 64e718c9-ff99-47f1-8ca3-950c850777d4 2019-03-25 2019-01-30 ALICE jane.doe@test.com 10 2 EUR completed
64e718c9-ff99-47f1-8ca3-950c850777d4 2019-01-30 BOB 2 EUR processing
1af3322d-3cb1-46be-b312-0ae9ec311537 2019-03-16 BOB 9 EUR completed

View File

@@ -1,50 +1,35 @@
namespace sap.capire.bookshop;
using {
Currency,
managed,
cuid
} from '@sap/cds/common';
type Status : String enum {
completed;
processing;
blocked;
}
using { Currency, managed, cuid } from '@sap/cds/common';
entity Books : managed {
key ID : Integer;
title : localized String(111);
descr : localized String(1111);
author : Association to Authors;
stock : Integer;
price : Decimal(9, 2);
currency : Currency;
}
entity Authors : managed {
key ID : Integer;
name : String(111);
dateOfBirth : Date;
dateOfDeath : Date;
placeOfBirth : String;
placeOfDeath : String;
books : Association to many Books
on books.author = $self;
}
entity Orders : cuid, managed {
OrderNo : String @title : 'Order Number'; //> readable key
status : Status default 'processing';
Items : Composition of many OrderItems
on Items.parent = $self;
total : Decimal(9, 2)@readonly;
key ID : Integer;
title : localized String(111);
descr : localized String(1111);
author : Association to Authors;
stock : Integer;
price : Decimal(9,2);
currency : Currency;
}
entity Authors : managed {
key ID : Integer;
name : String(111);
dateOfBirth : Date;
dateOfDeath : Date;
placeOfBirth : String;
placeOfDeath : String;
books : Association to many Books on books.author = $self;
}
entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many OrderItems on Items.parent = $self;
total : Decimal(9,2) @readonly;
currency : Currency;
}
entity OrderItems : cuid {
parent : Association to Orders;
book : Association to Books;
amount : Integer;
netAmount : Decimal(9, 2);
}
netAmount : Decimal(9,2);
}

View File

@@ -6,12 +6,13 @@
"dependencies": {
"@sap/cds": "^3",
"express": "^4",
"@sap/xb-msg-amqp-v100": "^0.9.35"
"hdb": "^0.17.1"
},
"devDependencies": {
"sqlite3": "^4.1.1"
"jest": "*"
},
"scripts": {
"start": "npx cds run"
"start": "npx cds run",
"test": "jest"
}
}

View File

@@ -8,9 +8,8 @@ service AdminService @(_requires:'authenticated-user') {
// Enable Fiori Draft for Orders
annotate AdminService.Orders with @odata.draft.enabled;
// annotate AdminService.Books with @odata.draft.enabled;
// Temporary workaround -> https://github.wdf.sap.corp/cap/issues/issues/3121
// Temporary workaround -> cap/issues#3121
extend service AdminService with {
entity OrderItems as select from my.OrderItems;
}

View File

@@ -0,0 +1,22 @@
const cds = require('@sap/cds')
/** Service implementation for AdminService */
module.exports = cds.service.impl(srv => {
const { OrderItems } = srv.entities ('sap.capire.bookshop')
srv.after (['READ','EDIT'], 'Orders', _calculateTotals)
// on-the-fly calculate the total Order price based on the OrderItems' netAmounts
async function _calculateTotals (orders, req) {
const ordersByID = Array.isArray(orders)
? orders.reduce ((all,o) => { (all[o.ID] = o).total=0; return all },{})
: { [orders.ID]: orders }
return cds.transaction(req) .run (
SELECT.from(OrderItems) .columns ('parent_ID', 'netAmount')
.where ({ parent_ID: {in: Object.keys(ordersByID)} })
) .then (items =>
items.forEach (item => ordersByID [item.parent_ID] .total += item.netAmount)
)
}
})

View File

@@ -1,6 +1,7 @@
using { sap.capire.bookshop as my } from '../db/schema';
@path:'/browse'
// @impl: './cat-service.js'
service CatalogService {
@readonly entity Books as SELECT from my.Books {*,

View File

@@ -1,26 +1,28 @@
const cds = require('@sap/cds')
const { Books } = cds.entities
/** Service implementation for CatalogService */
module.exports = cds.service.impl(function () {
const { Books, Orders } = this.entities
this.after('READ', Books, each => each.stock > 111 && _addDiscount2(each, 11))
this.before('CREATE', Orders, _reduceStock)
/** Add some discount for overstocked books */
function _addDiscount2(each, discount) {
each.title += ` -- ${discount}% discount!`
}
/** Reduce stock of ordered books if available stock suffices */
async function _reduceStock(req) {
const { Items: OrderItems } = req.data
return cds.transaction(req).run(() => OrderItems.map(order =>
UPDATE(Books).set('stock -=', order.amount)
.where('ID =', order.book_ID).and('stock >=', order.amount)
)).then(all => all.forEach((affectedRows, i) => {
if (affectedRows === 0) req.error(409,
`${OrderItems[i].amount} exceeds stock for book #${OrderItems[i].book_ID}`
)
}))
}
module.exports = cds.service.impl(srv => {
srv.after ('READ', 'Books', each => each.stock > 111 && _addDiscount2(each,11))
srv.before ('CREATE', 'Orders', _reduceStock)
// srv.before ('*', (req) => { console.debug ('>>>', req.method, req.target.name) })
})
/** Add some discount for overstocked books */
function _addDiscount2 (each,discount) {
each.title += ` -- ${discount}% discount!`
}
/** Reduce stock of ordered books if available stock suffices */
async function _reduceStock (req) {
const { Items: orderItems } = req.data
return cds.transaction(req) .run (()=> orderItems.map (item =>
UPDATE (Books)
.set ('stock -=', item.amount)
.where ('ID =', item.book_ID) .and ('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}`)
}
}))
}

View File

@@ -0,0 +1,79 @@
const cds = require('@sap/cds')
describe('Bookshop: OData Protocol Level Testing', () => {
jest.setTimeout(20*1000)
const app = require('express')()
const request = require('supertest')(app)
beforeAll(async () => {
await cds.deploy(__dirname + '/../srv/cat-service').to('sqlite::memory:')
await cds.serve('CatalogService').from(__dirname + '/../srv/cat-service').in(app)
})
it('Service $metadata document', async () => {
const response = await request
.get('/browse/$metadata')
.expect('Content-Type', /^application\/xml/)
.expect(200)
const expectedVersion = '<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">'
const expectedBooksEntitySet = '<EntitySet Name="Books" EntityType="CatalogService.Books">'
expect(response.text.includes(expectedVersion)).toBeTruthy()
expect(response.text.includes(expectedBooksEntitySet)).toBeTruthy()
})
it('Get with select, expand and localized', async () => {
const response = await request
.get('/browse/Books?$select=title,author&$expand=currency')
.set('Accept-Language', 'de')
.expect('Content-Type', /^application\/json/)
.expect(200)
expect(response.body.value).toMatchObject([
{
ID: 201, title: "Sturmhöhe", author: "Emily Brontë",
currency: { name: "Pfund", descr: "Britische Pfund", code: "GBP", symbol: "£" }
},
{
ID: 207, title: "Jane Eyre", author: "Charlotte Brontë",
currency: { name: "Pfund", descr: "Britische Pfund", code: "GBP", symbol: "£" }
},
{
ID: 251, title: "The Raven", author: "Edgar Allen Poe",
currency: { name: "US-Dollar", descr: "United States Dollar", code: "USD", symbol: "$" }
},
{
ID: 252, title: "Eleonora", author: "Edgar Allen Poe",
currency: { name: "US-Dollar", descr: "United States Dollar", code: "USD", symbol: "$" }
},
{
ID: 271, title: "Catweazle", author: "Richard Carpenter",
currency: { name: "Euro", descr: "European Euro", code: "EUR", symbol: "€" }
}
])
})
})
describe('Bookshop: CDS Service Level Testing', () => {
let srv, Books
beforeAll(async () => {
srv = await cds.serve('CatalogService').from(__dirname + '/../srv/cat-service')
Books = srv.entities.Books
expect(Books).toBeDefined()
})
it('GETs all books', async () => {
const books = await srv.read(Books, b => { b.title })
expect(books).toMatchObject([
{ title: 'Wuthering Heights' },
{ title: 'Jane Eyre' },
{ title: 'The Raven' },
{ title: 'Eleonora' },
{ title: 'Catweazle' }
])
})
})