Compare commits
2 Commits
CAA265-nod
...
CAA265-nod
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9ae57008c5 | ||
|
|
2c1079bc5b |
@@ -1,3 +1,3 @@
|
|||||||
# Final state of exercise 4 for CAA265 - Rapid Service Development with SAP Cloud Application Programming Model
|
# Final state of exercise 1 for CAA265 - Rapid Service Development with SAP Cloud Application Programming Model
|
||||||
|
|
||||||
**DO NOT MERGE IN MASTER**
|
**DO NOT MERGE IN MASTER**
|
||||||
|
|||||||
@@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"cds_version": "^3.17.4"
|
"cds_version": "^3.17.4"
|
||||||
}
|
}
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
/* global hasher */
|
|
||||||
sap.ui.define([
|
|
||||||
"sap/fe/AppComponent"
|
|
||||||
], function (AppComponent) {
|
|
||||||
"use strict";
|
|
||||||
|
|
||||||
return AppComponent.extend("ns.bookList.Component", {
|
|
||||||
metadata: {
|
|
||||||
"manifest": "json"
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
@@ -1,118 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
|
||||||
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
|
|
||||||
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
|
|
||||||
version="2.5"
|
|
||||||
xmlns="http://java.sun.com/xml/ns/javaee">
|
|
||||||
|
|
||||||
<display-name>OData v4</display-name>
|
|
||||||
|
|
||||||
<!-- ============================================================== -->
|
|
||||||
<!-- UI5 resource servlet used to handle application resources -->
|
|
||||||
<!-- ============================================================== -->
|
|
||||||
|
|
||||||
<servlet>
|
|
||||||
<display-name>ResourceServlet</display-name>
|
|
||||||
<servlet-name>ResourceServlet</servlet-name>
|
|
||||||
<servlet-class>com.sap.ui5.resource.ResourceServlet</servlet-class>
|
|
||||||
</servlet>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>ResourceServlet</servlet-name>
|
|
||||||
<url-pattern>/resources/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>ResourceServlet</servlet-name>
|
|
||||||
<url-pattern>/test-resources/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
<!-- enable CORS -->
|
|
||||||
<context-param>
|
|
||||||
<param-name>com.sap.ui5.resource.ACCEPTED_ORIGINS</param-name>
|
|
||||||
<param-value>*</param-value>
|
|
||||||
</context-param>
|
|
||||||
|
|
||||||
<!-- BEGIN: DEV MODE -->
|
|
||||||
<!-- DEV MODE switched off by default and can be switched on during development -->
|
|
||||||
<!-- but has to be switched off for productive use on a Java server! -->
|
|
||||||
<context-param>
|
|
||||||
<param-name>com.sap.ui5.resource.DEV_MODE</param-name>
|
|
||||||
<param-value>true</param-value>
|
|
||||||
</context-param>
|
|
||||||
<!-- END: DEV MODE -->
|
|
||||||
|
|
||||||
|
|
||||||
<!-- ============================================================== -->
|
|
||||||
<!-- Cache Control Filter to prevent caching of any resource -->
|
|
||||||
<!-- ============================================================== -->
|
|
||||||
|
|
||||||
<filter>
|
|
||||||
<display-name>CacheControlFilter</display-name>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<filter-class>com.sap.ui5.resource.CacheControlFilter</filter-class>
|
|
||||||
</filter>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.html</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.js</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.css</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.json</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.xml</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.gif</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.png</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.jpg</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.properties</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
<filter-mapping>
|
|
||||||
<filter-name>CacheControlFilter</filter-name>
|
|
||||||
<url-pattern>*.tmpl</url-pattern>
|
|
||||||
</filter-mapping>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- ============================================================== -->
|
|
||||||
<!-- UI5 proxy servlet -->
|
|
||||||
<!-- ============================================================== -->
|
|
||||||
|
|
||||||
<servlet>
|
|
||||||
<servlet-name>SimpleProxyServlet</servlet-name>
|
|
||||||
<servlet-class>com.sap.ui5.proxy.SimpleProxyServlet</servlet-class>
|
|
||||||
</servlet>
|
|
||||||
<servlet-mapping>
|
|
||||||
<servlet-name>SimpleProxyServlet</servlet-name>
|
|
||||||
<url-pattern>/proxy/*</url-pattern>
|
|
||||||
</servlet-mapping>
|
|
||||||
|
|
||||||
|
|
||||||
<!-- ============================================================== -->
|
|
||||||
<!-- Welcome file list -->
|
|
||||||
<!-- ============================================================== -->
|
|
||||||
|
|
||||||
<welcome-file-list>
|
|
||||||
<welcome-file>test.html</welcome-file>
|
|
||||||
</welcome-file-list>
|
|
||||||
|
|
||||||
|
|
||||||
</web-app>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
# This is the resource bundle for bookList#Texts for manifest.json
|
|
||||||
|
|
||||||
#XTIT: Application name
|
|
||||||
appTitle=bookList
|
|
||||||
|
|
||||||
#YDES: Application description
|
|
||||||
appDescription=bookList
|
|
||||||
@@ -1,39 +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>{{appTitle}}</title>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window["sap-ushell-config"] = {
|
|
||||||
defaultRenderer: "fiori2",
|
|
||||||
applications: {
|
|
||||||
"fe-lrop-v4": {
|
|
||||||
title: "bookList",
|
|
||||||
description: "bookList",
|
|
||||||
additionalInformation: "SAPUI5.Component=ns.bookList",
|
|
||||||
applicationType : "URL",
|
|
||||||
url: "./",
|
|
||||||
navigationMode: "embedded"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
|
||||||
<script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
|
|
||||||
data-sap-ui-libs="sap.m, sap.ushell"
|
|
||||||
data-sap-ui-compatVersion="edge"
|
|
||||||
data-sap-ui-theme="sap_belize"
|
|
||||||
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>
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
{
|
|
||||||
"_version": "1.15.0",
|
|
||||||
"sap.app": {
|
|
||||||
"id": "ns.bookList",
|
|
||||||
"type": "application",
|
|
||||||
"i18n": "i18n/i18n.properties",
|
|
||||||
"applicationVersion": {
|
|
||||||
"version": "1.0"
|
|
||||||
},
|
|
||||||
"title": "{{appTitle}}",
|
|
||||||
"description": "{{appDescription}}",
|
|
||||||
"ach": "CA-UI5-FE",
|
|
||||||
"dataSources": {
|
|
||||||
"mainService": {
|
|
||||||
"uri": "catalog/",
|
|
||||||
"type": "OData",
|
|
||||||
"settings": {
|
|
||||||
"odataVersion": "4.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"offline": false,
|
|
||||||
"resources": "resources.json",
|
|
||||||
"sourceTemplate": {
|
|
||||||
"id": "ui5template.fiorielements.v4.lrop",
|
|
||||||
"version": "1.0.0"
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sap.ui": {
|
|
||||||
"technology": "UI5",
|
|
||||||
"icons": {
|
|
||||||
"icon": "",
|
|
||||||
"favIcon": "",
|
|
||||||
"phone": "",
|
|
||||||
"phone@2": "",
|
|
||||||
"tablet": "",
|
|
||||||
"tablet@2": ""
|
|
||||||
},
|
|
||||||
"deviceTypes": {
|
|
||||||
"desktop": true,
|
|
||||||
"tablet": true,
|
|
||||||
"phone": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sap.ui5": {
|
|
||||||
"resources": {
|
|
||||||
"js": [],
|
|
||||||
"css": []
|
|
||||||
},
|
|
||||||
"dependencies": {
|
|
||||||
"minUI5Version": "1.60.1",
|
|
||||||
"libs": {
|
|
||||||
"sap.fe": {}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"models": {
|
|
||||||
"i18n": {
|
|
||||||
"type": "sap.ui.model.resource.ResourceModel",
|
|
||||||
"uri": "i18n/i18n.properties"
|
|
||||||
},
|
|
||||||
"": {
|
|
||||||
"dataSource": "mainService",
|
|
||||||
"settings": {
|
|
||||||
"synchronizationMode": "None",
|
|
||||||
"operationMode": "Server",
|
|
||||||
"autoExpandSelect": true,
|
|
||||||
"earlyRequests": true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
"routing": {
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"pattern": "",
|
|
||||||
"name": "BooksList",
|
|
||||||
"target": "BooksList"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"pattern": "Books({key})",
|
|
||||||
"name": "BooksObjectPage",
|
|
||||||
"target": "BooksObjectPage"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"targets": {
|
|
||||||
"BooksList": {
|
|
||||||
"type": "Component",
|
|
||||||
"id": "BooksList",
|
|
||||||
"name": "sap.fe.templates.ListReport",
|
|
||||||
"options": {
|
|
||||||
"settings" : {
|
|
||||||
"entitySet" : "Books",
|
|
||||||
"variantManagement": "Page",
|
|
||||||
"navigation" : {
|
|
||||||
"Books" : {
|
|
||||||
"detail" : {
|
|
||||||
"route" : "BooksObjectPage"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"BooksObjectPage": {
|
|
||||||
"type": "Component",
|
|
||||||
"id": "BooksObjectPage",
|
|
||||||
"name": "sap.fe.templates.ObjectPage",
|
|
||||||
"options": {
|
|
||||||
"settings" : {
|
|
||||||
"entitySet" : "Books"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"contentDensities": {
|
|
||||||
"compact": true,
|
|
||||||
"cozy": true
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"sap.platform.abap": {
|
|
||||||
"_version": "1.1.0",
|
|
||||||
"uri": ""
|
|
||||||
},
|
|
||||||
"sap.platform.hcp": {
|
|
||||||
"_version": "1.1.0",
|
|
||||||
"uri": ""
|
|
||||||
},
|
|
||||||
"sap.fiori": {
|
|
||||||
"_version": "1.1.0",
|
|
||||||
"registrationIds": [],
|
|
||||||
"archeType": "transactional"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"welcomeFile": "/index.html",
|
|
||||||
"authenticationMethod": "none",
|
|
||||||
"logout": {
|
|
||||||
"logoutEndpoint": "/do/logout"
|
|
||||||
},
|
|
||||||
"routes": [
|
|
||||||
{
|
|
||||||
"source": "^/catalog/(.*)$",
|
|
||||||
"target": "$1",
|
|
||||||
"destination": "srv-api",
|
|
||||||
"csrfProtection": false,
|
|
||||||
"authenticationType": "none"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"source": "^(.*)$",
|
|
||||||
"target": "$1",
|
|
||||||
"service": "html5-apps-repo-rt",
|
|
||||||
"authenticationType": "xsuaa"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1,66 +0,0 @@
|
|||||||
using sap.capire.bookstore.CatalogService as CatalogService from '../srv/services';
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Books Lists
|
|
||||||
//
|
|
||||||
annotate CatalogService.Books with @(
|
|
||||||
UI: {
|
|
||||||
HeaderFacets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: 'Description', Target: '@UI.FieldGroup#Descr'},
|
|
||||||
],
|
|
||||||
Facets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: 'Details', Target: '@UI.FieldGroup#Price'},
|
|
||||||
],
|
|
||||||
FieldGroup#Descr: {
|
|
||||||
Data: [
|
|
||||||
{Value: descr},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
FieldGroup#Price: {
|
|
||||||
Data: [
|
|
||||||
{Value: price},
|
|
||||||
{Value: currency.symbol, Label: 'Currency'},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
Identification: [{Value:title}],
|
|
||||||
SelectionFields: [ ID, price, currency_code ],
|
|
||||||
LineItem: [
|
|
||||||
{Value: ID},
|
|
||||||
{Value: title},
|
|
||||||
{Value: author_ID, Label:'Author ID'},
|
|
||||||
{Value: stock},
|
|
||||||
{Value: price},
|
|
||||||
{Value: currency.symbol, Label:''},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Books Details
|
|
||||||
//
|
|
||||||
annotate CatalogService.Books with @(
|
|
||||||
UI: {
|
|
||||||
HeaderInfo: {
|
|
||||||
TypeName: 'Book',
|
|
||||||
TypeNamePlural: 'Books',
|
|
||||||
Title: {Value: title},
|
|
||||||
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Books Elements
|
|
||||||
//
|
|
||||||
annotate CatalogService.Books with {
|
|
||||||
ID @title:'ID' @UI.HiddenFilter;
|
|
||||||
title @title:'Title';
|
|
||||||
author @title:'Author ID';
|
|
||||||
price @title:'Price';
|
|
||||||
stock @title:'Stock';
|
|
||||||
descr @UI.MultiLineText;
|
|
||||||
}
|
|
||||||
@@ -9,7 +9,6 @@
|
|||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@sap/capire-products": "^1.0.0",
|
"@sap/capire-products": "^1.0.0",
|
||||||
"reviews-service": "file:packages/reviews-service-1.0.0.tgz",
|
|
||||||
"@sap/cds": "^3.17.4",
|
"@sap/cds": "^3.17.4",
|
||||||
"express": "^4.17.1"
|
"express": "^4.17.1"
|
||||||
},
|
},
|
||||||
@@ -17,16 +16,5 @@
|
|||||||
"build": "cds build/all --clean",
|
"build": "cds build/all --clean",
|
||||||
"deploy": "cds deploy",
|
"deploy": "cds deploy",
|
||||||
"start": "cds run"
|
"start": "cds run"
|
||||||
},
|
|
||||||
"cds": {
|
|
||||||
"requires": {
|
|
||||||
"sap.capire.reviews.ReviewsService": {
|
|
||||||
"model": "reviews-service",
|
|
||||||
"kind": "odata",
|
|
||||||
"credentials": {
|
|
||||||
"file": "default"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,11 +14,4 @@ service CatalogService {
|
|||||||
using { sap.capire.products.AdminService } from '@sap/capire-products';
|
using { sap.capire.products.AdminService } from '@sap/capire-products';
|
||||||
extend service AdminService with {
|
extend service AdminService with {
|
||||||
entity Authors as projection on my.Authors;
|
entity Authors as projection on my.Authors;
|
||||||
}
|
|
||||||
|
|
||||||
// Adding reviews via capire-reviews service
|
|
||||||
using { sap.capire.reviews.ReviewsService as external} from 'reviews-service';
|
|
||||||
|
|
||||||
extend service CatalogService {
|
|
||||||
@readonly entity Reviews @(cds.persistence.skip) as projection on external.Reviews;
|
|
||||||
}
|
}
|
||||||
@@ -15,21 +15,4 @@ module.exports = async (srv) => {
|
|||||||
}
|
}
|
||||||
})))
|
})))
|
||||||
})
|
})
|
||||||
|
|
||||||
const reviews_srv = await cds.connect.to('sap.capire.reviews.ReviewsService')
|
|
||||||
|
|
||||||
// react on event messages from reviews service
|
|
||||||
reviews_srv.on('reviewed', (msg) => {
|
|
||||||
console.debug('> received', msg)
|
|
||||||
})
|
|
||||||
|
|
||||||
// delegate requests to reviews service
|
|
||||||
srv.on('READ', 'Reviews', async (req) => {
|
|
||||||
const { Reviews } = reviews_srv.entities
|
|
||||||
|
|
||||||
const tx = reviews_srv.transaction(req)
|
|
||||||
const results = await tx.read(Reviews)
|
|
||||||
|
|
||||||
return results
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
Binary file not shown.
@@ -1,6 +0,0 @@
|
|||||||
{
|
|
||||||
"build": {
|
|
||||||
"target": ".",
|
|
||||||
"tasks": []
|
|
||||||
}
|
|
||||||
}
|
|
||||||
15
packages/reviews-service/.gitignore
vendored
15
packages/reviews-service/.gitignore
vendored
@@ -1,15 +0,0 @@
|
|||||||
.che/
|
|
||||||
.gen/
|
|
||||||
gen/
|
|
||||||
mta_archives/
|
|
||||||
node_modules/
|
|
||||||
target/
|
|
||||||
|
|
||||||
.cds_gen.log
|
|
||||||
connection.properties
|
|
||||||
*.db
|
|
||||||
.DS_Store
|
|
||||||
*.orig
|
|
||||||
_out
|
|
||||||
default-*.json
|
|
||||||
package-lock.json
|
|
||||||
16
packages/reviews-service/.vscode/launch.json
vendored
16
packages/reviews-service/.vscode/launch.json
vendored
@@ -1,16 +0,0 @@
|
|||||||
{
|
|
||||||
// Use IntelliSense to learn about possible attributes.
|
|
||||||
// Hover to view descriptions of existing attributes.
|
|
||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
|
||||||
"version": "0.2.0",
|
|
||||||
"configurations": [
|
|
||||||
{
|
|
||||||
"name": "cds run",
|
|
||||||
"request": "launch",
|
|
||||||
"type": "node", "runtimeExecutable": "npx","runtimeArgs": [ "-n" ],
|
|
||||||
"args": [ "--","cds","run","--in-memory?" ], // the leading "--" arg ensures it works with as well as without debugging
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"skipFiles": [ "<node_internals>/**" ]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,30 +0,0 @@
|
|||||||
namespace sap.capire.reviews;
|
|
||||||
using { User } from '@sap/cds/common';
|
|
||||||
|
|
||||||
// Reviewed subjects can be any entity that is uniquely identified
|
|
||||||
type ReviewedSubject : String(111);
|
|
||||||
|
|
||||||
entity Reviews {
|
|
||||||
key ID : String(36);
|
|
||||||
subject : ReviewedSubject;
|
|
||||||
reviewer : User;
|
|
||||||
rating : Rating;
|
|
||||||
title : String(111);
|
|
||||||
text : String(1111);
|
|
||||||
date : DateTime;
|
|
||||||
likes : Composition of many Likes on likes.review = $self;
|
|
||||||
liked : Integer default 0; // counter for likes as helpful review (count of all _likes belonging to this review)
|
|
||||||
}
|
|
||||||
|
|
||||||
type Rating : Integer enum {
|
|
||||||
Best = 5;
|
|
||||||
Good = 4;
|
|
||||||
Avg = 3;
|
|
||||||
Poor = 2;
|
|
||||||
Worst = 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity Likes {
|
|
||||||
key review : Association to Reviews;
|
|
||||||
key user : User;
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
using from './srv/reviews-service';
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "reviews-service",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "Generated by cds init",
|
|
||||||
"repository": "<Add your repository here>",
|
|
||||||
"license": "ISC",
|
|
||||||
"dependencies": {
|
|
||||||
"@sap/cds": "^3.17.4",
|
|
||||||
"express": "^4.17.1"
|
|
||||||
},
|
|
||||||
"engines": {
|
|
||||||
"node": "^8.9"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"build": "cds build/all --clean",
|
|
||||||
"deploy": "cds deploy",
|
|
||||||
"start": "cds run"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
namespace sap.capire.reviews;
|
|
||||||
using { sap.capire.reviews as my } from '../db/schema';
|
|
||||||
|
|
||||||
service ReviewsService {
|
|
||||||
|
|
||||||
event reviewed : { subject:String; rating: Decimal(2,1) };
|
|
||||||
|
|
||||||
// API
|
|
||||||
entity Reviews as projection on my.Reviews excluding { likes }
|
|
||||||
action like (review:Reviews.ID);
|
|
||||||
action unlike (review:Reviews.ID);
|
|
||||||
|
|
||||||
// Input validation
|
|
||||||
annotate Reviews with {
|
|
||||||
subject @mandatory;
|
|
||||||
title @mandatory;
|
|
||||||
rating @mandatory @assert.enum;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Auto-fill reviewers and review dates
|
|
||||||
annotate Reviews with {
|
|
||||||
reviewer @cds.on.insert:$user;
|
|
||||||
date @cds.on.insert:$now;
|
|
||||||
date @cds.on.update:$now;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,40 +0,0 @@
|
|||||||
const cds = require('@sap/cds')
|
|
||||||
module.exports = cds.service.impl((srv) => {
|
|
||||||
|
|
||||||
// Get the CSN definition for Reviews from the db schema for sub-sequent queries
|
|
||||||
// ( Note: we explicitly specify the namespace to support embedded reuse )
|
|
||||||
const { Reviews, Likes } = cds.entities('sap.capire.reviews')
|
|
||||||
|
|
||||||
// Increment counter for reviews considered helpful
|
|
||||||
srv.on ('like', (req) => {
|
|
||||||
if (!req.user) return req.reject(400, 'You must be identified to like a review')
|
|
||||||
const {review} = req.data, {user} = req
|
|
||||||
const tx = cds.transaction(req)
|
|
||||||
return tx.run ([
|
|
||||||
INSERT.into (Likes) .entries ({review_ID: review, user: user.id}),
|
|
||||||
UPDATE (Reviews) .set({liked: {'+=': 1}}) .where({ID:review})
|
|
||||||
]).catch(() => req.reject(400, 'You already liked that review'))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Delete a former like by the same user
|
|
||||||
srv.on('unlike', async (req) => {
|
|
||||||
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 tx = cds.transaction(req)
|
|
||||||
const affectedRows = await tx.run(DELETE.from(Likes).where({ review_ID: review, user: user.id }))
|
|
||||||
if (affectedRows === 1) return tx.run(UPDATE(Reviews).set({ liked: { '-=': 1 } }).where({ ID: review }))
|
|
||||||
})
|
|
||||||
|
|
||||||
// Emit an event to inform subscribers about new avg ratings for reviewed subjects
|
|
||||||
// ( Note: req.on.succeeded ensures we only do that if there's no error )
|
|
||||||
srv.after(['CREATE', 'UPDATE', 'DELETE'], 'Reviews', async (_, req) => {
|
|
||||||
const { subject } = req.data
|
|
||||||
const { rating } = await cds.transaction(req).run(
|
|
||||||
SELECT.one(['avg(rating) as rating']).from(Reviews).where({ subject })
|
|
||||||
)
|
|
||||||
req.on('succeeded', () => {
|
|
||||||
srv.emit('reviewed', { subject, rating })
|
|
||||||
console.log(`Reviewed event was emitted for book "${subject}" with rating ${rating}.`)
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
Reference in New Issue
Block a user