Compare commits
10 Commits
test-exten
...
graphql
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9536a7f34c | ||
|
|
079620463f | ||
|
|
d53d4de105 | ||
|
|
b13ed5cc8d | ||
|
|
eaaf0d29a5 | ||
|
|
64fe700d1e | ||
|
|
5c3cec973e | ||
|
|
680a6ae68f | ||
|
|
366b0f8f9a | ||
|
|
b95df77b9a |
@@ -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
39
bookstore/package.json
Normal 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
21
bookstore/server.js
Normal 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
|
||||||
@@ -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';
|
||||||
@@ -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
|
|
||||||
})
|
})
|
||||||
|
|
||||||
//
|
//
|
||||||
10
bookstore/srv/swagger-ui.js
Normal file
10
bookstore/srv/swagger-ui.js
Normal 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
|
||||||
|
}
|
||||||
@@ -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
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# cds.requires.messaging.kind = file-based-messaging
|
|
||||||
PORT = 4004
|
|
||||||
@@ -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
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
|||||||
@@ -1,3 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0;url=bookshop/index.html">
|
|
||||||
</head>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0;url=reviews/index.html">
|
|
||||||
</head>
|
|
||||||
@@ -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';
|
|
||||||
|
|||||||
@@ -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"
|
||||||
},
|
},
|
||||||
"db": {
|
|
||||||
"kind": "sql",
|
|
||||||
"[development]": {
|
|
||||||
"model": "db/sqlite"
|
|
||||||
},
|
|
||||||
"[production]": {
|
|
||||||
"model": "db/hana"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"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"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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)))
|
|
||||||
|
|||||||
1
graphql/.env
Normal file
1
graphql/.env
Normal file
@@ -0,0 +1 @@
|
|||||||
|
PORT = 4007
|
||||||
4
graphql/db/data/sap.capire.graphql-Chapters.csv
Normal file
4
graphql/db/data/sap.capire.graphql-Chapters.csv
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
book_ID;number;title
|
||||||
|
201;1;Chapter 1
|
||||||
|
201;2;Chapter 2
|
||||||
|
201;3;Chapter 3
|
||||||
|
26
graphql/db/schema.cds
Normal file
26
graphql/db/schema.cds
Normal 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
7
graphql/examples.http
Normal 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
16
graphql/examples.md
Normal 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
21
graphql/package.json
Normal 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"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
11
graphql/srv/bookshop-service.cds
Normal file
11
graphql/srv/bookshop-service.cds
Normal 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;
|
||||||
|
}
|
||||||
11
graphql/srv/bookshop-service.js
Normal file
11
graphql/srv/bookshop-service.js
Normal 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`)
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
using { OrdersService } from '../../srv/orders-service';
|
using { OrdersService } from '../srv/orders-service';
|
||||||
|
|
||||||
|
|
||||||
@odata.draft.enabled
|
@odata.draft.enabled
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
/*
|
|
||||||
This model controls what gets served to Fiori frontends...
|
|
||||||
*/
|
|
||||||
|
|
||||||
using from './orders/fiori-service';
|
|
||||||
2634
package-lock.json
generated
2634
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -5,14 +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": "^5.5.3"
|
"@sap/cds": "^5.6"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"chai": "^4.3.4",
|
"chai": "^4.3.4",
|
||||||
|
|||||||
@@ -1,2 +1,2 @@
|
|||||||
cds.requires.messaging.kind = file-based-messaging
|
# cds.requires.messaging.kind = file-based-messaging
|
||||||
PORT = 4005
|
PORT = 4005
|
||||||
@@ -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" }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0;url=app/bookshop/index.html">
|
|
||||||
</head>
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0;url=app/reviews/index.html">
|
|
||||||
</head>
|
|
||||||
@@ -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"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -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')))
|
|
||||||
})
|
|
||||||
19
samples.md
19
samples.md
@@ -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
|
||||||
|
|||||||
Reference in New Issue
Block a user