cleaned up

This commit is contained in:
Daniel
2020-03-02 08:29:49 +01:00
parent cb066233c9
commit c6eb21ec51
26 changed files with 140 additions and 122 deletions

17
.vscode/tasks.json vendored
View File

@@ -1,17 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "npm", "script": "watch", "path": "packages/bookshop/",
"options": { "env": { "PORT": "4004" }},
"presentation": { "group": "A" }
},
{
"type": "npm", "script": "watch", "path": "packages/reviews-service/",
"options": { "env": { "PORT": "5005" }},
"presentation": { "group": "A" }
}
]
}

View File

@@ -13,7 +13,7 @@ Clone this repo as shown below, if you have [git](https://git-scm.com/downloads)
otherwise [download as zip file](archive/master.zip). otherwise [download as zip file](archive/master.zip).
```sh ```sh
git clone https://github.com/sap-samples/cap samples git clone https://github.com/sap-samples/cloud-cap-samples samples
cd samples cd samples
``` ```
@@ -29,7 +29,7 @@ npm install
With that you're ready to run the samples, for example: With that you're ready to run the samples, for example:
```sh ```sh
npm run bookshop # or... npm run bookshop # or...
npm run bookshop-fiori cd bookshop; cds watch
``` ```
After that open this link in your browser: <http://localhost:4004> After that open this link in your browser: <http://localhost:4004>

View File

@@ -1,5 +1,6 @@
namespace sap.capire.bookshop; namespace sap.capire.bookshop;
using { Currency, managed, cuid, sap } from '@sap/cds/common'; // using { Currency, managed, sap } from '@sap/cds/common';
using { Currency, managed, sap } from '@capire/common';
entity Books : managed { entity Books : managed {
key ID : Integer; key ID : Integer;
@@ -27,16 +28,3 @@ entity Genres : sap.common.CodeList {
parent : Association to Genres; parent : Association to Genres;
children : Composition of many Genres on children.parent = $self; children : Composition of many Genres on children.parent = $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);
}

View File

@@ -2,20 +2,13 @@
"name": "@capire/bookshop", "name": "@capire/bookshop",
"version": "1.0.0", "version": "1.0.0",
"description": "A simple bookshop application, build in a self-contained all-in-one fashion, i.e. w/o reusing other packages.", "description": "A simple bookshop application, build in a self-contained all-in-one fashion, i.e. w/o reusing other packages.",
"license": "SAP SAMPLE CODE LICENSE",
"dependencies": { "dependencies": {
"@capire/common": "*" "@capire/common": "*"
}, },
"scripts": { "scripts": {
"test:genres": "cds serve tests/genres.cds --in-memory",
"start": "cds run --in-memory?", "start": "cds run --in-memory?",
"watch": "cds watch", "watch": "cds watch"
"test:genres": "cds serve tests/genres.cds --in-memory"
}, },
"cds": { "license": "SAP SAMPLE CODE LICENSE"
"requires": {
"db": {
"kind": "sql"
}
}
}
} }

View File

@@ -3,5 +3,4 @@ using { sap.capire.bookshop as my } from '../db/schema';
service AdminService @(_requires:'authenticated-user') { service AdminService @(_requires:'authenticated-user') {
entity Books as projection on my.Books; entity Books as projection on my.Books;
entity Authors as projection on my.Authors; entity Authors as projection on my.Authors;
entity Orders as select from my.Orders;
} }

View File

@@ -1,13 +1,12 @@
using { sap.capire.bookshop as my } from '../db/schema'; using { sap.capire.bookshop as my } from '../db/schema';
@path:'/browse' service CatalogService @(path:'/browse') {
service CatalogService {
@readonly entity Books as SELECT from my.Books {*, @readonly entity Books as SELECT from my.Books {*,
author.name as author author.name as author
} excluding { createdBy, modifiedBy }; } excluding { createdBy, modifiedBy };
@requires_: 'authenticated-user' @requires_: 'authenticated-user'
@insertonly entity Orders as projection on my.Orders; action order (book : Books.ID, amount: Integer);
} }

View File

