Compare commits
69 Commits
data-load
...
d034457-UI
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
e08cdf7831 | ||
|
|
1ec44aaecf | ||
|
|
a6ea2fb130 | ||
|
|
1fde0c928b | ||
|
|
fa854a2411 | ||
|
|
40b6acee6a | ||
|
|
a37f1b8e0c | ||
|
|
042ddec41c | ||
|
|
8d8aa6c2c9 | ||
|
|
756ed48ce3 | ||
|
|
19b680ab3d | ||
|
|
33380c0792 | ||
|
|
0d5b65d93f | ||
|
|
dcf5ec6068 | ||
|
|
fb41dd100b | ||
|
|
769aa2b4f5 | ||
|
|
f6c5e620c0 | ||
|
|
7013cb6296 | ||
|
|
6411ce4ea3 | ||
|
|
cd914a8d87 | ||
|
|
b35e92b5bc | ||
|
|
8350aff111 | ||
|
|
9aa604c5a0 | ||
|
|
f5cb53adf8 | ||
|
|
9fe16edc55 | ||
|
|
0093883f25 | ||
|
|
14024a4837 | ||
|
|
2e13b37096 | ||
|
|
7407b0aae3 | ||
|
|
152d088ff4 | ||
|
|
19c886d6dc | ||
|
|
575cffe70a | ||
|
|
03869e5896 | ||
|
|
597afeb3d8 | ||
|
|
d376e6e063 | ||
|
|
2a44cfa9dc | ||
|
|
7964c9aefd | ||
|
|
ce752097d0 | ||
|
|
e838f380c5 | ||
|
|
e89e012eba | ||
|
|
bf0f57efe5 | ||
|
|
40ea057f7f | ||
|
|
31d2f4c932 | ||
|
|
8cc2db7118 | ||
|
|
482b71e60b | ||
|
|
7536451e29 | ||
|
|
2703e8b999 | ||
|
|
ac2e4dd6f9 | ||
|
|
c718fa400d | ||
|
|
730da6e435 | ||
|
|
7d9b4f7a5d | ||
|
|
35d3708592 | ||
|
|
eaaf0d29a5 | ||
|
|
64fe700d1e | ||
|
|
5c3cec973e | ||
|
|
680a6ae68f | ||
|
|
366b0f8f9a | ||
|
|
b95df77b9a | ||
|
|
7f6b87171a | ||
|
|
21e74bbbfb | ||
|
|
102b15c3cd | ||
|
|
73db2e96bc | ||
|
|
7bb58ee2d5 | ||
|
|
27e82d16e0 | ||
|
|
ff9bbe6d8d | ||
|
|
5f1b7b8cbf | ||
|
|
0253300557 | ||
|
|
139d957495 | ||
|
|
404427237b |
@@ -1,18 +1,24 @@
|
|||||||
const { exec } = require ('child_process')
|
const { exec } = require ('child_process')
|
||||||
|
const isWin = process.platform === 'win32'
|
||||||
const express = require ('express')
|
const express = require ('express')
|
||||||
const fs = require ('fs')
|
const fs = require ('fs')
|
||||||
const app = express()
|
const app = express()
|
||||||
|
|
||||||
const { PORT=4444 } = process.env
|
const { PORT=4444 } = process.env
|
||||||
const [,,port=PORT] = process.argv
|
const [,,port=PORT,scope='@capire'] = process.argv
|
||||||
const cwd = __dirname
|
const cwd = __dirname
|
||||||
|
|
||||||
|
// clean up on start (exit handler might not complete on Windows)
|
||||||
|
exec(isWin ? 'del *.tgz' : 'rm *.tgz', {cwd})
|
||||||
|
|
||||||
|
|
||||||
app.use('/-/:tarball', (req,res,next) => {
|
app.use('/-/:tarball', (req,res,next) => {
|
||||||
console.debug ('GET', req.params)
|
console.debug ('GET', req.params)
|
||||||
try {
|
try {
|
||||||
const { tarball } = req.params
|
const { tarball } = req.params
|
||||||
const [, pkg ] = /^capire-(\w+)/.exec(tarball)
|
const [, pkg ] = /^\w+-(\w+)/.exec(tarball)
|
||||||
fs.lstat(tarball,(err => {
|
fs.lstat(tarball,(err => {
|
||||||
|
if (err) console.debug (`npm pack ../${pkg}`)
|
||||||
if (err) exec(`npm pack ../${pkg}`,{cwd},next)
|
if (err) exec(`npm pack ../${pkg}`,{cwd},next)
|
||||||
else next()
|
else next()
|
||||||
}))
|
}))
|
||||||
@@ -25,12 +31,14 @@ app.use('/-/:tarball', (req,res,next) => {
|
|||||||
app.use('/-', express.static(__dirname))
|
app.use('/-', express.static(__dirname))
|
||||||
|
|
||||||
app.get('/*', (req,res)=>{
|
app.get('/*', (req,res)=>{
|
||||||
|
const urlRegex = /^\/(@\w+)\/(\w+)/
|
||||||
const url = decodeURIComponent(req.url)
|
const url = decodeURIComponent(req.url)
|
||||||
console.debug ('GET',url)
|
console.debug ('GET',url)
|
||||||
try {
|
try {
|
||||||
const [, capire, pkg ] = /^\/(@capire)\/(\w+)/.exec(url)
|
if (!urlRegex.test(url)) return res.sendStatus(404)
|
||||||
const package = require (`${capire}/${pkg}/package.json`)
|
const [, scpe, pkg ] = urlRegex.exec(url)
|
||||||
const tarball = `capire-${pkg}-${package.version}.tgz`
|
const package = require (`${scpe}/${pkg}/package.json`)
|
||||||
|
const tarball = `${scpe.slice(1)}-${pkg}-${package.version}.tgz`
|
||||||
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
|
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
|
||||||
res.json({
|
res.json({
|
||||||
"name": package.name,
|
"name": package.name,
|
||||||
@@ -42,29 +50,30 @@ app.get('/*', (req,res)=>{
|
|||||||
"name": package.name,
|
"name": package.name,
|
||||||
"version": package.version,
|
"version": package.version,
|
||||||
"dist": {
|
"dist": {
|
||||||
"tarball": `http://localhost:${port}/-/${tarball}`
|
"tarball": `${server.url}/-/${tarball}`
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error(e)
|
if (e.code === 'MODULE_NOT_FOUND') return res.sendStatus(404)
|
||||||
res.sendStatus(404)
|
console.error(e); throw e
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
app.listen(port, ()=>{
|
const server = app.listen(port, ()=>{
|
||||||
console.log (`npm set @capire:registry=http://localhost:${port}`)
|
const url = server.url = `http://localhost:${server.address().port}`
|
||||||
console.log (`@capire registry listening on http://localhost:${port}`)
|
console.log (`npm set ${scope}:registry=${url}`)
|
||||||
exec(`npm set @capire:registry=http://localhost:${port}`)
|
exec(`npm set ${scope}:registry=${url}`)
|
||||||
|
console.log (`${scope} registry listening on ${url}`)
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
const _exit = ()=>{
|
const _exit = ()=>{
|
||||||
console.log ('\nnpm conf rm @capire:registry')
|
server.close()
|
||||||
exec('npm conf rm @capire:registry')
|
exec(`npm conf rm "${scope}:registry"`, ()=> { process.exit() })
|
||||||
exec('rm *.tgz')
|
|
||||||
process.exit()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
process.on ('SIGTERM',_exit)
|
process.on ('SIGTERM',_exit)
|
||||||
process.on ('SIGHUP',_exit)
|
process.on ('SIGHUP',_exit)
|
||||||
process.on ('SIGINT',_exit)
|
process.on ('SIGINT',_exit)
|
||||||
|
|||||||
@@ -68,26 +68,35 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "fiori/package.json",
|
"file": "fiori/package.json",
|
||||||
"description": "#### Configuration\n\nThe `cds` section in `package.json` is a place to configure which of the `db/sqlite` and `db/hana` folders are used for which database.\nWe use [Node.js profiles](https://cap.cloud.sap/docs/node.js/cds-env#profiles) to separate the configuration.\nIn the `development` profile, you can see that `db/sqlite` is set as the model, while the `db/hana` folder is configured in the `production` profile.",
|
"description": "#### Configuration\n\nThe `cds.requires` section in `package.json` is a place to configure which of the `db/sqlite` and `db/hana` folders are used for which database.\n\nWe use [Node.js profiles](https://cap.cloud.sap/docs/node.js/cds-env#profiles) to separate the configuration.\nIn the `development` profile, you can see that `db/sqlite` is set as the model, while the `db/hana` folder is configured in the `production` profile. `db-ext` is a pseudo datasource, its name doesn't matter.\n\nSee [`cds.resolve`](https://cap.cloud.sap/docs/node.js/cds-compile#cds-resolve) to learn more about how models are found.",
|
||||||
"line": 17,
|
"selection": {
|
||||||
|
"start": {
|
||||||
|
"line": 41,
|
||||||
|
"character": 1
|
||||||
|
},
|
||||||
|
"end": {
|
||||||
|
"line": 48,
|
||||||
|
"character": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
"title": "Configuration"
|
"title": "Configuration"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "fiori/package.json",
|
"file": "fiori/package.json",
|
||||||
"description": "#### Run with SQLite\n\nTo run with `development` and an in-memory SQLite database, you don't need to do anything special, because it's activated by default. Just run:\n\n>> cds watch fiori\n\nThen open [http://localhost:4004/admin/Authors](http://localhost:4004/admin/Authors) to see the two new fields.\n",
|
"description": "#### Run with SQLite\n\nTo run with `development` and an in-memory SQLite database, you don't need to do anything special, because it's activated by default. Just run:\n\n>> cds watch fiori\n\nThen open [http://localhost:4004/admin/Authors](http://localhost:4004/admin/Authors) to see the two new fields.\n",
|
||||||
"line": 28,
|
"line": 43,
|
||||||
"title": "Run with SQLite"
|
"title": "Run with SQLite"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "fiori/package.json",
|
"file": "fiori/package.json",
|
||||||
"description": "#### Deploy the CDS Model to SAP HANA\n\nTo 'activate' SAP HANA through the `production` profile, you can use the global `--production` flag:\n\n>> cd fiori; cds deploy --to hana --production\n\n[Learn more about SAP HANA deployment](https://cap.cloud.sap/docs/guides/databases#get-hana)\n\n#### Run the Application\n\n>> cd fiori; cds watch --production\n\nThe service on [http://localhost:4004/admin/Authors](http://localhost:4004/admin/Authors) is the same as before, but this time the `Authors` entity is backed by a database view with an SAP HANA function.\n\n#### More\n\nIf you don't see data, you can add some in the next step.",
|
"description": "#### Deploy the CDS Model to SAP HANA\n\nTo 'activate' SAP HANA through the `production` profile, you can use the global `--production` flag:\n\n>> cd fiori; cds deploy --to hana --production\n\n[Learn more about SAP HANA deployment](https://cap.cloud.sap/docs/guides/databases#get-hana)\n\n#### Run the Application\n\n>> cd fiori; cds watch --production\n\nThe service on [http://localhost:4004/admin/Authors](http://localhost:4004/admin/Authors) is the same as before, but this time the `Authors` entity is backed by a database view with an SAP HANA function.\n\n#### More\n\nIf you don't see data, you can add some in the next step.",
|
||||||
"line": 31,
|
"line": 46,
|
||||||
"title": "Run with SAP HANA"
|
"title": "Run with SAP HANA"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "fiori/test/requests.http",
|
"file": "fiori/test/requests.http",
|
||||||
"description": "### Add More Data\n\nOptionally you can add some `Authors` data by clicking on the _Send Request_ link (provided by the [REST client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension).",
|
"description": "### Add More Data\n\nOptionally you can add some `Authors` data by clicking on the _Send Request_ link (provided by the [REST client](https://marketplace.visualstudio.com/items?itemName=humao.rest-client) extension).",
|
||||||
"line": 68,
|
"line": 72,
|
||||||
"selection": {
|
"selection": {
|
||||||
"start": {
|
"start": {
|
||||||
"line": 67,
|
"line": 67,
|
||||||
@@ -104,6 +113,5 @@
|
|||||||
"title": "Wrap-up",
|
"title": "Wrap-up",
|
||||||
"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."
|
"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": "main"
|
|
||||||
}
|
}
|
||||||
@@ -19,7 +19,7 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "hello/world.cds",
|
"file": "hello/srv/world.cds",
|
||||||
"description": "### Hello World!\n\nThis is a simplistic [Hello World](https://cap.cloud.sap/docs/get-started/hello-world) service using [CDS](https://cap.cloud.sap/docs/cds/) and [cds.services](https://cap.cloud.sap/docs/node.js/api#services-api).",
|
"description": "### Hello World!\n\nThis is a simplistic [Hello World](https://cap.cloud.sap/docs/get-started/hello-world) service using [CDS](https://cap.cloud.sap/docs/cds/) and [cds.services](https://cap.cloud.sap/docs/node.js/api#services-api).",
|
||||||
"line": 2,
|
"line": 2,
|
||||||
"selection": {
|
"selection": {
|
||||||
@@ -68,7 +68,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "orders/db/schema.cds",
|
"file": "orders/db/schema.cds",
|
||||||
"description": "### Compositions and Serving Documents\n\nA standalone orders management service, demonstrating:\n- Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with\n- [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data)\n",
|
"description": "### Orders - Compositions and Serving Documents\n\nA standalone orders management service, demonstrating:\n- Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with\n- [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data)\n",
|
||||||
"line": 1,
|
"line": 1,
|
||||||
"selection": {
|
"selection": {
|
||||||
"start": {
|
"start": {
|
||||||
@@ -84,7 +84,7 @@
|
|||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "reviews/db/schema.cds",
|
"file": "reviews/db/schema.cds",
|
||||||
"description": "### More Modularity\n\nShows how to implement a modular service to manage product reviews, including:\n- Consuming other services synchronously and asynchronously\n- Serving requests synchronously\n- Emitting events asynchronously\n- Grow as you go, with:\n- Mocking app services\n- Running service meshes\n- Late-cut Micro Services\n- As well as managed data, input validations, and authorization\n",
|
"description": "### Reviews - More Modularity\n\nShows how to implement a modular service to manage product reviews, including:\n- Consuming other services synchronously and asynchronously\n- Serving requests synchronously\n- Emitting events asynchronously\n- Grow as you go, with:\n- Mocking app services\n- Running service meshes\n- Late-cut Micro Services\n- As well as managed data, input validations, and authorization\n",
|
||||||
"line": 1,
|
"line": 1,
|
||||||
"selection": {
|
"selection": {
|
||||||
"start": {
|
"start": {
|
||||||
@@ -99,8 +99,12 @@
|
|||||||
"title": "Reviews"
|
"title": "Reviews"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"file": "fiori/app/index.cds",
|
"title": "Bookstore",
|
||||||
"description": "### Annotations for SAP Fiori Elements\n\nA [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages:\n - [@capire/bookshop](bookshop)\n - [@capire/reviews](reviews)\n - [@capire/orders](orders)\n - [@capire/common](common)\n\n[Adds a SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to:\n - [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files\n - Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)\n - Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)\n - Serving SAP Fiori apps locally\n\n[The Vue.js app](bookshop/app/vue) imported from bookshop is served as well.\n",
|
"description": "### Bookstore - Reuse and UI\n\n- A [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/reuse-and-compose) these packages:\n - [@capire/bookshop](bookshop)\n - [@capire/reviews](reviews)\n - [@capire/orders](orders)\n - [@capire/common](common)\n- [The Vue.js app](bookshop/app/vue) imported from bookshop is served as well\n- [The Vue.js app](reviews/app/vue) imported from reviews is served as well\n- [The Fiori app](orders/app) imported from orders is served as well\n- [OpenAPI export + Swagger UI](https://cap.cloud.sap/docs/advanced/openapi)"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"file": "fiori/app/services.cds",
|
||||||
|
"description": "### Annotations for SAP Fiori Elements\n\n- [Adds an SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookstore, thereby introducing to:\n- [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files\n- Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)\n- Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)\n- Serving SAP Fiori apps locally\n",
|
||||||
"line": 1,
|
"line": 1,
|
||||||
"selection": {
|
"selection": {
|
||||||
"start": {
|
"start": {
|
||||||
@@ -117,14 +121,13 @@
|
|||||||
{
|
{
|
||||||
"file": "package.json",
|
"file": "package.json",
|
||||||
"description": "### All-in-one Monorepo\n\nEach sample sub directory essentially is a standard npm package, some with standard npm dependencies to other samples. The root folder's [package.json](package.json) has local links to the sub folders, such that an `npm install` populates a local `node_modules` folder acts like a local npm registry to the individual sample packages.\n",
|
"description": "### All-in-one Monorepo\n\nEach sample sub directory essentially is a standard npm package, some with standard npm dependencies to other samples. The root folder's [package.json](package.json) has local links to the sub folders, such that an `npm install` populates a local `node_modules` folder acts like a local npm registry to the individual sample packages.\n",
|
||||||
"line": 8,
|
|
||||||
"selection": {
|
"selection": {
|
||||||
"start": {
|
"start": {
|
||||||
"line": 8,
|
"line": 8,
|
||||||
"character": 1
|
"character": 1
|
||||||
},
|
},
|
||||||
"end": {
|
"end": {
|
||||||
"line": 15,
|
"line": 16,
|
||||||
"character": 1
|
"character": 1
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -34,7 +34,7 @@
|
|||||||
<td>{{ book.author }}</td>
|
<td>{{ book.author }}</td>
|
||||||
<td>{{ book.genre.name }}</td>
|
<td>{{ book.genre.name }}</td>
|
||||||
<td class="rating-stars">
|
<td class="rating-stars">
|
||||||
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }}
|
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }} ({{ book.numberOfReviews }})
|
||||||
</td>
|
</td>
|
||||||
<td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td>
|
<td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td>
|
||||||
</tr>
|
</tr>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
using { Currency, managed, sap } from '@sap/cds/common';
|
using { Currency, managed, sap, extensible } from '@sap/cds/common';
|
||||||
namespace sap.capire.bookshop;
|
namespace sap.capire.bookshop;
|
||||||
|
|
||||||
entity Books : managed {
|
entity Books : managed, extensible {
|
||||||
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 {
|
|||||||
image : LargeBinary @Core.MediaType : 'image/png';
|
image : LargeBinary @Core.MediaType : 'image/png';
|
||||||
}
|
}
|
||||||
|
|
||||||
entity Authors : managed {
|
entity Authors : managed, extensible {
|
||||||
key ID : Integer;
|
key ID : Integer;
|
||||||
name : String(111);
|
name : String(111);
|
||||||
dateOfBirth : Date;
|
dateOfBirth : Date;
|
||||||
|
|||||||
@@ -1 +1,2 @@
|
|||||||
exports.CatalogService = require('./srv/cat-service')
|
const { CatalogService } = require('./srv/cat-service')
|
||||||
|
module.exports = { CatalogService }
|
||||||
|
|||||||
@@ -1,18 +1,18 @@
|
|||||||
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
|
||||||
|
if (quantity < 1) return req.reject (400,`quantity has to be 1 or more`)
|
||||||
let {stock} = await SELECT `stock` .from (Books,book)
|
let {stock} = await SELECT `stock` .from (Books,book)
|
||||||
if (stock >= quantity) {
|
if (quantity > stock) return req.reject (409,`${quantity} exceeds stock for book #${book}`)
|
||||||
await UPDATE (Books,book) .with (`stock -=`, quantity)
|
await UPDATE (Books,book) .with ({ stock: stock -= quantity })
|
||||||
await this.emit ('OrderedBook', { book, quantity, buyer:req.user.id })
|
await this.emit ('OrderedBook', { book, quantity, buyer:req.user.id })
|
||||||
return { stock }
|
return { stock }
|
||||||
}
|
|
||||||
else return req.error (409,`${quantity} exceeds stock for book #${book}`)
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// Add some discount for overstocked books
|
// Add some discount for overstocked books
|
||||||
|
|||||||
@@ -32,6 +32,19 @@ GET {{server}}/admin/Authors?
|
|||||||
# &sap-language=de
|
# &sap-language=de
|
||||||
Authorization: Basic alice:
|
Authorization: Basic alice:
|
||||||
|
|
||||||
|
### ------------------------------------------------------------------------
|
||||||
|
# Create Author
|
||||||
|
POST {{server}}/admin/Authors
|
||||||
|
Content-Type: application/json;IEEE754Compatible=true
|
||||||
|
Authorization: Basic alice:
|
||||||
|
|
||||||
|
{
|
||||||
|
"ID": 112,
|
||||||
|
"name": "Shakespeeeeere",
|
||||||
|
"age": 22
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
### ------------------------------------------------------------------------
|
||||||
# Create book
|
# Create book
|
||||||
POST {{server}}/admin/Books
|
POST {{server}}/admin/Books
|
||||||
|
|||||||
2
bookstore/index.cds
Normal file
2
bookstore/index.cds
Normal file
@@ -0,0 +1,2 @@
|
|||||||
|
namespace sap.capire.bookshop; //> important for reflection
|
||||||
|
using from './srv/mashup';
|
||||||
33
bookstore/package.json
Normal file
33
bookstore/package.json
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
{
|
||||||
|
"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"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"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,18 +9,24 @@ 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;
|
||||||
rating : Decimal;
|
rating : Decimal;
|
||||||
|
numberOfReviews : Integer;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
//
|
//
|
||||||
// Extend Orders with Books as Products
|
// Extend Orders with Books as Products
|
||||||
//
|
//
|
||||||
|
using { sap.capire.orders.Orders } from '@capire/orders';
|
||||||
using { sap.capire.orders.Orders_Items } from '@capire/orders';
|
extend Orders with {
|
||||||
extend Orders_Items with {
|
extend 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, rating } = msg.data
|
const { subject, count, rating } = msg.data
|
||||||
return UPDATE(Books,subject).with({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
|
|
||||||
47
fiori/app/admin-authors/fiori-service.cds
Normal file
47
fiori/app/admin-authors/fiori-service.cds
Normal file
@@ -0,0 +1,47 @@
|
|||||||
|
using {AdminService} from '@capire/bookshop';
|
||||||
|
|
||||||
|
annotate AdminService.Authors with @odata.draft.enabled;
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Authors Object Page
|
||||||
|
//
|
||||||
|
annotate AdminService.Authors with @(UI : {
|
||||||
|
HeaderInfo : {
|
||||||
|
TypeName : 'Author',
|
||||||
|
TypeNamePlural : 'Authors',
|
||||||
|
Description : {Value : lifetime}
|
||||||
|
},
|
||||||
|
Facets : [
|
||||||
|
{
|
||||||
|
$Type : 'UI.ReferenceFacet',
|
||||||
|
Label : '{i18n>Details}',
|
||||||
|
Target : '@UI.FieldGroup#Details'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$Type : 'UI.ReferenceFacet',
|
||||||
|
Label : '{i18n>Books}',
|
||||||
|
Target : 'books/@UI.LineItem'
|
||||||
|
},
|
||||||
|
],
|
||||||
|
FieldGroup #Details : {Data : [
|
||||||
|
{Value : placeOfBirth},
|
||||||
|
{Value : placeOfDeath},
|
||||||
|
{Value : dateOfBirth},
|
||||||
|
{Value : dateOfDeath},
|
||||||
|
{
|
||||||
|
Value : age,
|
||||||
|
Label : '{i18n>Age}'
|
||||||
|
},
|
||||||
|
]},
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
// Workaround to avoid errors for unknown db-specific calculated fields above
|
||||||
|
extend sap.capire.bookshop.Authors with {
|
||||||
|
virtual age : Integer;
|
||||||
|
virtual lifetime : String;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Workaround for Fiori popup for asking user to enter a new UUID on Create
|
||||||
|
annotate AdminService.Authors with { ID @Core.Computed; }
|
||||||
7
fiori/app/admin-authors/webapp/Component.js
Normal file
7
fiori/app/admin-authors/webapp/Component.js
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
sap.ui.define(["sap/fe/core/AppComponent"], function (AppComponent) {
|
||||||
|
"use strict";
|
||||||
|
return AppComponent.extend("authors.Component", {
|
||||||
|
metadata: { manifest: "json" },
|
||||||
|
});
|
||||||
|
});
|
||||||
|
/* eslint no-undef:0 */
|
||||||
11
fiori/app/admin-authors/webapp/i18n/i18n.properties
Normal file
11
fiori/app/admin-authors/webapp/i18n/i18n.properties
Normal file
@@ -0,0 +1,11 @@
|
|||||||
|
# This is the resource bundle of itelo
|
||||||
|
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
|
||||||
|
|
||||||
|
# JCI app descriptor contains lower case TITLE
|
||||||
|
appTitle=Bookshop Authors
|
||||||
|
|
||||||
|
# JCI app descriptor contains lower case DESCRIPTION
|
||||||
|
appSubTitle=Bookshop Authors
|
||||||
|
|
||||||
|
# JCI app descriptor contains lower case DESCRIPTION
|
||||||
|
appDescription=Bookshop Authors
|
||||||
145
fiori/app/admin-authors/webapp/manifest.json
Normal file
145
fiori/app/admin-authors/webapp/manifest.json
Normal file
@@ -0,0 +1,145 @@
|
|||||||
|
{
|
||||||
|
"_version": "1.28.0",
|
||||||
|
"sap.app": {
|
||||||
|
"id": "authors",
|
||||||
|
"type": "application",
|
||||||
|
"title": "Manage Authors",
|
||||||
|
"description": "Sample Application",
|
||||||
|
"i18n": "i18n/i18n.properties",
|
||||||
|
"applicationVersion": {
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"dataSources": {
|
||||||
|
"AdminService": {
|
||||||
|
"uri": "/admin/",
|
||||||
|
"type": "OData",
|
||||||
|
"settings": {
|
||||||
|
"odataVersion": "4.0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sourceTemplate": {
|
||||||
|
"id": "ui5template.basicSAPUI5ApplicationProject",
|
||||||
|
"-id": "ui5template.smartTemplate",
|
||||||
|
"version": "1.40.12"
|
||||||
|
},
|
||||||
|
"crossNavigation": {
|
||||||
|
"inbounds": {
|
||||||
|
"intent1": {
|
||||||
|
"signature": {
|
||||||
|
"parameters": {
|
||||||
|
"Books.author.ID":{
|
||||||
|
"renameTo": "ID"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalParameters": "ignored"
|
||||||
|
},
|
||||||
|
"semanticObject": "Authors",
|
||||||
|
"action": "display",
|
||||||
|
"title": "{{appTitle}}",
|
||||||
|
"info": "{{appInfo}}",
|
||||||
|
"subTitle": "{{appSubTitle}}",
|
||||||
|
"icon": "sap-icon://SAP-icons-TNT/user",
|
||||||
|
"indicatorDataSource": {
|
||||||
|
"dataSource": "AdminService",
|
||||||
|
"path": "Authors/$count",
|
||||||
|
"refresh": 1800
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sap.ui5": {
|
||||||
|
"flexEnabled": true,
|
||||||
|
"config": {
|
||||||
|
"experimentalCAPScenario": true
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"minUI5Version": "1.81.0",
|
||||||
|
"libs": {
|
||||||
|
"sap.fe.templates": {}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"models": {
|
||||||
|
"i18n": {
|
||||||
|
"type": "sap.ui.model.resource.ResourceModel",
|
||||||
|
"uri": "i18n/i18n.properties"
|
||||||
|
},
|
||||||
|
"": {
|
||||||
|
"dataSource": "AdminService",
|
||||||
|
"settings": {
|
||||||
|
"synchronizationMode": "None",
|
||||||
|
"operationMode": "Server",
|
||||||
|
"autoExpandSelect": true,
|
||||||
|
"earlyRequests": true,
|
||||||
|
"groupProperties": {
|
||||||
|
"default": {
|
||||||
|
"submit": "Auto"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"routing": {
|
||||||
|
"routes": [
|
||||||
|
{
|
||||||
|
"pattern": ":?query:",
|
||||||
|
"name": "AuthorsList",
|
||||||
|
"target": "AuthorsList"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"pattern": "Authors({key}):?query:",
|
||||||
|
"name": "AuthorsDetails",
|
||||||
|
"target": "AuthorsDetails"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"targets": {
|
||||||
|
"AuthorsList": {
|
||||||
|
"type": "Component",
|
||||||
|
"id": "AuthorsList",
|
||||||
|
"name": "sap.fe.templates.ListReport",
|
||||||
|
"options": {
|
||||||
|
"settings": {
|
||||||
|
"entitySet": "Authors",
|
||||||
|
"initialLoad": true,
|
||||||
|
"navigation": {
|
||||||
|
"Authors": {
|
||||||
|
"detail": {
|
||||||
|
"route": "AuthorsDetails"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"AuthorsDetails": {
|
||||||
|
"type": "Component",
|
||||||
|
"id": "AuthorsDetailsList",
|
||||||
|
"name": "sap.fe.templates.ObjectPage",
|
||||||
|
"options": {
|
||||||
|
"settings": {
|
||||||
|
"entitySet": "Authors"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"contentDensities": {
|
||||||
|
"compact": true,
|
||||||
|
"cozy": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sap.ui": {
|
||||||
|
"technology": "UI5",
|
||||||
|
"fullWidth": false,
|
||||||
|
"deviceTypes":{
|
||||||
|
"desktop": true,
|
||||||
|
"tablet": true,
|
||||||
|
"phone": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"sap.fiori": {
|
||||||
|
"registrationIds": [],
|
||||||
|
"archeType": "transactional"
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using { AdminService } from '../../db/schema';
|
using { AdminService } from '@capire/bookstore';
|
||||||
using from '../common'; // to help UI linter get the complete annotations
|
using from '../common'; // to help UI linter get the complete annotations
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -40,27 +40,6 @@ annotate AdminService.Books with @(
|
|||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
annotate AdminService.Authors with @(
|
|
||||||
UI: {
|
|
||||||
HeaderInfo: {
|
|
||||||
Description: {Value: lifetime}
|
|
||||||
},
|
|
||||||
Facets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Books}', Target: 'books/@UI.LineItem'},
|
|
||||||
],
|
|
||||||
FieldGroup#Details: {
|
|
||||||
Data: [
|
|
||||||
{Value: placeOfBirth},
|
|
||||||
{Value: placeOfDeath},
|
|
||||||
{Value: dateOfBirth},
|
|
||||||
{Value: dateOfDeath},
|
|
||||||
{Value: age, Label: '{i18n>Age}'},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////
|
||||||
@@ -85,10 +64,14 @@ annotate AdminService.Books.texts with @(
|
|||||||
|
|
||||||
// Add Value Help for Locales
|
// Add Value Help for Locales
|
||||||
annotate AdminService.Books.texts {
|
annotate AdminService.Books.texts {
|
||||||
locale @ValueList:{entity:'Languages',type:#fixed}
|
locale @ValueList:{entity:'Languages'};
|
||||||
|
locale @Common.ValueListWithFixedValues:true; //show as drop down, not a dialog
|
||||||
}
|
}
|
||||||
// In addition we need to expose Languages through AdminService as a target for ValueList
|
// In addition we need to expose Languages through AdminService as a target for ValueList
|
||||||
using { sap } from '@sap/cds/common';
|
using { sap } from '@sap/cds/common';
|
||||||
extend service AdminService {
|
extend service AdminService {
|
||||||
@readonly entity Languages as projection on sap.common.Languages;
|
@readonly entity Languages as projection on sap.common.Languages;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Workaround for Fiori popup for asking user to enter a new UUID on Create
|
||||||
|
annotate AdminService.Books with { ID @Core.Computed; }
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
|
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
|
||||||
"use strict";
|
"use strict";
|
||||||
return AppComponent.extend("admin.Component", {
|
return AppComponent.extend("books.Component", {
|
||||||
metadata: { manifest: "json" }
|
metadata: { manifest: "json" }
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -1,7 +1,7 @@
|
|||||||
{
|
{
|
||||||
"_version": "1.8.0",
|
"_version": "1.8.0",
|
||||||
"sap.app": {
|
"sap.app": {
|
||||||
"id": "admin",
|
"id": "books",
|
||||||
"type": "application",
|
"type": "application",
|
||||||
"title": "Manage Books",
|
"title": "Manage Books",
|
||||||
"description": "Sample Application",
|
"description": "Sample Application",
|
||||||
@@ -22,6 +22,10 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sap.ui5": {
|
"sap.ui5": {
|
||||||
|
"flexEnabled": true,
|
||||||
|
"config": {
|
||||||
|
"experimentalCAPScenario": true
|
||||||
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"libs": {
|
"libs": {
|
||||||
"sap.fe.templates": {}
|
"sap.fe.templates": {}
|
||||||
@@ -73,6 +77,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"settings" : {
|
"settings" : {
|
||||||
"entitySet" : "Books",
|
"entitySet" : "Books",
|
||||||
|
"initialLoad": true,
|
||||||
"navigation" : {
|
"navigation" : {
|
||||||
"Books" : {
|
"Books" : {
|
||||||
"detail" : {
|
"detail" : {
|
||||||
142
fiori/app/appconfig/fioriSandboxConfig.json
Normal file
142
fiori/app/appconfig/fioriSandboxConfig.json
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
{
|
||||||
|
"services": {
|
||||||
|
"LaunchPage": {
|
||||||
|
"adapter": {
|
||||||
|
"config": {
|
||||||
|
"catalogs": [],
|
||||||
|
"groups": [
|
||||||
|
{
|
||||||
|
"id": "Bookshop",
|
||||||
|
"title": "Bookshop",
|
||||||
|
"isPreset": true,
|
||||||
|
"isVisible": true,
|
||||||
|
"isGroupLocked": false,
|
||||||
|
"tiles": [
|
||||||
|
{
|
||||||
|
"id": "BrowseBooks",
|
||||||
|
"tileType": "sap.ushell.ui.tile.StaticTile",
|
||||||
|
"properties": {
|
||||||
|
"title": "Browse Books",
|
||||||
|
"targetURL": "#Books-display"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "Administration",
|
||||||
|
"title": "Administration",
|
||||||
|
"isPreset": true,
|
||||||
|
"isVisible": true,
|
||||||
|
"isGroupLocked": false,
|
||||||
|
"tiles": [
|
||||||
|
{
|
||||||
|
"id": "ManageBooks",
|
||||||
|
"tileType": "sap.ushell.ui.tile.StaticTile",
|
||||||
|
"properties": {
|
||||||
|
"title": "Manage Books",
|
||||||
|
"targetURL": "#Books-manage"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ManageAuthors",
|
||||||
|
"tileType": "sap.ushell.ui.tile.StaticTile",
|
||||||
|
"properties": {
|
||||||
|
"title": "Manage Authors",
|
||||||
|
"targetURL": "#Authors-display"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": "ManageOrders",
|
||||||
|
"tileType": "sap.ushell.ui.tile.StaticTile",
|
||||||
|
"properties": {
|
||||||
|
"title": "Manage Orders",
|
||||||
|
"targetURL": "#Orders-manage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"NavTargetResolution": {
|
||||||
|
"config": {
|
||||||
|
"enableClientSideTargetResolution": true
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ClientSideTargetResolution": {
|
||||||
|
"adapter": {
|
||||||
|
"config": {
|
||||||
|
"inbounds": {
|
||||||
|
"BrowseBooks": {
|
||||||
|
"semanticObject": "Books",
|
||||||
|
"action": "display",
|
||||||
|
"title": "Browse Books",
|
||||||
|
"signature": {
|
||||||
|
"parameters": {
|
||||||
|
"Books.ID": {
|
||||||
|
"renameTo": "ID"
|
||||||
|
},
|
||||||
|
"Authors.books.ID": {
|
||||||
|
"renameTo": "ID"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalParameters": "ignored"
|
||||||
|
},
|
||||||
|
"resolutionResult": {
|
||||||
|
"applicationType": "SAPUI5",
|
||||||
|
"additionalInformation": "SAPUI5.Component=bookshop",
|
||||||
|
"url": "/browse/webapp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BrowseAuthors": {
|
||||||
|
"semanticObject": "Authors",
|
||||||
|
"action": "display",
|
||||||
|
"title": "Browse Authors",
|
||||||
|
"signature": {
|
||||||
|
"parameters": {
|
||||||
|
"Books.author.ID":{
|
||||||
|
"renameTo": "ID"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalParameters": "ignored"
|
||||||
|
},
|
||||||
|
"resolutionResult": {
|
||||||
|
"applicationType": "SAPUI5",
|
||||||
|
"additionalInformation": "SAPUI5.Component=authors",
|
||||||
|
"url": "/admin-authors/webapp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ManageBooks": {
|
||||||
|
"semanticObject": "Books",
|
||||||
|
"action": "manage",
|
||||||
|
"title": "Manage Books",
|
||||||
|
"signature": {
|
||||||
|
"parameters": {},
|
||||||
|
"additionalParameters": "allowed"
|
||||||
|
},
|
||||||
|
"resolutionResult": {
|
||||||
|
"applicationType": "SAPUI5",
|
||||||
|
"additionalInformation": "SAPUI5.Component=books",
|
||||||
|
"url": "/admin-books/webapp"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ManageOrders": {
|
||||||
|
"semanticObject": "Orders",
|
||||||
|
"action": "manage",
|
||||||
|
"signature": {
|
||||||
|
"parameters": {},
|
||||||
|
"additionalParameters": "allowed"
|
||||||
|
},
|
||||||
|
"resolutionResult": {
|
||||||
|
"applicationType": "SAPUI5",
|
||||||
|
"additionalInformation": "SAPUI5.Component=orders",
|
||||||
|
"url": "/orders/webapp"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0;url=bookshop/index.html">
|
|
||||||
</head>
|
|
||||||
@@ -1,50 +1,60 @@
|
|||||||
using CatalogService from '@capire/bookshop';
|
using CatalogService from '@capire/bookstore';
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Books Object Page
|
// Books Object Page
|
||||||
//
|
//
|
||||||
annotate CatalogService.Books with @(
|
annotate CatalogService.Books with @(UI : {
|
||||||
UI: {
|
HeaderInfo : {
|
||||||
HeaderInfo: {
|
TypeName : 'Book',
|
||||||
TypeName: 'Book',
|
TypeNamePlural : 'Books',
|
||||||
TypeNamePlural: 'Books',
|
Description : {Value : author}
|
||||||
Description: {Value: author}
|
|
||||||
},
|
},
|
||||||
HeaderFacets: [
|
HeaderFacets : [{
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Description}', Target: '@UI.FieldGroup#Descr'},
|
$Type : 'UI.ReferenceFacet',
|
||||||
],
|
Label : '{i18n>Description}',
|
||||||
Facets: [
|
Target : '@UI.FieldGroup#Descr'
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Price'},
|
}, ],
|
||||||
],
|
Facets : [{
|
||||||
FieldGroup#Descr: {
|
$Type : 'UI.ReferenceFacet',
|
||||||
Data: [
|
Label : '{i18n>Details}',
|
||||||
{Value: descr},
|
Target : '@UI.FieldGroup#Price'
|
||||||
]
|
}, ],
|
||||||
|
FieldGroup #Descr : {Data : [{Value : descr}, ]},
|
||||||
|
FieldGroup #Price : {Data : [
|
||||||
|
{Value : price},
|
||||||
|
{
|
||||||
|
Value : currency.symbol,
|
||||||
|
Label : '{i18n>Currency}'
|
||||||
},
|
},
|
||||||
FieldGroup#Price: {
|
]},
|
||||||
Data: [
|
});
|
||||||
{Value: price},
|
|
||||||
{Value: currency.symbol, Label: '{i18n>Currency}'},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Books Object Page
|
// Books Object Page
|
||||||
//
|
//
|
||||||
annotate CatalogService.Books with @(
|
annotate CatalogService.Books with @(UI : {
|
||||||
UI: {
|
SelectionFields : [
|
||||||
SelectionFields: [ ID, price, currency_code ],
|
ID,
|
||||||
LineItem: [
|
price,
|
||||||
{Value: title},
|
currency_code
|
||||||
{Value: author, Label:'{i18n>Author}'},
|
],
|
||||||
{Value: genre.name},
|
LineItem : [
|
||||||
{Value: price},
|
{
|
||||||
{Value: currency.symbol, Label:' '},
|
Value : ID,
|
||||||
]
|
Label : '{i18n>Title}'
|
||||||
},
|
},
|
||||||
);
|
{
|
||||||
|
Value : author,
|
||||||
|
Label : '{i18n>Author}'
|
||||||
|
},
|
||||||
|
{Value : genre.name},
|
||||||
|
{Value : price},
|
||||||
|
{
|
||||||
|
Value : currency.symbol,
|
||||||
|
Label : ' '
|
||||||
|
},
|
||||||
|
]
|
||||||
|
}, );
|
||||||
|
|||||||
@@ -1,11 +1,14 @@
|
|||||||
{
|
{
|
||||||
"_version": "1.8.0",
|
"_version": "1.28.0",
|
||||||
"sap.app": {
|
"sap.app": {
|
||||||
"id": "bookshop",
|
"id": "bookshop",
|
||||||
"type": "application",
|
"type": "application",
|
||||||
"title": "Browse Books",
|
"title": "Browse Books",
|
||||||
"description": "Sample Application",
|
"description": "Sample Application",
|
||||||
"i18n": "i18n/i18n.properties",
|
"i18n": "i18n/i18n.properties",
|
||||||
|
"applicationVersion": {
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
"dataSources": {
|
"dataSources": {
|
||||||
"CatalogService": {
|
"CatalogService": {
|
||||||
"uri": "/browse/",
|
"uri": "/browse/",
|
||||||
@@ -15,14 +18,43 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"-sourceTemplate": {
|
"sourceTemplate": {
|
||||||
"id": "ui5template.basicSAPUI5ApplicationProject",
|
"id": "ui5template.basicSAPUI5ApplicationProject",
|
||||||
"-id": "ui5template.smartTemplate",
|
"-id": "ui5template.smartTemplate",
|
||||||
"-version": "1.40.12"
|
"version": "1.40.12"
|
||||||
|
},
|
||||||
|
"crossNavigation": {
|
||||||
|
"inbounds": {
|
||||||
|
"intent1": {
|
||||||
|
"signature": {
|
||||||
|
"parameters": {
|
||||||
|
"Books.ID":{
|
||||||
|
"renameTo": "ID"
|
||||||
|
},
|
||||||
|
"Authors.books.ID": {
|
||||||
|
"renameTo": "ID"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"additionalParameters": "ignored"
|
||||||
|
},
|
||||||
|
"semanticObject": "Books",
|
||||||
|
"action": "display",
|
||||||
|
"title": "{{appTitle}}",
|
||||||
|
"info": "{{appInfo}}",
|
||||||
|
"subTitle": "{{appSubTitle}}",
|
||||||
|
"icon": "sap-icon://course-book",
|
||||||
|
"indicatorDataSource": {
|
||||||
|
"dataSource": "CatalogService",
|
||||||
|
"path": "Books/$count",
|
||||||
|
"refresh": 1800
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"sap.ui5": {
|
"sap.ui5": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"minUI5Version": "1.81.0",
|
||||||
"libs": {
|
"libs": {
|
||||||
"sap.fe.templates": {}
|
"sap.fe.templates": {}
|
||||||
}
|
}
|
||||||
@@ -68,6 +100,7 @@
|
|||||||
"options": {
|
"options": {
|
||||||
"settings": {
|
"settings": {
|
||||||
"entitySet": "Books",
|
"entitySet": "Books",
|
||||||
|
"initialLoad": true,
|
||||||
"navigation": {
|
"navigation": {
|
||||||
"Books": {
|
"Books": {
|
||||||
"detail": {
|
"detail": {
|
||||||
@@ -97,7 +130,12 @@
|
|||||||
},
|
},
|
||||||
"sap.ui": {
|
"sap.ui": {
|
||||||
"technology": "UI5",
|
"technology": "UI5",
|
||||||
"fullWidth": false
|
"fullWidth": false,
|
||||||
|
"deviceTypes":{
|
||||||
|
"desktop": true,
|
||||||
|
"tablet": true,
|
||||||
|
"phone": true
|
||||||
|
}
|
||||||
},
|
},
|
||||||
"sap.fiori": {
|
"sap.fiori": {
|
||||||
"registrationIds": [],
|
"registrationIds": [],
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
Common Annotations shared by all apps
|
Common Annotations shared by all apps
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using { sap.capire.bookshop as my } from '@capire/bookshop';
|
using { sap.capire.bookshop as my } from '@capire/bookstore';
|
||||||
using { sap.common } from '@capire/common';
|
using { sap.common } from '@capire/common';
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -10,39 +10,52 @@ using { sap.common } from '@capire/common';
|
|||||||
// Books Lists
|
// Books Lists
|
||||||
//
|
//
|
||||||
annotate my.Books with @(
|
annotate my.Books with @(
|
||||||
Common.SemanticKey: [title],
|
Common.SemanticKey : [ID],
|
||||||
UI: {
|
UI : {
|
||||||
Identification: [{Value:title}],
|
Identification : [{Value : title}],
|
||||||
SelectionFields: [ ID, author_ID, price, currency_code ],
|
SelectionFields : [
|
||||||
LineItem: [
|
ID,
|
||||||
{Value: ID},
|
author_ID,
|
||||||
{Value: title},
|
price,
|
||||||
{Value: author.name, Label:'{i18n>Author}'},
|
currency_code
|
||||||
{Value: genre.name},
|
],
|
||||||
{Value: stock},
|
LineItem : [
|
||||||
{Value: price},
|
{
|
||||||
{Value: currency.symbol, Label:' '},
|
Value : ID,
|
||||||
|
Label : '{i18n>Title}'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
Value : author.ID,
|
||||||
|
Label : '{i18n>Author}'
|
||||||
|
},
|
||||||
|
{Value : genre.name},
|
||||||
|
{Value : stock},
|
||||||
|
{Value : price},
|
||||||
|
{
|
||||||
|
Value : currency.symbol,
|
||||||
|
Label : ' '
|
||||||
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
) {
|
) {
|
||||||
author @ValueList.entity:'Authors';
|
ID @Common: {
|
||||||
|
SemanticObject : 'Books',
|
||||||
|
Text: title,
|
||||||
|
TextArrangement : #TextOnly
|
||||||
|
};
|
||||||
|
author @ValueList.entity : 'Authors';
|
||||||
};
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Books Details
|
// Books Details
|
||||||
//
|
//
|
||||||
annotate my.Books with @(
|
annotate my.Books with @(UI : {HeaderInfo : {
|
||||||
UI: {
|
TypeName : '{i18n>Book}',
|
||||||
HeaderInfo: {
|
TypeNamePlural : '{i18n>Books}',
|
||||||
TypeName: '{i18n>Book}',
|
Title : {Value : title},
|
||||||
TypeNamePlural: '{i18n>Books}',
|
Description : {Value : author.name}
|
||||||
Title: {Value: title},
|
}, });
|
||||||
Description: {Value: author.name}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -50,12 +63,18 @@ annotate my.Books with @(
|
|||||||
// Books Elements
|
// Books Elements
|
||||||
//
|
//
|
||||||
annotate my.Books with {
|
annotate my.Books with {
|
||||||
ID @title:'{i18n>ID}' @UI.HiddenFilter;
|
ID @title : '{i18n>ID}';
|
||||||
title @title:'{i18n>Title}';
|
title @title : '{i18n>Title}';
|
||||||
genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
|
genre @title : '{i18n>Genre}' @Common : {
|
||||||
author @title:'{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly };
|
Text : genre.name,
|
||||||
price @title:'{i18n>Price}' @Measures.ISOCurrency: currency_code;
|
TextArrangement : #TextOnly
|
||||||
stock @title:'{i18n>Stock}';
|
};
|
||||||
|
author @title : '{i18n>Author}' @Common : {
|
||||||
|
Text : author.name,
|
||||||
|
TextArrangement : #TextOnly
|
||||||
|
};
|
||||||
|
price @title : '{i18n>Price}' @Measures.ISOCurrency : currency_code;
|
||||||
|
stock @title : '{i18n>Stock}';
|
||||||
descr @UI.MultiLineText;
|
descr @UI.MultiLineText;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -64,12 +83,15 @@ annotate my.Books with {
|
|||||||
// Genres List
|
// Genres List
|
||||||
//
|
//
|
||||||
annotate my.Genres with @(
|
annotate my.Genres with @(
|
||||||
Common.SemanticKey: [name],
|
Common.SemanticKey : [name],
|
||||||
UI: {
|
UI : {
|
||||||
SelectionFields: [ name ],
|
SelectionFields : [name],
|
||||||
LineItem:[
|
LineItem : [
|
||||||
{Value: name},
|
{Value : name},
|
||||||
{Value: parent.name, Label: 'Main Genre'},
|
{
|
||||||
|
Value : parent.name,
|
||||||
|
Label : 'Main Genre'
|
||||||
|
},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -78,28 +100,28 @@ annotate my.Genres with @(
|
|||||||
//
|
//
|
||||||
// Genre Details
|
// Genre Details
|
||||||
//
|
//
|
||||||
annotate my.Genres with @(
|
annotate my.Genres with @(UI : {
|
||||||
UI: {
|
Identification : [{Value : name}],
|
||||||
Identification: [{Value:name}],
|
HeaderInfo : {
|
||||||
HeaderInfo: {
|
TypeName : '{i18n>Genre}',
|
||||||
TypeName: '{i18n>Genre}',
|
TypeNamePlural : '{i18n>Genres}',
|
||||||
TypeNamePlural: '{i18n>Genres}',
|
Title : {Value : name},
|
||||||
Title: {Value: name},
|
Description : {Value : ID}
|
||||||
Description: {Value: ID}
|
|
||||||
},
|
},
|
||||||
Facets: [
|
Facets : [{
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>SubGenres}', Target: 'children/@UI.LineItem'},
|
$Type : 'UI.ReferenceFacet',
|
||||||
],
|
Label : '{i18n>SubGenres}',
|
||||||
}
|
Target : 'children/@UI.LineItem'
|
||||||
);
|
}, ],
|
||||||
|
});
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Genres Elements
|
// Genres Elements
|
||||||
//
|
//
|
||||||
annotate my.Genres with {
|
annotate my.Genres with {
|
||||||
ID @title: '{i18n>ID}';
|
ID @title : '{i18n>ID}';
|
||||||
name @title: '{i18n>Genre}';
|
name @title : '{i18n>Genre}';
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -107,38 +129,42 @@ annotate my.Genres with {
|
|||||||
// Authors List
|
// Authors List
|
||||||
//
|
//
|
||||||
annotate my.Authors with @(
|
annotate my.Authors with @(
|
||||||
Common.SemanticKey: [name],
|
Common.SemanticKey : [ID],
|
||||||
UI: {
|
UI : {
|
||||||
Identification: [{Value:name}],
|
Identification : [{Value : name}],
|
||||||
SelectionFields: [ name ],
|
SelectionFields : [name],
|
||||||
LineItem:[
|
LineItem : [
|
||||||
{Value: ID},
|
{Value : ID},
|
||||||
{Value: name},
|
{Value : dateOfBirth},
|
||||||
{Value: dateOfBirth},
|
{Value : dateOfDeath},
|
||||||
{Value: dateOfDeath},
|
{Value : placeOfBirth},
|
||||||
{Value: placeOfBirth},
|
{Value : placeOfDeath},
|
||||||
{Value: placeOfDeath},
|
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
);
|
) {
|
||||||
|
ID @Common: {
|
||||||
|
SemanticObject : 'Authors',
|
||||||
|
Text: name,
|
||||||
|
TextArrangement : #TextOnly,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Author Details
|
// Author Details
|
||||||
//
|
//
|
||||||
annotate my.Authors with @(
|
annotate my.Authors with @(UI : {
|
||||||
UI: {
|
HeaderInfo : {
|
||||||
HeaderInfo: {
|
TypeName : '{i18n>Author}',
|
||||||
TypeName: '{i18n>Author}',
|
TypeNamePlural : '{i18n>Authors}',
|
||||||
TypeNamePlural: '{i18n>Authors}',
|
Title : {Value : name},
|
||||||
Title: {Value: name},
|
Description : {Value : dateOfBirth}
|
||||||
Description: {Value: dateOfBirth}
|
|
||||||
},
|
},
|
||||||
Facets: [
|
Facets : [{
|
||||||
{$Type: 'UI.ReferenceFacet', Target: 'books/@UI.LineItem'},
|
$Type : 'UI.ReferenceFacet',
|
||||||
],
|
Target : 'books/@UI.LineItem'
|
||||||
}
|
}, ],
|
||||||
);
|
});
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -146,12 +172,12 @@ annotate my.Authors with @(
|
|||||||
// Authors Elements
|
// Authors Elements
|
||||||
//
|
//
|
||||||
annotate my.Authors with {
|
annotate my.Authors with {
|
||||||
ID @title:'{i18n>ID}' @UI.HiddenFilter;
|
ID @title : '{i18n>ID}';
|
||||||
name @title:'{i18n>Name}';
|
name @title : '{i18n>Name}';
|
||||||
dateOfBirth @title:'{i18n>DateOfBirth}';
|
dateOfBirth @title : '{i18n>DateOfBirth}';
|
||||||
dateOfDeath @title:'{i18n>DateOfDeath}';
|
dateOfDeath @title : '{i18n>DateOfDeath}';
|
||||||
placeOfBirth @title:'{i18n>PlaceOfBirth}';
|
placeOfBirth @title : '{i18n>PlaceOfBirth}';
|
||||||
placeOfDeath @title:'{i18n>PlaceOfDeath}';
|
placeOfDeath @title : '{i18n>PlaceOfDeath}';
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
@@ -159,13 +185,16 @@ annotate my.Authors with {
|
|||||||
// Languages List
|
// Languages List
|
||||||
//
|
//
|
||||||
annotate common.Languages with @(
|
annotate common.Languages with @(
|
||||||
Common.SemanticKey: [code],
|
Common.SemanticKey : [code],
|
||||||
Identification: [{Value:code}],
|
Identification : [{Value : code}],
|
||||||
UI: {
|
UI : {
|
||||||
SelectionFields: [ name, descr ],
|
SelectionFields : [
|
||||||
LineItem:[
|
name,
|
||||||
{Value: code},
|
descr
|
||||||
{Value: name},
|
],
|
||||||
|
LineItem : [
|
||||||
|
{Value : code},
|
||||||
|
{Value : name},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -174,40 +203,41 @@ annotate common.Languages with @(
|
|||||||
//
|
//
|
||||||
// Language Details
|
// Language Details
|
||||||
//
|
//
|
||||||
annotate common.Languages with @(
|
annotate common.Languages with @(UI : {
|
||||||
UI: {
|
HeaderInfo : {
|
||||||
HeaderInfo: {
|
TypeName : '{i18n>Language}',
|
||||||
TypeName: '{i18n>Language}',
|
TypeNamePlural : '{i18n>Languages}',
|
||||||
TypeNamePlural: '{i18n>Languages}',
|
Title : {Value : name},
|
||||||
Title: {Value: name},
|
Description : {Value : descr}
|
||||||
Description: {Value: descr}
|
|
||||||
},
|
},
|
||||||
Facets: [
|
Facets : [{
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
$Type : 'UI.ReferenceFacet',
|
||||||
],
|
Label : '{i18n>Details}',
|
||||||
FieldGroup#Details: {
|
Target : '@UI.FieldGroup#Details'
|
||||||
Data: [
|
}, ],
|
||||||
{Value: code},
|
FieldGroup #Details : {Data : [
|
||||||
{Value: name},
|
{Value : code},
|
||||||
{Value: descr}
|
{Value : name},
|
||||||
]
|
{Value : descr}
|
||||||
},
|
]},
|
||||||
}
|
});
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Currencies List
|
// Currencies List
|
||||||
//
|
//
|
||||||
annotate common.Currencies with @(
|
annotate common.Currencies with @(
|
||||||
Common.SemanticKey: [code],
|
Common.SemanticKey : [code],
|
||||||
Identification: [{Value:code}],
|
Identification : [{Value : code}],
|
||||||
UI: {
|
UI : {
|
||||||
SelectionFields: [ name, descr ],
|
SelectionFields : [
|
||||||
LineItem:[
|
name,
|
||||||
{Value: descr},
|
descr
|
||||||
{Value: symbol},
|
],
|
||||||
{Value: code},
|
LineItem : [
|
||||||
|
{Value : descr},
|
||||||
|
{Value : symbol},
|
||||||
|
{Value : code},
|
||||||
],
|
],
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -216,42 +246,44 @@ annotate common.Currencies with @(
|
|||||||
//
|
//
|
||||||
// Currency Details
|
// Currency Details
|
||||||
//
|
//
|
||||||
annotate common.Currencies with @(
|
annotate common.Currencies with @(UI : {
|
||||||
UI: {
|
HeaderInfo : {
|
||||||
HeaderInfo: {
|
TypeName : '{i18n>Currency}',
|
||||||
TypeName: '{i18n>Currency}',
|
TypeNamePlural : '{i18n>Currencies}',
|
||||||
TypeNamePlural: '{i18n>Currencies}',
|
Title : {Value : descr},
|
||||||
Title: {Value: descr},
|
Description : {Value : code}
|
||||||
Description: {Value: code}
|
},
|
||||||
|
Facets : [
|
||||||
|
{
|
||||||
|
$Type : 'UI.ReferenceFacet',
|
||||||
|
Label : '{i18n>Details}',
|
||||||
|
Target : '@UI.FieldGroup#Details'
|
||||||
|
},
|
||||||
|
{
|
||||||
|
$Type : 'UI.ReferenceFacet',
|
||||||
|
Label : '{i18n>Extended}',
|
||||||
|
Target : '@UI.FieldGroup#Extended'
|
||||||
},
|
},
|
||||||
Facets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Extended}', Target: '@UI.FieldGroup#Extended'},
|
|
||||||
],
|
],
|
||||||
FieldGroup#Details: {
|
FieldGroup #Details : {Data : [
|
||||||
Data: [
|
{Value : name},
|
||||||
{Value: name},
|
{Value : symbol},
|
||||||
{Value: symbol},
|
{Value : code},
|
||||||
{Value: code},
|
{Value : descr}
|
||||||
{Value: descr}
|
]},
|
||||||
]
|
FieldGroup #Extended : {Data : [
|
||||||
},
|
{Value : numcode},
|
||||||
FieldGroup#Extended: {
|
{Value : minor},
|
||||||
Data: [
|
{Value : exponent}
|
||||||
{Value: numcode},
|
]},
|
||||||
{Value: minor},
|
});
|
||||||
{Value: exponent}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Currencies Elements
|
// Currencies Elements
|
||||||
//
|
//
|
||||||
annotate common.Currencies with {
|
annotate common.Currencies with {
|
||||||
numcode @title:'{i18n>NumCode}';
|
numcode @title : '{i18n>NumCode}';
|
||||||
minor @title:'{i18n>MinorUnit}';
|
minor @title : '{i18n>MinorUnit}';
|
||||||
exponent @title:'{i18n>Exponent}';
|
exponent @title : '{i18n>Exponent}';
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,31 +10,20 @@
|
|||||||
<script>
|
<script>
|
||||||
window["sap-ushell-config"] = {
|
window["sap-ushell-config"] = {
|
||||||
defaultRenderer: "fiori2",
|
defaultRenderer: "fiori2",
|
||||||
applications: {
|
applications: {},
|
||||||
"browse-books": {
|
bootstrapPlugins: {
|
||||||
title: "Browse Books",
|
RuntimeAuthoringPlugin: {
|
||||||
description: "w/ SAP Fiori Elements",
|
component: "sap.ushell.plugins.rta",
|
||||||
additionalInformation: "SAPUI5.Component=bookshop",
|
config: {
|
||||||
applicationType : "URL",
|
validateAppVersion: false,
|
||||||
url: "/browse/webapp",
|
},
|
||||||
navigationMode: "embedded"
|
},
|
||||||
|
PersonalizePlugin: {
|
||||||
|
component: "sap.ushell.plugins.rta-personalize",
|
||||||
|
config: {
|
||||||
|
validateAppVersion: false,
|
||||||
},
|
},
|
||||||
"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>
|
||||||
@@ -47,8 +36,13 @@
|
|||||||
data-sap-ui-frameOptions="allow"
|
data-sap-ui-frameOptions="allow"
|
||||||
></script>
|
></script>
|
||||||
<script>
|
<script>
|
||||||
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
|
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"));
|
||||||
</script>
|
sap.ui
|
||||||
|
.getCore()
|
||||||
|
.getConfiguration()
|
||||||
|
.setFlexibilityServices([{ connector: "SessionStorageConnector" }]);
|
||||||
|
sap.ui.getCore().getConfiguration().setLanguage("en");
|
||||||
|
</script>
|
||||||
|
|
||||||
</head>
|
</head>
|
||||||
<body class="sapUiBody" id="content"></body>
|
<body class="sapUiBody" id="content"></body>
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0;url=reviews/index.html">
|
|
||||||
</head>
|
|
||||||
@@ -2,11 +2,8 @@
|
|||||||
This model controls what gets served to Fiori frontends...
|
This model controls what gets served to Fiori frontends...
|
||||||
*/
|
*/
|
||||||
|
|
||||||
using from './admin/fiori-service';
|
using from './admin-authors/fiori-service';
|
||||||
|
using from './admin-books/fiori-service';
|
||||||
using from './browse/fiori-service';
|
using from './browse/fiori-service';
|
||||||
using from './common';
|
using from './common';
|
||||||
|
using from '@capire/bookstore/srv/mashup';
|
||||||
using from '@capire/common';
|
|
||||||
|
|
||||||
// only works in case of embedded orders service
|
|
||||||
using from '@capire/orders/app/orders/fiori-service';
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
// Add Author.age and .lifetime with a DB-specific function
|
// Add Author.age and .lifetime with a DB-specific function
|
||||||
//
|
//
|
||||||
|
|
||||||
using { AdminService } from '../schema';
|
using { AdminService } from '@capire/bookshop';
|
||||||
|
|
||||||
extend projection AdminService.Authors with {
|
extend projection AdminService.Authors with {
|
||||||
YEARS_BETWEEN(dateOfBirth, dateOfDeath) as age: Integer,
|
YEARS_BETWEEN(dateOfBirth, dateOfDeath) as age: Integer,
|
||||||
|
|||||||
@@ -1,8 +0,0 @@
|
|||||||
using { sap.capire.bookshop } from '@capire/bookshop';
|
|
||||||
|
|
||||||
// Forward-declare calculated fields to be filled in database-specific ways
|
|
||||||
// TODO find a better way to have 'default' fields that still can be overwritten.
|
|
||||||
extend bookshop.Authors with {
|
|
||||||
virtual age: Integer;
|
|
||||||
virtual lifetime: String;
|
|
||||||
}
|
|
||||||
@@ -2,7 +2,7 @@
|
|||||||
// Add Author.age and .lifetime with a DB-specific function
|
// Add Author.age and .lifetime with a DB-specific function
|
||||||
//
|
//
|
||||||
|
|
||||||
using { AdminService } from '../schema';
|
using { AdminService } from '@capire/bookshop';
|
||||||
|
|
||||||
extend projection AdminService.Authors with {
|
extend projection AdminService.Authors with {
|
||||||
strftime('%Y',dateOfDeath)-strftime('%Y',dateOfBirth) as age: Integer,
|
strftime('%Y',dateOfDeath)-strftime('%Y',dateOfBirth) as age: Integer,
|
||||||
|
|||||||
@@ -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,10 +12,10 @@
|
|||||||
"watch": "cds watch"
|
"watch": "cds watch"
|
||||||
},
|
},
|
||||||
"cds": {
|
"cds": {
|
||||||
"hana": {
|
|
||||||
"deploy-format": "hdbtable"
|
|
||||||
},
|
|
||||||
"requires": {
|
"requires": {
|
||||||
|
"extensibility": {
|
||||||
|
"kind": "uiflex"
|
||||||
|
},
|
||||||
"auth": {
|
"auth": {
|
||||||
"strategy": "dummy"
|
"strategy": "dummy"
|
||||||
},
|
},
|
||||||
@@ -30,14 +27,30 @@
|
|||||||
"kind": "odata",
|
"kind": "odata",
|
||||||
"model": "@capire/orders"
|
"model": "@capire/orders"
|
||||||
},
|
},
|
||||||
|
"messaging": {
|
||||||
|
"[production]": {
|
||||||
|
"kind": "enterprise-messaging"
|
||||||
|
},
|
||||||
|
"[development]": {
|
||||||
|
"kind": "file-based-messaging"
|
||||||
|
},
|
||||||
|
"[hybrid!]": {
|
||||||
|
"kind": "enterprise-messaging-shared"
|
||||||
|
}
|
||||||
|
},
|
||||||
"db": {
|
"db": {
|
||||||
"kind": "sql",
|
"kind": "sql"
|
||||||
|
},
|
||||||
|
"db-ext": {
|
||||||
"[development]": {
|
"[development]": {
|
||||||
"model": "db/sqlite"
|
"model": "db/sqlite"
|
||||||
},
|
},
|
||||||
"[production]": {
|
"[production]": {
|
||||||
"model": "db/hana"
|
"model": "db/hana"
|
||||||
}
|
}
|
||||||
|
},
|
||||||
|
"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)))
|
|
||||||
|
|||||||
@@ -7,8 +7,8 @@
|
|||||||
"start:ts": "cds-ts serve srv/world.cds"
|
"start:ts": "cds-ts serve srv/world.cds"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/jest": "^26.0.23",
|
"@types/jest": "^27.0.2",
|
||||||
"@types/node": "^15.12.0",
|
"@types/node": "^16.11.6",
|
||||||
"ts-jest": "^27.0.2",
|
"ts-jest": "^27.0.2",
|
||||||
"typescript": "^4.3.5"
|
"typescript": "^4.3.5"
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
|
import type { Request } from "@sap/cds/apis/services"
|
||||||
|
|
||||||
module.exports = class say {
|
module.exports = class say {
|
||||||
hello(req: any) {
|
hello(req: Request) {
|
||||||
return `Hello ${req.data.to} from a TypeScript file!`
|
return `Hello ${req.data.to} from a TypeScript file!`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,7 +10,7 @@
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
using { OrdersService } from '../../srv/orders-service';
|
using { OrdersService } from '../srv/orders-service';
|
||||||
|
|
||||||
|
|
||||||
@odata.draft.enabled
|
@odata.draft.enabled
|
||||||
@@ -68,7 +68,7 @@ annotate OrdersService.Orders with @(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
annotate OrdersService.Orders_Items with @(
|
annotate OrdersService.Orders.Items with @(
|
||||||
UI: {
|
UI: {
|
||||||
LineItem: [
|
LineItem: [
|
||||||
{Value: product_ID, Label:'Product ID'},
|
{Value: product_ID, Label:'Product ID'},
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
/*
|
|
||||||
This model controls what gets served to Fiori frontends...
|
|
||||||
*/
|
|
||||||
|
|
||||||
using from './orders/fiori-service';
|
|
||||||
|
500 Internal Server Error
Gitea Version: 1.24.4 |