Merge branch 'main' into ql-changes-typo-fix
This commit is contained in:
4
.github/workflows/node.js.yml
vendored
4
.github/workflows/node.js.yml
vendored
@@ -5,9 +5,9 @@ name: CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
pull_request:
|
||||
branches: [ master ]
|
||||
branches: [ main ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
@@ -105,5 +105,5 @@
|
||||
"description": "### Summary\n\nThat's it! You have seen: \n- How to integrate database-specific functions in a CDS model.\n- How to switch between the two implementations for SQLite and SAP HANA."
|
||||
}
|
||||
],
|
||||
"ref": "master"
|
||||
"ref": "main"
|
||||
}
|
||||
@@ -17,7 +17,7 @@ Find here a collection of samples for the [SAP Cloud Application Programming Mod
|
||||
|
||||
### Download
|
||||
|
||||
If you've [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/master.zip).
|
||||
If you've [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/main.zip).
|
||||
|
||||
```sh
|
||||
git clone https://github.com/sap-samples/cloud-cap-samples samples
|
||||
|
||||
@@ -34,7 +34,7 @@
|
||||
<td>{{ book.author }}</td>
|
||||
<td>{{ book.genre.name }}</td>
|
||||
<td class="rating-stars">
|
||||
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }}
|
||||
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }} ({{ book.numberOfReviews }})
|
||||
</td>
|
||||
<td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td>
|
||||
</tr>
|
||||
|
||||
@@ -1 +1,2 @@
|
||||
exports.CatalogService = require('./srv/cat-service')
|
||||
const { CatalogService } = require('./srv/cat-service')
|
||||
module.exports = { CatalogService }
|
||||
|
||||
@@ -6,11 +6,11 @@ using CatalogService from '@capire/bookshop';
|
||||
//
|
||||
annotate CatalogService.Books with @(
|
||||
UI: {
|
||||
HeaderInfo: {
|
||||
HeaderInfo: {
|
||||
TypeName: 'Book',
|
||||
TypeNamePlural: 'Books',
|
||||
Description: {Value: author}
|
||||
},
|
||||
Description: {Value: author}
|
||||
},
|
||||
HeaderFacets: [
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Description}', Target: '@UI.FieldGroup#Descr'},
|
||||
],
|
||||
@@ -38,7 +38,7 @@ annotate CatalogService.Books with @(
|
||||
//
|
||||
annotate CatalogService.Books with @(
|
||||
UI: {
|
||||
SelectionFields: [ ID, price, currency_code ],
|
||||
SelectionFields: [ ID, price, currency_code ],
|
||||
LineItem: [
|
||||
{Value: title},
|
||||
{Value: author, Label:'{i18n>Author}'},
|
||||
|
||||
@@ -38,6 +38,11 @@
|
||||
"[production]": {
|
||||
"model": "db/hana"
|
||||
}
|
||||
},
|
||||
"messaging": {
|
||||
"[development]": { "kind": "file-based-messaging" },
|
||||
"[hybrid]": { "kind": "enterprise-messaging-shared" },
|
||||
"kind": "enterprise-messaging"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,9 +10,11 @@ cds.once('bootstrap',(app)=>{
|
||||
cds.once('served', require('./srv/mashup'))
|
||||
|
||||
// Swagger UI - see https://cap.cloud.sap/docs/advanced/openapi
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ 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;
|
||||
numberOfReviews : Integer;
|
||||
rating : Decimal;
|
||||
}
|
||||
|
||||
|
||||
@@ -41,8 +41,8 @@ module.exports = async()=>{ // called by server.js
|
||||
//
|
||||
ReviewsService.on ('reviewed', (msg) => {
|
||||
console.debug ('> received:', msg.event, msg.data)
|
||||
const { subject, rating } = msg.data
|
||||
return UPDATE(Books,subject).with({rating})
|
||||
const { subject, count, rating } = msg.data
|
||||
return UPDATE(Books,subject).with({ numberOfReviews:count, rating })
|
||||
// ^ Note: the framework will execute this and take care for db.tx
|
||||
})
|
||||
|
||||
|
||||
8215
package-lock.json
generated
8215
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -12,14 +12,13 @@
|
||||
"@capire/media": "./media",
|
||||
"@capire/orders": "./orders",
|
||||
"@capire/reviews": "./reviews",
|
||||
"@sap/cds": "^5.1.5"
|
||||
"@sap/cds": "^5.5.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"cds-swagger-ui-express": "^0.2.0",
|
||||
"chai": "^4.2.0",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-subset": "^1.6.0",
|
||||
"sqlite3": "^5.0.0"
|
||||
"sqlite3": "^5"
|
||||
},
|
||||
"scripts": {
|
||||
"cleanup": "rm -rf node_modules && rm -rf */node_modules && rm -rf */*/node_modules",
|
||||
@@ -35,6 +34,7 @@
|
||||
},
|
||||
"jest": {
|
||||
"testEnvironment": "node",
|
||||
"testTimeout": 20000,
|
||||
"testMatch": [
|
||||
"**/*.test.js"
|
||||
]
|
||||
|
||||
@@ -8,10 +8,11 @@ service ReviewsService {
|
||||
action unlike (review: type of Reviews:ID);
|
||||
|
||||
// Async API
|
||||
event reviewed : {
|
||||
subject: type of Reviews:subject;
|
||||
rating: Decimal(2,1)
|
||||
}
|
||||
event reviewed : {
|
||||
subject : type of Reviews:subject;
|
||||
count : Integer;
|
||||
rating : Decimal;
|
||||
}
|
||||
|
||||
// Input validation
|
||||
annotate Reviews with {
|
||||
|
||||
@@ -12,11 +12,11 @@ module.exports = cds.service.impl (function(){
|
||||
// Emit an event to inform subscribers about new avg ratings for reviewed subjects
|
||||
this.after (['CREATE','UPDATE','DELETE'], 'Reviews', async function(_,req) {
|
||||
const {subject} = req.data
|
||||
const {rating} = await cds.tx(req) .run (
|
||||
SELECT.one (['round(avg(rating),2) as rating']) .from (Reviews) .where ({subject})
|
||||
const { count, rating } = await cds.tx(req) .run (
|
||||
SELECT.one `round(avg(rating),2) as rating, count(*) as count` .from (Reviews) .where ({subject})
|
||||
)
|
||||
global.it || console.log ('< emitting:', 'reviewed', { subject, rating })
|
||||
await this.emit ('reviewed', { subject, rating })
|
||||
global.it || console.log ('< emitting:', 'reviewed', { subject, count, rating })
|
||||
await this.emit ('reviewed', { subject, count, rating })
|
||||
})
|
||||
|
||||
// Increment counter for reviews considered helpful
|
||||
|
||||
3
reviews/test/app/bookshop.html
Normal file
3
reviews/test/app/bookshop.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=app/bookshop/index.html">
|
||||
</head>
|
||||
3
reviews/test/app/reviews.html
Normal file
3
reviews/test/app/reviews.html
Normal file
@@ -0,0 +1,3 @@
|
||||
<head>
|
||||
<meta http-equiv="refresh" content="0;url=app/reviews/index.html">
|
||||
</head>
|
||||
17
reviews/test/package.json
Normal file
17
reviews/test/package.json
Normal file
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"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"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
13
reviews/test/srv/bookshop.cds
Normal file
13
reviews/test/srv/bookshop.cds
Normal file
@@ -0,0 +1,13 @@
|
||||
// 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;
|
||||
}
|
||||
27
reviews/test/srv/bookshop.js
Normal file
27
reviews/test/srv/bookshop.js
Normal file
@@ -0,0 +1,27 @@
|
||||
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')))
|
||||
})
|
||||
@@ -60,11 +60,11 @@ describe('Messaging', ()=>{
|
||||
expect(M).equals(N)
|
||||
expect(received.length).equals(N)
|
||||
expect(received.map(m=>m.data)).to.deep.equal([
|
||||
{ subject: '201', rating: 1 },
|
||||
{ subject: '201', rating: 1.5 },
|
||||
{ subject: '201', rating: 2 },
|
||||
{ subject: '201', rating: 2.5 },
|
||||
{ subject: '201', rating: 3 },
|
||||
{ count: 1, subject: '201', rating: 1 },
|
||||
{ count: 2, subject: '201', rating: 1.5 },
|
||||
{ count: 3, subject: '201', rating: 2 },
|
||||
{ count: 4, subject: '201', rating: 2.5 },
|
||||
{ count: 5, subject: '201', rating: 3 },
|
||||
])
|
||||
})
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user