@@ -1,26 +1,23 @@
const cds = require('@sap/cds') const cds = require('@sap/cds')
const { Books } = cds.entities
/** Service implementation for CatalogService */ /** Service implementation for CatalogService */
module.exports = cds.service.impl(function() { module.exports = cds.service.impl (function() {
this.after ('READ', 'Books', each => each.stock > 111 && _addDiscount2(each,11))
this.before ('CREATE', 'Orders', _reduceStock) // Get entity definitions from reflected model
const { Books } = cds.entities
// Add some discount for overstocked books
this.after ('READ', 'Books', each => {
if (each.stock > 111) each.title += ` -- 11% discount!`
})
// Reduce stock of ordered books if available stock suffices
this.on ('order', async (req) => {
const {UPDATE} = cds.ql(req), {book,amount} = req.data
const n = await UPDATE (Books, book)
.where ('stock >=', amount)
.set ('stock -=', amount)
n > 0 || req.error (409,`${amount} exceeds stock for book #${book}`)
})
}) })
/** 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}`
)
}))
}

View File

@@ -1,5 +1,5 @@
const cds = require ('@sap/cds') const cds = require ('@sap/cds')
const { expect } = require('chai').use (require('chai-deep-match')) const { expect } = require('chai') .use (require('chai-subset'))
describe('reading/writing hierarchies', ()=>{ describe('reading/writing hierarchies', ()=>{
@@ -35,7 +35,7 @@ describe('reading/writing hierarchies', ()=>{
}) })
}) .where ({name:'Cat'}) }) .where ({name:'Cat'})
) .to.deep.match ( ) .to.containSubset (
{ ID:101, parent:'Cat', children:[ { ID:101, parent:'Cat', children:[
{ child:'Kitty' }, { child:'Kitty' },
@@ -53,7 +53,7 @@ describe('reading/writing hierarchies', ()=>{
c.ID, c.name, c.children (c=>{ c.name },{levels:3}) c.ID, c.name, c.children (c=>{ c.name },{levels:3})
}) .where ({name:'Cat'}) }) .where ({name:'Cat'})
) .to.deep.match ( ) .to.containSubset (
{ ID:101, name:'Cat', children:[ { ID:101, name:'Cat', children:[
{ name:'Kitty', children:[ { name:'Kitty', children:[

View File

@@ -1,18 +1,40 @@
### Service Document
GET http://localhost:4004/browse GET http://localhost:4004/browse
###
### Service $metadata document
GET http://localhost:4004/browse/$metadata GET http://localhost:4004/browse/$metadata
###
### Browsing Books
GET http://localhost:4004/browse/Books? GET http://localhost:4004/browse/Books?
# &$select=title,author &$select=title,stock
# &$expand=currency &$expand=currency
#> add @capire/common to see data for currencies
# &sap-language=de # &sap-language=de
###
### Browsing Authors
GET http://localhost:4004/admin/Authors? GET http://localhost:4004/admin/Authors?
# &$select=name,dateOfBirth,placeOfBirth # &$select=name,dateOfBirth,placeOfBirth
# &$expand=books($select=title;$expand=currency) &$expand=books($select=title;$expand=currency)
#> add @capire/common to see data for currencies
# &$filter=ID eq 101 # &$filter=ID eq 101
# &sap-language=de &sap-language=de
###
POST http://localhost:4004/browse/order
# Run that three times to get out-of-stock message
Content-Type: application/json
{ "book":201, "amount":5 }
###
GET http://localhost:4004/browse/Genres?
###
GET http://localhost:4004/browse/Genres?
&$filter=parent_ID eq null&$select=name
&$expand=children($select=name)
###

View File

@@ -5,6 +5,9 @@
"@capire/bookshop": "*", "@capire/bookshop": "*",
"@capire/reviews": "*" "@capire/reviews": "*"
}, },
"scripts": {
"watch": "cds watch"
},
"cds": { "cds": {
"requires": { "requires": {
"db": { "db": {

View File

@@ -28,7 +28,7 @@
navigationMode: "embedded" navigationMode: "embedded"
}, },
"manage-orders": { "manage-orders": {
title: "Manage Orders", title: "Order Books",
description: "... testing FE v42", description: "... testing FE v42",
additionalInformation: "SAPUI5.Component=orders", additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL", applicationType : "URL",

View File

@@ -1,13 +1,15 @@
using AdminService from '@capire/bookshop/srv/admin-service'; using OrdersService from '@capire/orders/srv/orders-service';
annotate AdminService.Books with { annotate OrdersService.Books with {
price @Common.FieldControl: #ReadOnly; price @Common.FieldControl: #ReadOnly;
} }
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Common // Common
// //
annotate AdminService.OrderItems with { annotate OrdersService.OrderItems with {
book @( book @(
Common: { Common: {
Text: book.title, Text: book.title,
@@ -22,7 +24,7 @@ annotate AdminService.OrderItems with {
@odata.draft.enabled @odata.draft.enabled
annotate AdminService.Orders with @( annotate OrdersService.Orders with @(
UI: { UI: {
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
@@ -83,9 +85,9 @@ annotate AdminService.Orders with @(
//The enity types name is AdminService.my_bookshop_OrderItems //The enity types name is OrdersService.my_bookshop_OrderItems
//The annotations below are not generated in edmx WHY? //The annotations below are not generated in edmx WHY?
annotate AdminService.OrderItems with @( annotate OrdersService.OrderItems with @(
UI: { UI: {
HeaderInfo: { HeaderInfo: {
TypeName: 'Order Item', TypeNamePlural: ' ', TypeName: 'Order Item', TypeNamePlural: ' ',

View File

@@ -3,12 +3,12 @@
"sap.app": { "sap.app": {
"id": "orders", "id": "orders",
"type": "application", "type": "application",
"title": "Manage Orders", "title": "Order Books",
"description": "Sample Application", "description": "Sample Application",
"i18n": "i18n/i18n.properties", "i18n": "i18n/i18n.properties",
"dataSources": { "dataSources": {
"AdminService": { "OrdersService": {
"uri": "/admin/", "uri": "/orders/",
"type": "OData", "type": "OData",
"settings": { "settings": {
"odataVersion": "4.0" "odataVersion": "4.0"
@@ -33,7 +33,7 @@
"uri": "i18n/i18n.properties" "uri": "i18n/i18n.properties"
}, },
"": { "": {
"dataSource": "AdminService", "dataSource": "OrdersService",
"settings": { "settings": {
"synchronizationMode": "None", "synchronizationMode": "None",
"operationMode": "Server", "operationMode": "Server",

View File

@@ -2,20 +2,14 @@
"name": "@capire/fiori", "name": "@capire/fiori",
"version": "1.0.0", "version": "1.0.0",
"description": "A simple bookshop application, build in a self-contained all-in-one fashion, i.e. w/o reusing other packages.", "description": "A simple bookshop application, build in a self-contained all-in-one fashion, i.e. w/o reusing other packages.",
"license": "SAP SAMPLE CODE LICENSE",
"dependencies": { "dependencies": {
"@capire/bookshop": "*", "@capire/bookshop": "*",
"@capire/orders": "*",
"@capire/common": "*" "@capire/common": "*"
}, },
"scripts": { "scripts": {
"start": "cds run --in-memory?", "start": "cds run --in-memory?",
"watch": "cds watch" "watch": "cds watch"
}, },
"cds": { "license": "SAP SAMPLE CODE LICENSE"
"requires": {
"db": {
"kind": "sql"
}
}
}
} }

View File

@@ -1,5 +1,3 @@
// Proxy for importing services from bookshop sample // Proxy for importing services from bookshop sample
using from '@capire/bookshop/srv/admin-service'; using from '@capire/bookshop/srv/admin-service';
using from '@capire/bookshop/srv/cat-service';
annotate AdminService with @impl:'srv/admin-service.js'; annotate AdminService with @impl:'srv/admin-service.js';

17
orders/db/schema.cds Normal file
View File

@@ -0,0 +1,17 @@
using { sap.capire.bookshop.Books } from '@capire/bookshop/db/schema';
using { Currency, managed, cuid } from '@sap/cds/common';
namespace sap.capire.bookshop;
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);
}

5
orders/package.json Normal file
View File

@@ -0,0 +1,5 @@
{
"name": "@capire/orders",
"version": "1.0.0",
"license": "SAP SAMPLE CODE LICENSE"
}

View File

@@ -0,0 +1,5 @@
using { sap.capire.bookshop as my } from '../db/schema';
service OrdersService {
entity Orders as projection on my.Orders;
}

View File

@@ -0,0 +1,21 @@
const cds = require('@sap/cds')
module.exports = cds.service.impl(function() {
const { Books } = cds.entities
// 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)
)) .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

@@ -1,26 +1,22 @@
{ {
"name": "@capire/samples", "name": "@capire/samples",
"description": "The umbrella project for all samples to easily setup for local development and tests.", "description": "The umbrella project for all samples to easily setup for local development and tests.",
"repository": "https://github.com/SAP-samples/cloud-cap-samples.git", "repository": "https://github.com/sap-samples/cloud-cap-samples.git",
"author": "daniel.hutzel@sap.com", "author": "daniel.hutzel@sap.com",
"private": true,
"scripts": { "scripts": {
"bookshop-fiori": "cds watch fiori", "bookshop": "cds watch fiori"
"bookshop": "cds watch bookshop"
}, },
"dependencies": { "dependencies": {
"@capire/bookshop": "file:bookshop", "@capire/bookshop": "file:bookshop",
"@capire/common": "file:common", "@capire/common": "file:common",
"@capire/fiori": "file:fiori", "@capire/fiori": "file:fiori",
"@capire/orders": "file:orders",
"@capire/reviews": "file:reviews" "@capire/reviews": "file:reviews"
}, },
"add-these-to-devDependencies-for-tests": {
"@types/jest": "*",
"jest": "*"
},
"license": "SAP SAMPLE CODE LICENSE",
"devDependencies": { "devDependencies": {
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-deep-match": "^1.2.1" "chai-subset": "^1.6.0"
} },
"license": "SAP SAMPLE CODE LICENSE",
"private": true
} }

View File

@@ -1 +0,0 @@
PORT = 5005

View File

@@ -4,17 +4,14 @@
"description": "A reuse service providing generic means to add reviews and ratings to target objects, e.g. products.", "description": "A reuse service providing generic means to add reviews and ratings to target objects, e.g. products.",
"repository": "https://github.com/SAP-samples/cloud-cap-samples.git", "repository": "https://github.com/SAP-samples/cloud-cap-samples.git",
"license": "SAP SAMPLE CODE LICENSE", "license": "SAP SAMPLE CODE LICENSE",
"dependencies": {
},
"scripts": {
"start": "cds run --in-memory?",
"watch": "cds watch"
},
"files": [ "files": [
"db", "db",
"srv", "srv",
"index.cds" "index.cds"
], ],
"scripts": {
"watch": "PORT=5005 cds watch"
},
"cds": { "cds": {
"requires": { "requires": {
"db": { "db": {