Compare commits
5 Commits
data-load
...
cds.contex
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6de9c7d839 | ||
|
|
c3e35cd54c | ||
|
|
9fe79b28d6 | ||
|
|
81897a3d7e | ||
|
|
9e45ac2f0c |
@@ -7,6 +7,6 @@ module.exports = cds.service.impl (function(){
|
|||||||
|
|
||||||
/** Generate primary keys for target entity in request */
|
/** Generate primary keys for target entity in request */
|
||||||
async function genid (req) {
|
async function genid (req) {
|
||||||
const {ID} = await cds.tx(req).run (SELECT.one.from(req.target).columns('max(ID) as ID'))
|
const {ID} = await SELECT.one.from(req.target).columns('max(ID) as ID')
|
||||||
req.data.ID = ID - ID % 100 + 100 + 1
|
req.data.ID = ID - ID % 100 + 100 + 1
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,25 +1,30 @@
|
|||||||
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(){
|
||||||
|
|
||||||
|
// Reflect entities from model
|
||||||
|
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,amount} = req.data, tx = cds.tx(req)
|
const {book,amount} = req.data
|
||||||
let {stock} = await tx.read('stock').from(Books,book)
|
// Read stock from database
|
||||||
|
let {stock} = await SELECT.from (Books, book, b => b.stock)
|
||||||
if (stock >= amount) {
|
if (stock >= amount) {
|
||||||
await tx.update (Books,book).with ({ stock: stock -= amount })
|
// Reduce stock by ordered amount
|
||||||
|
await UPDATE (Books,book) .with ({ stock: stock -= amount })
|
||||||
|
// Emit event to inform others
|
||||||
await this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
|
await this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
|
||||||
return { stock }
|
// Return reduced stock to caller
|
||||||
|
return req.reply ({ stock })
|
||||||
}
|
}
|
||||||
|
// Return error about insufficient stock
|
||||||
else return req.error (409,`${amount} exceeds stock for book #${book}`)
|
else return req.error (409,`${amount} exceeds stock for book #${book}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add some discount for overstocked books
|
// Add some discount for overstocked books
|
||||||
this.after ('READ','Books', each => {
|
this.after ('READ','Books', each => {
|
||||||
if (each.stock > 111) {
|
if (each.stock > 111) each.title += ` -- 11% discount!`
|
||||||
each.title += ` -- 11% discount!`
|
|
||||||
}
|
|
||||||
})
|
})
|
||||||
|
|
||||||
return super.init()
|
return super.init()
|
||||||
|
|||||||
@@ -5,13 +5,17 @@
|
|||||||
module.exports = async()=>{ // called by server.js
|
module.exports = async()=>{ // called by server.js
|
||||||
|
|
||||||
const cds = require('@sap/cds')
|
const cds = require('@sap/cds')
|
||||||
|
|
||||||
|
// Connect to services to mashup
|
||||||
const CatalogService = await cds.connect.to ('CatalogService')
|
const CatalogService = await cds.connect.to ('CatalogService')
|
||||||
const ReviewsService = await cds.connect.to ('ReviewsService')
|
const ReviewsService = await cds.connect.to ('ReviewsService')
|
||||||
const OrdersService = await cds.connect.to ('OrdersService')
|
const OrdersService = await cds.connect.to ('OrdersService')
|
||||||
const db = await cds.connect.to ('db')
|
const db = await cds.connect.to ('db')
|
||||||
|
|
||||||
// reflect entity definitions used below...
|
// Reflect entity definitions used below...
|
||||||
const { Books } = db.entities ('sap.capire.bookshop')
|
const { Books } = db.entities ('sap.capire.bookshop')
|
||||||
|
const { Orders } = OrdersService.entities
|
||||||
|
const { Reviews } = ReviewsService.entities
|
||||||
|
|
||||||
//
|
//
|
||||||
// Delegate requests to read reviews to the ReviewsService
|
// Delegate requests to read reviews to the ReviewsService
|
||||||
@@ -20,7 +24,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 SELECT.from (Reviews,columns).limit(limit).where({subject:String(id)})
|
||||||
}))
|
}))
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -28,8 +32,9 @@ module.exports = async()=>{ // called by server.js
|
|||||||
//
|
//
|
||||||
CatalogService.on ('OrderedBook', async (msg) => {
|
CatalogService.on ('OrderedBook', async (msg) => {
|
||||||
const { book, amount, buyer } = msg.data
|
const { book, amount, buyer } = msg.data
|
||||||
const { title, price } = await db.tx(msg).read (Books, book, b => { b.title, b.price })
|
const { title, price } = await SELECT.from (Books, book, b => { b.title, b.price })
|
||||||
return OrdersService.tx(msg).create ('Orders').entries({
|
// FIXME: Fails due to Draft glitches when OrdersService is remote
|
||||||
|
return INSERT.into (Orders).entries({
|
||||||
OrderNo: 'Order at '+ (new Date).toLocaleString(),
|
OrderNo: 'Order at '+ (new Date).toLocaleString(),
|
||||||
Items: [{ product:{ID:`${book}`}, title, price, amount }],
|
Items: [{ product:{ID:`${book}`}, title, price, amount }],
|
||||||
buyer, createdBy: buyer
|
buyer, createdBy: buyer
|
||||||
@@ -42,12 +47,11 @@ module.exports = async()=>{ // called by server.js
|
|||||||
ReviewsService.on ('reviewed', (msg) => {
|
ReviewsService.on ('reviewed', (msg) => {
|
||||||
console.debug ('> received:', msg.event, msg.data)
|
console.debug ('> received:', msg.event, msg.data)
|
||||||
const { subject, rating } = msg.data
|
const { subject, rating } = msg.data
|
||||||
return UPDATE(Books,subject).with({rating})
|
return UPDATE (Books,subject) .with ({rating})
|
||||||
// ^ Note: the framework will execute this and take care for db.tx
|
|
||||||
})
|
})
|
||||||
|
|
||||||
//
|
//
|
||||||
// Reduce stock of ordered books for orders are created from Orders admin UI
|
// Reduce stock of ordered books when orders are modified in admin UI
|
||||||
//
|
//
|
||||||
OrdersService.on ('OrderChanged', (msg) => {
|
OrdersService.on ('OrderChanged', (msg) => {
|
||||||
console.debug ('> received:', msg.event, msg.data)
|
console.debug ('> received:', msg.event, msg.data)
|
||||||
@@ -56,4 +60,5 @@ module.exports = async()=>{ // called by server.js
|
|||||||
.and ('stock >=', deltaAmount)
|
.and ('stock >=', deltaAmount)
|
||||||
.set ('stock -=', deltaAmount)
|
.set ('stock -=', deltaAmount)
|
||||||
})
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ entity Orders_Items {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/** This is a stand-in for arbitrary ordered Products */
|
/** This is a stand-in for arbitrary ordered Products */
|
||||||
entity Products @(cds.persistence.skip:'always') {
|
entity Products @(cds.persistence.skip:'always',cds.autoexpose) {
|
||||||
key ID : String;
|
key ID : String;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -8,18 +8,14 @@ class OrdersService extends cds.ApplicationService {
|
|||||||
this.before ('UPDATE', 'Orders', async function(req) {
|
this.before ('UPDATE', 'Orders', async function(req) {
|
||||||
const { ID, Items } = req.data
|
const { ID, Items } = req.data
|
||||||
if (Items) for (let { product_ID, amount } of Items) {
|
if (Items) for (let { product_ID, amount } of Items) {
|
||||||
const { amount:before } = await cds.tx(req).run (
|
const { amount:before } = await SELECT.one.from (OrderItems, oi => oi.amount) .where ({up__ID:ID, product_ID})
|
||||||
SELECT.one.from (OrderItems, oi => oi.amount) .where ({up__ID:ID, product_ID})
|
|
||||||
)
|
|
||||||
if (amount != before) await this.orderChanged (product_ID, amount-before)
|
if (amount != before) await this.orderChanged (product_ID, amount-before)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
this.before ('DELETE', 'Orders', async function(req) {
|
this.before ('DELETE', 'Orders', async function(req) {
|
||||||
const { ID } = req.data
|
const { ID } = req.data
|
||||||
const Items = await cds.tx(req).run (
|
const Items = await SELECT.from (OrderItems, oi => { oi.product_ID, oi.amount }) .where ({up__ID:ID})
|
||||||
SELECT.from (OrderItems, oi => { oi.product_ID, oi.amount }) .where ({up__ID:ID})
|
|
||||||
)
|
|
||||||
if (Items) await Promise.all (Items.map(it => this.orderChanged (it.product_ID, -it.amount)))
|
if (Items) await Promise.all (Items.map(it => this.orderChanged (it.product_ID, -it.amount)))
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|||||||
@@ -31,6 +31,9 @@
|
|||||||
"mocha": {
|
"mocha": {
|
||||||
"parallel": true
|
"parallel": true
|
||||||
},
|
},
|
||||||
|
"engines": {
|
||||||
|
"node": ">= 12.18"
|
||||||
|
},
|
||||||
"jest": {
|
"jest": {
|
||||||
"testEnvironment": "node"
|
"testEnvironment": "node"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -12,9 +12,7 @@ module.exports = cds.service.impl (function(){
|
|||||||
// Emit an event to inform subscribers about new avg ratings for reviewed subjects
|
// Emit an event to inform subscribers about new avg ratings for reviewed subjects
|
||||||
this.after (['CREATE','UPDATE','DELETE'], 'Reviews', async function(_,req) {
|
this.after (['CREATE','UPDATE','DELETE'], 'Reviews', async function(_,req) {
|
||||||
const {subject} = req.data
|
const {subject} = req.data
|
||||||
const {rating} = await cds.tx(req) .run (
|
const {rating} = await SELECT.one (['round(avg(rating),2) as rating']) .from (Reviews) .where ({subject})
|
||||||
SELECT.one (['round(avg(rating),2) as rating']) .from (Reviews) .where ({subject})
|
|
||||||
)
|
|
||||||
global.it || console.log ('< emitting:', 'reviewed', { subject, rating })
|
global.it || console.log ('< emitting:', 'reviewed', { subject, rating })
|
||||||
await this.emit ('reviewed', { subject, rating })
|
await this.emit ('reviewed', { subject, rating })
|
||||||
})
|
})
|
||||||
@@ -23,8 +21,7 @@ module.exports = cds.service.impl (function(){
|
|||||||
this.on ('like', (req) => {
|
this.on ('like', (req) => {
|
||||||
if (!req.user) return req.reject(400, 'You must be identified to like a review')
|
if (!req.user) return req.reject(400, 'You must be identified to like a review')
|
||||||
const {review} = req.data, {user} = req
|
const {review} = req.data, {user} = req
|
||||||
const tx = cds.tx(req)
|
return cds.run ([
|
||||||
return tx.run ([
|
|
||||||
INSERT.into (Likes) .entries ({review_ID: review, user: user.id}),
|
INSERT.into (Likes) .entries ({review_ID: review, user: user.id}),
|
||||||
UPDATE (Reviews) .set({liked: {'+=': 1}}) .where({ID:review})
|
UPDATE (Reviews) .set({liked: {'+=': 1}}) .where({ID:review})
|
||||||
]).catch(() => req.reject(400, 'You already liked that review'))
|
]).catch(() => req.reject(400, 'You already liked that review'))
|
||||||
@@ -34,9 +31,8 @@ module.exports = cds.service.impl (function(){
|
|||||||
this.on ('unlike', async (req) => {
|
this.on ('unlike', async (req) => {
|
||||||
if (!req.user) return req.reject(400, 'You must be identified to remove a former like of yours')
|
if (!req.user) return req.reject(400, 'You must be identified to remove a former like of yours')
|
||||||
const {review} = req.data, {user} = req
|
const {review} = req.data, {user} = req
|
||||||
const tx = cds.tx(req)
|
const affectedRows = await DELETE.from (Likes) .where ({review_ID: review,user: user.id})
|
||||||
const affectedRows = await tx.run (DELETE.from (Likes) .where ({review_ID: review,user: user.id}))
|
if (affectedRows === 1) return UPDATE (Reviews) .set ({liked: {'-=': 1}}) .where ({ID:review})
|
||||||
if (affectedRows === 1) return tx.run (UPDATE (Reviews) .set ({liked: {'-=': 1}}) .where ({ID:review}))
|
|
||||||
})
|
})
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|||||||
Reference in New Issue
Block a user