Compare commits

..

10 Commits

Author SHA1 Message Date
sjvans
9536a7f34c rm server.js (#327) 2022-03-04 12:57:35 +01:00
sjvans
079620463f use dummy-auth 2022-02-24 09:10:01 +01:00
sjvans
d53d4de105 SAP Community Call 2022-02-24 08:30:26 +01:00
sjvans
b13ed5cc8d graphql 2021-11-04 17:31:57 +01:00
Daniel
eaaf0d29a5 Updated package-lock.json 2021-11-04 10:37:33 +01:00
Daniel
64fe700d1e ... 2021-11-04 10:37:33 +01:00
Daniel
5c3cec973e Using bookstore as composite app 2021-11-04 10:37:33 +01:00
Daniel
680a6ae68f Introduced bookstore composite app 2021-11-04 10:37:33 +01:00
Daniel
366b0f8f9a Using flat fiori app folder 2021-11-04 10:37:33 +01:00
Daniel
b95df77b9a Fixed access to cds.entities 2021-11-04 10:37:33 +01:00
47 changed files with 311 additions and 2954 deletions

1
.gitignore vendored
View File

@@ -17,4 +17,3 @@ reviews/msg-box
reviews/db/test.db reviews/db/test.db
*.openapi3.json *.openapi3.json
sqlite.db

View File

@@ -1,7 +1,7 @@
using { Currency, managed, sap, extensible } from '@sap/cds/common'; using { Currency, managed, sap } from '@sap/cds/common';
namespace sap.capire.bookshop; namespace sap.capire.bookshop;
entity Books : managed, extensible { entity Books : managed {
key ID : Integer; key ID : Integer;
title : localized String(111); title : localized String(111);
descr : localized String(1111); descr : localized String(1111);
@@ -13,7 +13,7 @@ entity Books : managed, extensible {
image : LargeBinary @Core.MediaType : 'image/png'; image : LargeBinary @Core.MediaType : 'image/png';
} }
entity Authors : managed, extensible { entity Authors : managed {
key ID : Integer; key ID : Integer;
name : String(111); name : String(111);
dateOfBirth : Date; dateOfBirth : Date;

View File

@@ -1,8 +1,9 @@
const cds = require('@sap/cds') const cds = require('@sap/cds')
const { Books } = cds.entities ('sap.capire.bookshop')
class CatalogService extends cds.ApplicationService { init(){ class CatalogService extends cds.ApplicationService { init(){
const { Books } = cds.entities ('sap.capire.bookshop')
// Reduce stock of ordered books if available stock suffices // Reduce stock of ordered books if available stock suffices
this.on ('submitOrder', async req => { this.on ('submitOrder', async req => {
const {book,quantity} = req.data const {book,quantity} = req.data

39
bookstore/package.json Normal file
View File

@@ -0,0 +1,39 @@
{
"name": "@capire/bookstore",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "*",
"@capire/reviews": "*",
"@capire/orders": "*",
"@capire/common": "*",
"@sap/cds": "^5",
"express": "^4.17.1"
},
"cds": {
"requires": {
"ReviewsService": {
"kind": "odata",
"model": "@capire/reviews"
},
"OrdersService": {
"kind": "odata",
"model": "@capire/orders"
},
"messaging": {
"[development]": { "kind": "file-based-messaging" },
"[hybrid]": { "kind": "enterprise-messaging-shared" },
"[production]": { "kind": "enterprise-messaging" }
},
"db": {
"kind": "sql",
"[development]": {
"model": "db/sqlite"
},
"[production]": {
"model": "db/hana"
}
}
},
"log": { "service": true }
}
}

21
bookstore/server.js Normal file
View File

@@ -0,0 +1,21 @@
const cds = require ('@sap/cds')
// Add mashup logic
cds.once('served', require('./srv/mashup'))
// Add routes to UIs from imported packages
cds.once('bootstrap',(app)=>{
app.serve ('/bookshop') .from ('@capire/bookshop','app/vue')
app.serve ('/reviews') .from ('@capire/reviews','app/vue')
app.serve ('/orders') .from('@capire/orders','app/orders')
})
// Add Swagger UI
require('./srv/swagger-ui')
// Returning cds.server
module.exports = cds.server
// For didactic reasons in capire
const { ReviewsService, OrdersService } = cds.requires
if (!ReviewsService.credentials && !OrdersService.credentials) cds.requires.messaging = false

View File

@@ -1,6 +1,7 @@
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Mashing up imported models... // Enhancing bookshop with Reviews and Orders provided through
// respective reuse packages and services
// //
using { sap.capire.bookshop.Books } from '@capire/bookshop'; using { sap.capire.bookshop.Books } from '@capire/bookshop';
@@ -8,19 +9,22 @@ using { sap.capire.bookshop.Books } from '@capire/bookshop';
// //
// Extend Books with access to Reviews and average ratings // Extend Books with access to Reviews and average ratings
// //
using { ReviewsService.Reviews } from '@capire/reviews'; using { ReviewsService.Reviews } from '@capire/reviews';
extend Books with { extend Books with {
reviews : Composition of many Reviews on reviews.subject = $self.ID; reviews : Composition of many Reviews on reviews.subject = $self.ID;
numberOfReviews : Integer;
rating : Decimal; rating : Decimal;
numberOfReviews : Integer;
} }
// //
// Extend Orders with Books as Products // Extend Orders with Books as Products
// //
using { sap.capire.orders.Orders_Items } from '@capire/orders'; using { sap.capire.orders.Orders_Items } from '@capire/orders';
extend Orders_Items with { extend Orders_Items with {
book : Association to Books on product.ID = book.ID book : Association to Books on product.ID = book.ID
} }
// Add orders fiori app (in case of embedded orders service)
using from '@capire/orders/app/fiori';

View File

@@ -1,6 +1,6 @@
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
// Mashing up provided and required services... // Mashing up bookshop services with required services...
// //
module.exports = async()=>{ // called by server.js module.exports = async()=>{ // called by server.js
@@ -20,7 +20,7 @@ module.exports = async()=>{ // called by server.js
CatalogService.prepend (srv => srv.on ('READ', 'Books/reviews', (req) => { CatalogService.prepend (srv => srv.on ('READ', 'Books/reviews', (req) => {
console.debug ('> delegating request to ReviewsService') console.debug ('> delegating request to ReviewsService')
const [id] = req.params, { columns, limit } = req.query.SELECT const [id] = req.params, { columns, limit } = req.query.SELECT
return ReviewsService.tx(req).read ('Reviews',columns).limit(limit).where({subject:String(id)}) return ReviewsService.read ('Reviews',columns).limit(limit).where({subject:String(id)})
})) }))
// //
@@ -37,13 +37,12 @@ module.exports = async()=>{ // called by server.js
}) })
// //
// Update Books' average ratings when ReviewsService signals updatd reviews // Update Books' average ratings when ReviewsService signals updated reviews
// //
ReviewsService.on ('reviewed', (msg) => { ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data) console.debug ('> received:', msg.event, msg.data)
const { subject, count, rating } = msg.data const { subject, count, rating } = msg.data
return UPDATE(Books,subject).with({ numberOfReviews:count, rating }) return UPDATE(Books,subject).with({ numberOfReviews:count, rating })
// ^ Note: the framework will execute this and take care for db.tx
}) })
// //

View File

@@ -0,0 +1,10 @@
// -----------------------------------------------------------------------
// Adding Swagger UI - see https://cap.cloud.sap/docs/advanced/openapi
const cds = require ('@sap/cds')
try {
const cds_swagger = require ('cds-swagger-ui-express')
cds.once ('bootstrap', app => app.use (cds_swagger()) )
} catch (err) {
if (err.code !== 'MODULE_NOT_FOUND') throw err
}

View File

@@ -2,7 +2,7 @@
@bookshop = http://localhost:4004 @bookshop = http://localhost:4004
@reviews-service = {{bookshop}}/reviews @reviews-service = {{bookshop}}/reviews
# Uncomment this when running a separate reviews service # Uncomment this when running a separate reviews service
@reviews-service = http://localhost:4005/reviews # @reviews-service = http://localhost:4005/reviews

View File

@@ -1,2 +0,0 @@
# cds.requires.messaging.kind = file-based-messaging
PORT = 4004

View File

@@ -1,81 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bookshop</title>
<script>
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
applications: {
"browse-books": {
title: "Browse Books",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=bookshop",
applicationType: "URL",
url: "/browse/webapp",
navigationMode: "embedded",
},
"manage-books": {
title: "Manage Books",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=admin",
applicationType: "URL",
url: "/admin/webapp",
navigationMode: "embedded",
},
"manage-orders": {
title: "Manage Orders",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=orders",
applicationType: "URL",
url: "/orders/webapp",
navigationMode: "embedded",
},
},
bootstrapPlugins: {
RuntimeAuthoringPlugin: {
component: "sap.ushell.plugins.rta",
config: {
validateAppVersion: false,
},
},
PersonalizePlugin: {
component: "sap.ushell.plugins.rta-personalize",
config: {
validateAppVersion: false,
},
},
},
};
</script>
<script
id="sap-ushell-bootstrap"
src="https://sapui5nightly.int.sap.eu2.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"
></script>
<script
id="sap-ui-bootstrap"
src="https://sapui5nightly.int.sap.eu2.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_fiori_3"
data-sap-ui-frameOptions="allow"
data-sap-ui-bindingSyntax="complex"
></script>
<script>
sap.ui
.getCore()
.attachInit(() =>
sap.ushell.Container.createRenderer().placeAt("content")
);
sap.ui
.getCore()
.getConfiguration()
.setFlexibilityServices([{ connector: "SessionStorageConnector" }]);
</script>
</head>
<body class="sapUiBody" id="content"></body>
</html>

View File

@@ -1,4 +1,4 @@
using { AdminService } from '../../db/schema'; using { AdminService } from '@capire/bookshop';
using from '../common'; // to help UI linter get the complete annotations using from '../common'; // to help UI linter get the complete annotations
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////

View File

@@ -22,10 +22,6 @@
} }
}, },
"sap.ui5": { "sap.ui5": {
"flexEnabled": true,
"config": {
"experimentalCAPScenario": true
},
"dependencies": { "dependencies": {
"libs": { "libs": {
"sap.fe.templates": {} "sap.fe.templates": {}

View File

@@ -1,3 +0,0 @@
<head>
<meta http-equiv="refresh" content="0;url=bookshop/index.html">
</head>

View File

@@ -22,7 +22,6 @@
} }
}, },
"sap.ui5": { "sap.ui5": {
"flexEnabled": true,
"dependencies": { "dependencies": {
"libs": { "libs": {
"sap.fe.templates": {} "sap.fe.templates": {}

55
fiori/app/fiori-apps.html Normal file
View File

@@ -0,0 +1,55 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bookshop</title>
<script>
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
applications: {
"browse-books": {
title: "Browse Books",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=bookshop",
applicationType : "URL",
url: "/browse/webapp",
navigationMode: "embedded"
},
"manage-books": {
title: "Manage Books",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=admin",
applicationType : "URL",
url: "/admin/webapp",
navigationMode: "embedded"
},
"manage-orders": {
title: "Manage Orders",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL",
url: "/orders/webapp",
navigationMode: "embedded"
}
}
};
</script>
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" 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_fiori_3"
data-sap-ui-frameOptions="allow"
></script>
<script>
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
</script>
</head>
<body class="sapUiBody" id="content"></body>
</html>

View File

@@ -1,3 +0,0 @@
<head>
<meta http-equiv="refresh" content="0;url=reviews/index.html">
</head>

View File

@@ -6,7 +6,4 @@ using from './admin/fiori-service';
using from './browse/fiori-service'; using from './browse/fiori-service';
using from './common'; using from './common';
using from '@capire/common'; using from '@capire/bookstore/srv/mashup';
// only works in case of embedded orders service
using from '@capire/orders/app/orders/fiori-service';

View File

@@ -2,10 +2,7 @@
"name": "@capire/fiori", "name": "@capire/fiori",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@capire/bookshop": "*", "@capire/bookstore": "*",
"@capire/reviews": "*",
"@capire/orders": "*",
"@capire/common": "*",
"@sap/cds": "^5", "@sap/cds": "^5",
"express": "^4.17.1", "express": "^4.17.1",
"passport": "^0.4.1" "passport": "^0.4.1"
@@ -15,9 +12,6 @@
"watch": "cds watch" "watch": "cds watch"
}, },
"cds": { "cds": {
"hana": {
"deploy-format": "hdbtable"
},
"requires": { "requires": {
"auth": { "auth": {
"strategy": "dummy" "strategy": "dummy"
@@ -30,19 +24,13 @@
"kind": "odata", "kind": "odata",
"model": "@capire/orders" "model": "@capire/orders"
}, },
"extensibility": {
"kind": "uiflex"
},
"db": {
"kind": "sqlite",
"credentials": {
"database": "sqlite.db"
}
},
"messaging": { "messaging": {
"[production]": { "kind": "enterprise-messaging" },
"[development]": { "kind": "file-based-messaging" }, "[development]": { "kind": "file-based-messaging" },
"[hybrid]": { "kind": "enterprise-messaging-shared" }, "[hybrid!]": { "kind": "enterprise-messaging-shared" }
"kind": "enterprise-messaging" },
"hana": {
"deploy-format": "hdbtable"
} }
} }
} }

View File

@@ -1,25 +1 @@
const cds = require ('@sap/cds') module.exports = require('@capire/bookstore/server.js')
module.exports = cds.server
cds.once('bootstrap',(app)=>{
app.use ('/orders/webapp', _from('@capire/orders/app/orders/webapp/manifest.json'))
app.use ('/bookshop', _from('@capire/bookshop/app/vue/index.html'))
app.use ('/reviews', _from('@capire/reviews/app/vue/index.html'))
})
cds.once('served', require('./srv/mashup'))
// Swagger UI - see https://cap.cloud.sap/docs/advanced/openapi
try {
const cds_swagger = require ('cds-swagger-ui-express')
cds.once ('bootstrap', app => app.use (cds_swagger()) )
} catch (err) {
if (err.code !== 'MODULE_NOT_FOUND') throw err
}
// -----------------------------------------------------------------------
// Helper for serving static content from npm-installed packages
const {static} = require('express')
const {dirname} = require('path')
const _from = target => static (dirname (require.resolve(target)))

View File

@@ -1,14 +0,0 @@
# Add integer extension field to Books
POST http://localhost:4004/extensibility/addExtension
Content-Type: application/json
{
"extensions": [
"{\"extend\":\"AdminService.Books\",\"elements\":{\"neuesFeld2\":{\"type\":\"cds.String\", \"default\":\"hallo\"}}}"
]
}
###
# { "extend": "AdminService.Books", "elements":{
# "abc":{"type":"cds.Integer"}
# }},

1
graphql/.env Normal file
View File

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

View File

@@ -0,0 +1,4 @@
book_ID;number;title
201;1;Chapter 1
201;2;Chapter 2
201;3;Chapter 3
1 book_ID number title
2 201 1 Chapter 1
3 201 2 Chapter 2
4 201 3 Chapter 3

26
graphql/db/schema.cds Normal file
View File

@@ -0,0 +1,26 @@
using {
cuid,
managed
} from '@sap/cds/common';
using {sap.capire.bookshop} from '@capire/bookshop';
namespace sap.capire.graphql;
extend bookshop.Books with {
chapters : Composition of many Chapters
on chapters.book = $self;
}
entity Chapters : managed {
key book : Association to bookshop.Books;
key number : Integer;
title : String;
}
entity Orders : cuid, managed {
@mandatory
book : Association to bookshop.Books;
@mandatory
@assert.range : [ 1, 5 ]
quantity : Integer;
}

7
graphql/examples.http Normal file
View File

@@ -0,0 +1,7 @@
# GraphQL
GET http://localhost:4007/graphql?query={BookshopService{Books{title,author{name},chapters{number,title}}}}
###
# OData
GET http://localhost:4007/bookshop/Books?$select=title&$expand=author($select=name),chapters($select=number,title)

16
graphql/examples.md Normal file
View File

@@ -0,0 +1,16 @@
1. open `http://localhost:4007/graphql`
2. paste into left field:
```graphql
{
BookshopService {
Books {
title
chapters {
number
title
}
}
}
}
```
3. press play button

21
graphql/package.json Normal file
View File

@@ -0,0 +1,21 @@
{
"name": "@capire/graphql",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "*",
"@graphql-tools/schema": "^8.3.1",
"@sap/cds": "^5.6",
"express-graphql": "^0.12.0",
"graphql": "^16.0.1"
},
"cds": {
"features": {
"graphql": true
},
"requires": {
"auth": {
"kind": "dummy-auth"
}
}
}
}

View File

@@ -0,0 +1,11 @@
using {
sap.capire.bookshop,
sap.capire.graphql
} from '../db/schema';
service BookshopService {
entity Books as projection on bookshop.Books;
entity Authors as projection on bookshop.Authors;
entity Chapters as projection on graphql.Chapters;
entity Orders as projection on graphql.Orders;
}

View File

@@ -0,0 +1,11 @@
module.exports = function() {
const { Orders, Books } = this.entities
this.before('CREATE', Orders, async function(req) {
const { book_ID, quantity } = req.data
// reduce the stock, if enough are available, else reject the order
const applied = await UPDATE(Books, book_ID).set({ stock: { '-=': quantity } }).where({ stock: { '>=': quantity }})
if (!applied) req.reject(400, `Sorry, ${quantity} are not in stock`)
})
}

View File

@@ -1,58 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bookshop</title>
<script>
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
applications: {
"manage-orders": {
title: "Manage Orders",
description: "... testing FE v42",
additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL",
url: "/orders/webapp",
navigationMode: "embedded"
}
},
bootstrapPlugins: {
RuntimeAuthoringPlugin: {
component: "sap.ushell.plugins.rta",
config: {
validateAppVersion: false,
},
},
PersonalizePlugin: {
component: "sap.ushell.plugins.rta-personalize",
config: {
validateAppVersion: false,
},
},
},
};
</script>
<script id="sap-ushell-bootstrap" src="https://sapui5nightly.int.sap.eu2.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" src="https://sapui5nightly.int.sap.eu2.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_fiori_3"
data-sap-ui-frameOptions="allow"
data-sap-ui-bindingSyntax="complex"
></script>
<script>
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"));
sap.ui
.getCore()
.getConfiguration()
.setFlexibilityServices([{ connector: "SessionStorageConnector" }]);
</script>
</head>
<body class="sapUiBody" id="content"></body>
</html>

View File

@@ -10,7 +10,7 @@
using { OrdersService } from '../../srv/orders-service'; using { OrdersService } from '../srv/orders-service';
@odata.draft.enabled @odata.draft.enabled

View File

@@ -1,5 +0,0 @@
/*
This model controls what gets served to Fiori frontends...
*/
using from './orders/fiori-service';

View File

@@ -0,0 +1,39 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bookshop</title>
<script>
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
applications: {
"manage-orders": {
title: "Manage Orders",
description: "... testing FE v42",
additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL",
url: "/orders/webapp",
navigationMode: "embedded"
}
}
};
</script>
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" 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_fiori_3"
data-sap-ui-frameOptions="allow"
></script>
<script>
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
</script>
</head>
<body class="sapUiBody" id="content"></body>
</html>

View File

@@ -22,10 +22,6 @@
} }
}, },
"sap.ui5": { "sap.ui5": {
"flexEnabled": true,
"config": {
"experimentalCAPScenario": true
},
"dependencies": { "dependencies": {
"libs": { "libs": {
"sap.fe.templates": {} "sap.fe.templates": {}

2637
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -5,15 +5,16 @@
"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",
"dependencies": { "dependencies": {
"@capire/bookstore": "./bookstore",
"@capire/bookshop": "./bookshop", "@capire/bookshop": "./bookshop",
"@capire/common": "./common", "@capire/common": "./common",
"@capire/fiori": "./fiori", "@capire/fiori": "./fiori",
"@capire/graphql": "./graphql",
"@capire/hello": "./hello", "@capire/hello": "./hello",
"@capire/media": "./media", "@capire/media": "./media",
"@capire/orders": "./orders", "@capire/orders": "./orders",
"@capire/reviews": "./reviews", "@capire/reviews": "./reviews",
"@sap/cds": "git+https://github.tools.sap/cap/cds.git", "@sap/cds": "^5.6"
"@sap/cds-dk": "*"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.3.4", "chai": "^4.3.4",

View File

@@ -1,2 +1,2 @@
cds.requires.messaging.kind = file-based-messaging # cds.requires.messaging.kind = file-based-messaging
PORT = 4005 PORT = 4005

View File

@@ -10,15 +10,14 @@
"@sap/cds": "^5", "@sap/cds": "^5",
"express": "^4.17.1" "express": "^4.17.1"
}, },
"scripts": {
"reviews-service": "cds watch",
"books-reviewed": "cds watch ../reviewed"
},
"cds": { "cds": {
"requires": { "requires": {
"db": { "messaging": {
"kind": "sql" "[development]": { "kind": "file-based-messaging" },
} "[hybrid]": { "kind": "enterprise-messaging-shared" },
"[production]": { "kind": "enterprise-messaging" }
},
"db": { "kind": "sql" }
} }
} }
} }

View File

@@ -1,3 +0,0 @@
<head>
<meta http-equiv="refresh" content="0;url=app/bookshop/index.html">
</head>

View File

@@ -1,3 +0,0 @@
<head>
<meta http-equiv="refresh" content="0;url=app/reviews/index.html">
</head>

View File

@@ -1,17 +0,0 @@
{
"name": "@capire/bookshop-with-reviews",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "*",
"@capire/reviews": "*",
"@sap/cds": "^5",
"express": "^4.17.1"
},
"cds": {
"requires": {
"db": {
"kind": "sql"
}
}
}
}

View File

@@ -1,13 +0,0 @@
// Use enhanced implementation for CatalogService
using { CatalogService } from '@capire/bookshop';
annotate CatalogService with @impl:'srv/bookshop.js';
// Extend Books with access to Reviews and average ratings
using { sap.capire.bookshop.Books } from '@capire/bookshop';
using { ReviewsService.Reviews } from '@capire/reviews';
extend Books with {
reviews : Composition of many Reviews on reviews.subject = $self.ID;
rating : Decimal;
numberOfReviews : Integer;
}

View File

@@ -1,27 +0,0 @@
const { CatalogService } = require('@capire/bookshop')
const cds = require ('@sap/cds')
module.exports = class extends CatalogService {async init(){
const { Books } = cds.entities('sap.capire.bookshop')
// Connect to ReviewsService to receive `reviewed` events from it
const ReviewsService = await cds.connect.to ('ReviewsService')
ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { subject, count, rating } = msg.data
return UPDATE(Books,subject).with({ numberOfReviews:count, rating })
})
return super.init()
}}
// -----------------------------------------------------------------------
// Helper for serving static content from npm-installed packages
const {dirname,resolve} = require('path')
const {static} = require('express')
cds.once('listening',()=>{
cds.app.use ('/app/bookshop', static (dirname (require.resolve('@capire/bookshop'))+'/app/vue'))
cds.app.use ('/app/reviews', static (resolve (__dirname, '../../app/vue')))
})

View File

@@ -51,21 +51,28 @@ Each sub directory essentially is an individual npm package arranged in an [all-
- As well as managed data, input validations, and authorization - As well as managed data, input validations, and authorization
## [@capire/fiori](fiori) ## [@capire/bookstore](bookstore)
- A [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages: - A [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages:
- [@capire/bookshop](bookshop) - [@capire/bookshop](bookshop)
- [@capire/reviews](reviews) - [@capire/reviews](reviews)
- [@capire/orders](orders) - [@capire/orders](orders)
- [@capire/common](common) - [@capire/common](common)
- [Adds an SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to:
- [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files
- Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)
- Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)
- Serving SAP Fiori apps locally
- [The Vue.js app](bookshop/app/vue) imported from bookshop is served as well - [The Vue.js app](bookshop/app/vue) imported from bookshop is served as well
- [The Vue.js app](reviews/app/vue) imported from reviews is served as well
- [The Fiori app](orders/app) imported from orders is served as well
- [OpenAPI export + Swagger UI](https://cap.cloud.sap/docs/advanced/openapi) - [OpenAPI export + Swagger UI](https://cap.cloud.sap/docs/advanced/openapi)
## [@capire/fiori](fiori)
- [Adds an SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to:
- [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files
- Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)
- Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)
- Serving SAP Fiori apps locally
<br> <br>
# All-in-one Monorepo # All-in-one Monorepo