Compare commits
33 Commits
suppliers-
...
openSAP-we
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
359bfebcac | ||
|
|
569546258d | ||
|
|
36981144d5 | ||
|
|
66e043294f | ||
|
|
6f79882c57 | ||
|
|
62c11dee1a | ||
|
|
7ef5034b3e | ||
|
|
a8dee62f25 | ||
|
|
85498a9875 | ||
|
|
19b09645db | ||
|
|
9861b05f2d | ||
|
|
c58bcf905f | ||
|
|
50ab059c13 | ||
|
|
46960159d1 | ||
|
|
02228e5a96 | ||
|
|
d4793177fc | ||
|
|
2787284aad | ||
|
|
61a318b250 | ||
|
|
6e42e5a173 | ||
|
|
bf162c23cc | ||
|
|
9b41615ac8 | ||
|
|
29840afc0b | ||
|
|
fa880e2987 | ||
|
|
90881558dc | ||
|
|
6d3e9a211a | ||
|
|
e694fbaf13 | ||
|
|
43d0373d70 | ||
|
|
b632013b16 | ||
|
|
a6deddf022 | ||
|
|
513bf9711f | ||
|
|
00c99c0e0b | ||
|
|
e042317f82 | ||
|
|
04ab69c48f |
11
.eslintrc
11
.eslintrc
@@ -1,14 +1,12 @@
|
|||||||
{
|
{
|
||||||
"extends": "eslint:recommended",
|
"extends": "eslint:recommended",
|
||||||
"env": {
|
"env": {
|
||||||
"browser": true,
|
|
||||||
"node": true,
|
"node": true,
|
||||||
"es6": true,
|
"es6": true,
|
||||||
"jest": true,
|
"jest": true
|
||||||
"mocha": true
|
|
||||||
},
|
},
|
||||||
"parserOptions": {
|
"parserOptions": {
|
||||||
"ecmaVersion": 2018
|
"ecmaVersion": 2017
|
||||||
},
|
},
|
||||||
"globals": {
|
"globals": {
|
||||||
"SELECT": true,
|
"SELECT": true,
|
||||||
@@ -21,7 +19,6 @@
|
|||||||
},
|
},
|
||||||
"rules": {
|
"rules": {
|
||||||
"no-console": "off",
|
"no-console": "off",
|
||||||
"require-atomic-updates": "off",
|
"require-atomic-updates": "off"
|
||||||
"require-await":"warn"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
---
|
|
||||||
name: This channel is CLOSED.
|
|
||||||
about: Use our community at https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce
|
|
||||||
title: ''
|
|
||||||
labels: ''
|
|
||||||
assignees: ''
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
Please use our community on https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce
|
|
||||||
28
.github/workflows/node.js.yml
vendored
28
.github/workflows/node.js.yml
vendored
@@ -1,28 +0,0 @@
|
|||||||
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
|
|
||||||
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
|
|
||||||
|
|
||||||
name: CI
|
|
||||||
|
|
||||||
on:
|
|
||||||
push:
|
|
||||||
branches: [ master ]
|
|
||||||
pull_request:
|
|
||||||
branches: [ master ]
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
|
|
||||||
strategy:
|
|
||||||
matrix:
|
|
||||||
node-version: [12.x, 14.x]
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v2
|
|
||||||
- name: Use Node.js ${{ matrix.node-version }}
|
|
||||||
uses: actions/setup-node@v1
|
|
||||||
with:
|
|
||||||
node-version: ${{ matrix.node-version }}
|
|
||||||
- run: npm install
|
|
||||||
- run: npm test
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
@@ -13,5 +13,3 @@ target/
|
|||||||
connection.properties
|
connection.properties
|
||||||
default-env.json
|
default-env.json
|
||||||
packages/messageBox
|
packages/messageBox
|
||||||
reviews/msg-box
|
|
||||||
reviews/db/test.db
|
|
||||||
|
|||||||
1
.registry/.gitignore
vendored
1
.registry/.gitignore
vendored
@@ -1 +0,0 @@
|
|||||||
*.tgz
|
|
||||||
@@ -1,71 +0,0 @@
|
|||||||
const { exec } = require ('child_process')
|
|
||||||
const express = require ('express')
|
|
||||||
const fs = require ('fs')
|
|
||||||
const app = express()
|
|
||||||
|
|
||||||
const { PORT=4444 } = process.env
|
|
||||||
const [,,port=PORT] = process.argv
|
|
||||||
process.chdir(__dirname)
|
|
||||||
|
|
||||||
app.use('/-/:tarball', (req,res,next) => {
|
|
||||||
console.debug ('GET', req.params)
|
|
||||||
try {
|
|
||||||
const { tarball } = req.params
|
|
||||||
const [, pkg ] = /^capire-(\w+)/.exec(tarball)
|
|
||||||
fs.lstat(tarball,(err => {
|
|
||||||
if (err) exec(`npm pack ../${pkg}`,next)
|
|
||||||
else next()
|
|
||||||
}))
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
res.sendStatus(500)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.use('/-', express.static(__dirname))
|
|
||||||
|
|
||||||
app.get('/*', (req,res)=>{
|
|
||||||
const url = decodeURIComponent(req.url)
|
|
||||||
console.debug ('GET',url)
|
|
||||||
try {
|
|
||||||
const [, capire, pkg ] = /^\/(@capire)\/(\w+)/.exec(url)
|
|
||||||
const package = require (`${capire}/${pkg}/package.json`)
|
|
||||||
const tarball = `capire-${pkg}-${package.version}.tgz`
|
|
||||||
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
|
|
||||||
res.json({
|
|
||||||
"name": package.name,
|
|
||||||
"dist-tags": {
|
|
||||||
"latest": package.version
|
|
||||||
},
|
|
||||||
"versions": {
|
|
||||||
[package.version]: {
|
|
||||||
"name": package.name,
|
|
||||||
"version": package.version,
|
|
||||||
"dist": {
|
|
||||||
"tarball": `http://localhost:${port}/-/${tarball}`
|
|
||||||
},
|
|
||||||
}
|
|
||||||
},
|
|
||||||
})
|
|
||||||
} catch (e) {
|
|
||||||
console.error(e)
|
|
||||||
res.sendStatus(404)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
app.listen(port, ()=>{
|
|
||||||
console.log (`npm set @capire:registry=http://localhost:${port}`)
|
|
||||||
console.log (`@capire registry listening on http://localhost:${port}`)
|
|
||||||
exec(`npm set @capire:registry=http://localhost:${port}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
const _exit = ()=>{
|
|
||||||
console.log ('\nnpm conf rm @capire:registry')
|
|
||||||
exec('npm conf rm @capire:registry')
|
|
||||||
exec('rm *.tgz')
|
|
||||||
process.exit()
|
|
||||||
}
|
|
||||||
process.on ('SIGTERM',_exit)
|
|
||||||
process.on ('SIGHUP',_exit)
|
|
||||||
process.on ('SIGINT',_exit)
|
|
||||||
process.on ('SIGUSR2',_exit)
|
|
||||||
29
.reuse/dep5
29
.reuse/dep5
@@ -1,29 +0,0 @@
|
|||||||
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
|
|
||||||
Upstream-Name: cloud-cap-samples
|
|
||||||
Upstream-Contact: <Christian Georgi (christian.georgi@sap.com)>
|
|
||||||
Source: https://github.com/SAP-samples/cloud-cap-samples
|
|
||||||
Disclaimer: The code in this project may include calls to APIs (“API Calls”) of
|
|
||||||
SAP or third-party products or services developed outside of this project
|
|
||||||
(“External Products”).
|
|
||||||
“APIs” means application programming interfaces, as well as their respective
|
|
||||||
specifications and implementing code that allows software to communicate with
|
|
||||||
other software.
|
|
||||||
API Calls to External Products are not licensed under the open source license
|
|
||||||
that governs this project. The use of such API Calls and related External
|
|
||||||
Products are subject to applicable additional agreements with the relevant
|
|
||||||
provider of the External Products. In no event shall the open source license
|
|
||||||
that governs this project grant any rights in or to any External Products,or
|
|
||||||
alter, expand or supersede any terms of the applicable additional agreements.
|
|
||||||
If you have a valid license agreement with SAP for the use of a particular SAP
|
|
||||||
External Product, then you may make use of any API Calls included in this
|
|
||||||
project’s code for that SAP External Product, subject to the terms of such
|
|
||||||
license agreement. If you do not have a valid license agreement for the use of
|
|
||||||
a particular SAP External Product, then you may only make use of any API Calls
|
|
||||||
in this project for that SAP External Product for your internal, non-productive
|
|
||||||
and non-commercial test and evaluation of such API Calls. Nothing herein grants
|
|
||||||
you any rights to use or access any SAP External Product, or provide any third
|
|
||||||
parties the right to use of access any SAP External Product, through API Calls.
|
|
||||||
|
|
||||||
Files: *
|
|
||||||
Copyright: 2019-2020 SAP SE or an SAP affiliate company and cap-cloud-samples
|
|
||||||
License: Apache-2.0
|
|
||||||
@@ -1,109 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://aka.ms/codetour-schema",
|
|
||||||
"title": "Database Functions",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"title": "Introduction",
|
|
||||||
"description": "### Database Functions in CDS Models\n\nIn this tour, you'll learn how to add database-specific functions to CDS models in your application."
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "bookshop/db/schema.cds",
|
|
||||||
"description": "#### Basic Schema\n\nWe want to add two fields to the `Authors` entity, one for the author's age and one for the span of years that she or he lived.\n\nThese two fields can be computed out of the existing `dateOfBirth` and `dateOfDeath` fields.",
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 19,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 21,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Base fields in Author"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "bookshop/srv/admin-service.cds",
|
|
||||||
"description": "This is how the `Authors` entity gets exposed in an OData or REST service.\n\nIn the next step, you'll see how we extend this projection.",
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 4,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 5,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Authors service"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "fiori/db/sqlite/index.cds",
|
|
||||||
"description": "#### SQLite Implementation\n\nHere's the first implementation for SQLite. It computes the two fields `age` and `lifetime` through SQLite's [strftime](https://sqlite.org/lang_datefunc.html) function.\n\nThrough the [`extend projection`](https://cap.cloud.sap/docs/cds/cdl#extend-view) clause you can add additional fields to projection entities. These are deployed as database views, which is why we can integrate the database functions in the first place.\n",
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 7,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 11,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "SQLite implementation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "fiori/db/hana/index.cds",
|
|
||||||
"description": "#### SAP HANA Implementation\n\nThis is the second implementation for SAP HANA. It computes the same two fields `age` and `lifetime` through the [YEARS_BETWEEN](https://help.sap.com/viewer/7c78579ce9b14a669c1f3295b0d8ca16/Cloud/en-US/7c0d2c161ea34def86de3f5eadd6a0af.html) and [YEAR](https://help.sap.com/viewer/7c78579ce9b14a669c1f3295b0d8ca16/Cloud/en-US/20f5fac6751910148dabd3c6821f907d.html) functions of SAP HANA.\n\n#### File Layout and Code Structure\n\nNote the path of the `.cds` file we are in: it's in a subfolder of `db`, so that it's _not_ automatically picked up when we start the application. The same is true for the SQLite implementation: it's in a separate `db/sqlite/` folder as well. In the next step, you'll see how these files are loaded.\n\nAlso, we choose to implement all of that as an extension of the original bookshop here in the _fiori_ package. See the first [CAP Samples] code tour for more details on the different packages of this repository.",
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 7,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 11,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "SAP HANA implementation"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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.",
|
|
||||||
"line": 17,
|
|
||||||
"title": "Configuration"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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",
|
|
||||||
"line": 28,
|
|
||||||
"title": "Run with SQLite"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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.",
|
|
||||||
"line": 31,
|
|
||||||
"title": "Run with SAP HANA"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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).",
|
|
||||||
"line": 68,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 67,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 73,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Add Data"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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."
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"ref": "master"
|
|
||||||
}
|
|
||||||
@@ -1,136 +0,0 @@
|
|||||||
{
|
|
||||||
"$schema": "https://aka.ms/codetour-schema",
|
|
||||||
"title": "CAP Samples",
|
|
||||||
"steps": [
|
|
||||||
{
|
|
||||||
"title": "Welcome",
|
|
||||||
"file": "README.md",
|
|
||||||
"description": "### Welcome to CAP Samples!\n\nThis tour leads you through a collection of samples for the [SAP Cloud Application Programming Model (CAP)](https://cap.cloud.sap).\nYou will learn which features of the programming model are demonstrated in which sample.\n\nLet's start!",
|
|
||||||
"line": 2,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 3,
|
|
||||||
"character": 108
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "hello/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).",
|
|
||||||
"line": 2,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 4,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Hello World!"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "bookshop/db/schema.cds",
|
|
||||||
"description": "### A Bookshop!\n\nIntroduces:\n- [Project Setup](https://cap.cloud.sap/docs/get-started/) and [Layouts](https://cap.cloud.sap/docs/get-started/projects)\n- [Domain Modeling](https://cap.cloud.sap/docs/guides/domain-models)\n- [Defining Services](https://cap.cloud.sap/docs/guides/providing-services)\n- [Generic Providers](https://cap.cloud.sap/docs/guides/generic-providers)\n- [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl)\n- [Using Databases](https://cap.cloud.sap/docs/guides/databases)\n",
|
|
||||||
"line": 1,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 32,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Bookshop"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "common/index.cds",
|
|
||||||
"description": "### Extend and Reuse\n\nShowcases how to extend [@sap/cds/common](https://cap.cloud.sap/docs/cds/common) thereby covering:\n- Building [extension packages](https://cap.cloud.sap/docs/guides/domain-models#aspects-extensibility)\n- Providing [reuse packages](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content)\n- [Verticalization](https://cap.cloud.sap/docs/cds/common#adapting-to-your-needs)\n- Using [Aspects](https://cap.cloud.sap/docs/cds/cdl#aspects)\n- Used in the [fiori app sample](#fiori)\n",
|
|
||||||
"line": 1,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 46,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Common"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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",
|
|
||||||
"line": 1,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 27,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Orders"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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",
|
|
||||||
"line": 1,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 39,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Reviews"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"file": "fiori/app/index.cds",
|
|
||||||
"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",
|
|
||||||
"line": 1,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 1,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 13,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Fiori"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"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",
|
|
||||||
"line": 8,
|
|
||||||
"selection": {
|
|
||||||
"start": {
|
|
||||||
"line": 8,
|
|
||||||
"character": 1
|
|
||||||
},
|
|
||||||
"end": {
|
|
||||||
"line": 15,
|
|
||||||
"character": 1
|
|
||||||
}
|
|
||||||
},
|
|
||||||
"title": "Packages"
|
|
||||||
}
|
|
||||||
],
|
|
||||||
"isPrimary": true,
|
|
||||||
"description": "Overview of CAP Samples for Node.js"
|
|
||||||
}
|
|
||||||
21
.vscode/extensions.json
vendored
21
.vscode/extensions.json
vendored
@@ -1,21 +0,0 @@
|
|||||||
{
|
|
||||||
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
|
|
||||||
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
|
|
||||||
|
|
||||||
// List of extensions which should be recommended for users of this workspace.
|
|
||||||
"recommendations": [
|
|
||||||
"sapse.vscode-cds",
|
|
||||||
"dbaeumer.vscode-eslint",
|
|
||||||
"esbenp.prettier-vscode",
|
|
||||||
"mechatroner.rainbow-csv",
|
|
||||||
"humao.rest-client",
|
|
||||||
"alexcvzz.vscode-sqlite",
|
|
||||||
"hbenl.vscode-mocha-test-adapter",
|
|
||||||
"sdras.night-owl",
|
|
||||||
"vsls-contrib.codetour"
|
|
||||||
],
|
|
||||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
|
||||||
"unwantedRecommendations": [
|
|
||||||
|
|
||||||
]
|
|
||||||
}
|
|
||||||
48
.vscode/launch.json
vendored
48
.vscode/launch.json
vendored
@@ -4,37 +4,39 @@
|
|||||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||||
"version": "0.2.0",
|
"version": "0.2.0",
|
||||||
"configurations": [
|
"configurations": [
|
||||||
{
|
|
||||||
"name": "Attach by Process ID",
|
|
||||||
"processId": "${command:PickProcess}",
|
|
||||||
"request": "attach",
|
|
||||||
"skipFiles": [
|
|
||||||
"<node_internals>/**"
|
|
||||||
],
|
|
||||||
"type": "pwa-node"
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
"name": "bookshop",
|
"name": "cds run",
|
||||||
"command": "cds watch bookshop",
|
|
||||||
"request": "launch",
|
"request": "launch",
|
||||||
"type": "node-terminal",
|
"type": "node",
|
||||||
"skipFiles": ["<node_internals>/**"]
|
"runtimeExecutable": "npx",
|
||||||
},
|
"runtimeArgs": ["-n"],
|
||||||
{
|
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"], // the leading "--" arg ensures it works with as well as without debugging
|
||||||
"name": "Fiori app",
|
"cwd": "${workspaceFolder}/packages/${input:service}",
|
||||||
"command": "cds watch fiori",
|
"console": "integratedTerminal",
|
||||||
"request": "launch",
|
"serverReadyAction": {
|
||||||
"type": "node-terminal",
|
"pattern": "server listening on (https?://\\S+|[0-9]+)",
|
||||||
|
"uriFormat": "http://localhost:%s",
|
||||||
|
"action": "openExternally"
|
||||||
|
},
|
||||||
"skipFiles": ["<node_internals>/**"]
|
"skipFiles": ["<node_internals>/**"]
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"inputs": [
|
"inputs": [
|
||||||
{
|
{
|
||||||
"type": "pickString",
|
"type": "pickString",
|
||||||
"id": "sample",
|
"id": "service",
|
||||||
"description": "Which sample do you want to start?",
|
"description": "Which service do you want to start?",
|
||||||
"options": ["bookshop", "fiori", "reviews", "reviews/test/bookshop"],
|
"options": [
|
||||||
"default": "bookshop"
|
"bookshop",
|
||||||
|
"bookstore",
|
||||||
|
"media-server",
|
||||||
|
"office-supplies",
|
||||||
|
"orders-service",
|
||||||
|
"products-service",
|
||||||
|
"reviews-service",
|
||||||
|
"user-service"
|
||||||
|
],
|
||||||
|
"default": "bookstore"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|||||||
5
.vscode/settings.json
vendored
5
.vscode/settings.json
vendored
@@ -1,6 +1,7 @@
|
|||||||
{
|
{
|
||||||
"files.exclude": {
|
"files.exclude": {
|
||||||
"**/.gitignore": true,
|
"**/.gitignore": false,
|
||||||
"**/.vscode": true
|
"**/.git": false,
|
||||||
|
"**/.vscode": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
37
.vscode/tasks.json
vendored
37
.vscode/tasks.json
vendored
@@ -1,15 +1,26 @@
|
|||||||
{
|
{
|
||||||
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
// See https://go.microsoft.com/fwlink/?LinkId=733558
|
||||||
// for the documentation about the tasks.json format
|
// for the documentation about the tasks.json format
|
||||||
"version": "2.0.0",
|
"version": "2.0.0",
|
||||||
"tasks": [
|
"tasks": [
|
||||||
{
|
{
|
||||||
"type": "npm",
|
"type": "shell", "label": "cds run bookshop",
|
||||||
"script": "jest",
|
"command": "npx", "args": [ "cds", "watch", "packages/bookshop" ],
|
||||||
"group": {
|
"presentation": { "group": "A" },
|
||||||
"kind": "test",
|
"problemMatcher": []
|
||||||
"isDefault": true
|
},
|
||||||
}
|
{
|
||||||
}
|
"type": "shell", "label": "cds run bookshop-enhanced",
|
||||||
]
|
"command": "npx", "args": [ "cds", "watch", "packages/bookshop-enhanced" ],
|
||||||
|
"presentation": { "group": "A" },
|
||||||
|
"problemMatcher": []
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "shell", "label": "cds run reviews-service",
|
||||||
|
"command": "npx", "args": [ "cds", "watch", "packages/reviews-service" ],
|
||||||
|
"options": {"env": { "PORT":"5005" }},
|
||||||
|
"presentation": { "group": "A" },
|
||||||
|
"problemMatcher": []
|
||||||
|
}
|
||||||
|
]
|
||||||
}
|
}
|
||||||
398
LICENSE
398
LICENSE
@@ -1,208 +1,190 @@
|
|||||||
Apache License
|
SAP SAMPLE CODE LICENSE AGREEMENT
|
||||||
|
|
||||||
Version 2.0, January 2004
|
Please scroll down and read the following SAP Sample Code License Agreement
|
||||||
|
carefully ("Agreement"). By downloading, installing, or otherwise using the
|
||||||
http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
|
SAP sample code or any materials that accompany the sample code documentation
|
||||||
AND DISTRIBUTION
|
(collectively, the "Sample Code"), You agree that this Agreement forms a legally
|
||||||
|
binding agreement between You ("You" or "Your") and SAP SE, for and on behalf
|
||||||
1. Definitions.
|
of itself and its subsidiaries and affiliates (as defined in Section 15 of the
|
||||||
|
German Stock Corporation Act), and You agree to be bound by all of the terms
|
||||||
|
and conditions stated in this Agreement. If You are trying to access or download
|
||||||
|
the Sample Code on behalf of Your employer or as a consultant or agent of a
|
||||||
"License" shall mean the terms and conditions for use, reproduction, and distribution
|
third party (either "Your Company"), You represent and warrant that You have
|
||||||
as defined by Sections 1 through 9 of this document.
|
the authority to act on behalf of and bind Your Company to the terms of this
|
||||||
|
Agreement and everywhere in this Agreement that refers to 'You' or 'Your' shall
|
||||||
|
also include Your Company. If You do not agree to these terms, do not attempt
|
||||||
|
to access or use the Sample Code.
|
||||||
"Licensor" shall mean the copyright owner or entity authorized by the copyright
|
|
||||||
owner that is granting the License.
|
1. LICENSE: Subject to the terms of this Agreement, SAP grants You a nonexclusive,
|
||||||
|
non-transferable, non-sublicensable, revocable, royalty-free,
|
||||||
|
limited license to use, copy, and modify the Sample Code solely for Your internal
|
||||||
|
business purposes.
|
||||||
"Legal Entity" shall mean the union of the acting entity and all other entities
|
|
||||||
that control, are controlled by, or are under common control with that entity.
|
2. RESTRICTIONS: You must not use the Sample Code to: (a) impair, degrade or
|
||||||
For the purposes of this definition, "control" means (i) the power, direct
|
reduce the performance or security of any SAP products, services or related
|
||||||
or indirect, to cause the direction or management of such entity, whether
|
technology (collectively, "SAP Products"); (b) enable the bypassing or
|
||||||
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
|
circumventing of SAP's license restrictions and/or provide users with access to
|
||||||
of the outstanding shares, or (iii) beneficial ownership of such entity.
|
the SAP Products to which such users are not licensed; or (c) permit mass data
|
||||||
|
extraction from an SAP Product to a non-SAP Product, including use,
|
||||||
|
modification, saving or other processing of such data in the non-SAP Product.
|
||||||
|
Further, You must not: (i) provide or make the Sample Code available to any
|
||||||
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
|
third party other than your authorized employees, contractors and agents
|
||||||
granted by this License.
|
(collectively, “Representatives”) and solely to be used by Your Representatives
|
||||||
|
for Your own internal business purposes; ii) remove or modify any marks or
|
||||||
|
proprietary notices from the Sample Code; iii) assign this Agreement, or any
|
||||||
|
interest therein, to any third party; (iv) use any SAP name, trademark or logo
|
||||||
"Source" form shall mean the preferred form for making modifications, including
|
without the prior written authorization of SAP; or (v) use the Sample Code to
|
||||||
but not limited to software source code, documentation source, and configuration
|
modify an SAP Product or decompile, disassemble or reverse engineer an SAP
|
||||||
files.
|
Product (except to the extent permitted by applicable law). You are responsible
|
||||||
|
for any breach of the terms of this Agreement by You or Your Representatives.
|
||||||
|
|
||||||
|
3. INTELLECTUAL PROPERTY: SAP or its licensors retain all ownership and
|
||||||
"Object" form shall mean any form resulting from mechanical transformation
|
intellectual property rights in and to the Sample Code and SAP Products. In
|
||||||
or translation of a Source form, including but not limited to compiled object
|
exchange for the right to use, copy and modify the Sample Code provided under
|
||||||
code, generated documentation, and conversions to other media types.
|
this Agreement, You covenant not to assert any intellectual property rights in
|
||||||
|
or to any of Your products, services, or related technology that are based on
|
||||||
|
or incorporate the Sample Code against any individual or entity in respect of
|
||||||
|
any current or future SAP Products.
|
||||||
"Work" shall mean the work of authorship, whether in Source or Object form,
|
|
||||||
made available under the License, as indicated by a copyright notice that
|
4. SAP AND THIRD PARTY APIS: The Sample Code may include API (application
|
||||||
is included in or attached to the work (an example is provided in the Appendix
|
programming interface) calls to SAP and third-party products or services. The
|
||||||
below).
|
access or use of the third-party products and services to which the API calls
|
||||||
|
are directed may be subject to additional terms and conditions between you and
|
||||||
|
SAP or such third parties. You (and not SAP) are solely responsible for
|
||||||
|
understanding and complying with any additional terms and conditions that apply
|
||||||
"Derivative Works" shall mean any work, whether in Source or Object form,
|
to the access or use of those APIs and/or third-party products and services.
|
||||||
that is based on (or derived from) the Work and for which the editorial revisions,
|
SAP does not grant You any rights in or to these APIs, products or services
|
||||||
annotations, elaborations, or other modifications represent, as a whole, an
|
under this Agreement.
|
||||||
original work of authorship. For the purposes of this License, Derivative
|
|
||||||
Works shall not include works that remain separable from, or merely link (or
|
5. FREE AND OPEN SOURCE COMPONENTS: The Sample Code may include third party
|
||||||
bind by name) to the interfaces of, the Work and Derivative Works thereof.
|
free or open source components ("FOSS Components"). You may have additional
|
||||||
|
rights in such FOSS Components that are provided by the third party licensors
|
||||||
|
of those components.
|
||||||
|
6. THIRD PARTY DEPENDENCIES: The Sample Code may require third party software
|
||||||
"Contribution" shall mean any work of authorship, including the original version
|
dependencies ("Dependencies") for the use or operation of the Sample Code. These
|
||||||
of the Work and any modifications or additions to that Work or Derivative
|
Dependencies may be identified by SAP in Maven POM files, documentation or by
|
||||||
Works thereof, that is intentionally submitted to Licensor for inclusion in
|
other means. SAP does not grant You any rights in or to such Dependencies under
|
||||||
the Work by the copyright owner or by an individual or Legal Entity authorized
|
this Agreement. You are solely responsible for the acquisition, installation
|
||||||
to submit on behalf of the copyright owner. For the purposes of this definition,
|
and use of such Dependencies.
|
||||||
"submitted" means any form of electronic, verbal, or written communication
|
7. WARRANTY:
|
||||||
sent to the Licensor or its representatives, including but not limited to
|
a) If You are located outside the US or Canada: AS THE SAMPLE CODE IS PROVIDED
|
||||||
communication on electronic mailing lists, source code control systems, and
|
TO YOU FREE OF CHARGE, SAP DOES NOT GUARANTEE OR WARRANT ANY FEATURES OR
|
||||||
issue tracking systems that are managed by, or on behalf of, the Licensor
|
QUALITIES OF THE SAMPLE CODE OR GIVE ANY UNDERTAKING WITH REGARD TO ANY OTHER
|
||||||
for the purpose of discussing and improving the Work, but excluding communication
|
QUALITY. NO SUCH WARRANTY OR UNDERTAKING SHALL BE IMPLIED BY YOU FROM ANY
|
||||||
that is conspicuously marked or otherwise designated in writing by the copyright
|
DESCRIPTION IN THE SAMPLE CODE OR ANY OTHER MATERIALS, COMMUNICATION OR
|
||||||
owner as "Not a Contribution."
|
ADVERTISEMENT. IN PARTICULAR, SAP DOES NOT WARRANT THAT THE SAMPLE CODE WILL BE
|
||||||
|
AVAILABLE UNINTERRUPTED, ERROR FREE, OR PERMANENTLY AVAILABLE. ALL WARRANTY
|
||||||
|
CLAIMS RESPECTING THE SAMPLE CODE ARE SUBJECT TO THE LIMITATION OF LIABILITY
|
||||||
|
STIPULATED IN SECTION 8 BELOW.
|
||||||
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
|
b) If You are located in the US or Canada: THE SAMPLE CODE IS LICENSED TO YOU
|
||||||
of whom a Contribution has been received by Licensor and subsequently incorporated
|
"AS IS", WITHOUT ANY WARRANTY, ESCROW, TRAINING, MAINTENANCE, OR SERVICE
|
||||||
within the Work.
|
OBLIGATIONS WHATSOEVER ON THE PART OF SAP. SAP MAKES NO EXPRESS OR IMPLIED
|
||||||
|
WARRANTIES OR CONDITIONS OF SALE OF ANY TYPE WHATSOEVER, INCLUDING BUT NOT
|
||||||
2. Grant of Copyright License. Subject to the terms and conditions of this
|
LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY AND OF FITNESS FOR A PARTICULAR
|
||||||
License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
PURPOSE. IN PARTICULAR, SAP DOES NOT WARRANT THAT THE SAMPLE CODE WILL BE
|
||||||
no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
|
AVAILABLE UNINTERRUPTED, ERROR FREE, OR PERMANENTLY AVAILABLE. YOU ASSUME ALL
|
||||||
Derivative Works of, publicly display, publicly perform, sublicense, and distribute
|
RISKS ASSOCIATED WITH THE USE OF THE SAMPLE CODE, INCLUDING WITHOUT LIMITATION
|
||||||
the Work and such Derivative Works in Source or Object form.
|
RISKS RELATING TO QUALITY, AVAILABILITY, PERFORMANCE, DATA LOSS, AND UTILITY IN
|
||||||
|
A PRODUCTION ENVIRONMENT.
|
||||||
3. Grant of Patent License. Subject to the terms and conditions of this License,
|
c) For all locations: SAP DOES NOT MAKE ANY REPRESENTATIONS OR WARRANTIES IN
|
||||||
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
|
RESPECT OF THIRD PARTY DEPENDENCIES, APIS, PRODUCTS AND SERVICES, INCLUDING BUT
|
||||||
no-charge, royalty-free, irrevocable (except as stated in this section) patent
|
NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY AND OF FITNESS FOR A
|
||||||
license to make, have made, use, offer to sell, sell, import, and otherwise
|
PARTICULAR PURPOSE. IN PARTICULAR, SAP DOES NOT WARRANT THAT THIRDPARTY
|
||||||
transfer the Work, where such license applies only to those patent claims
|
DEPENDENCIES, APIS, PRODUCTS AND SERVICES WILL BE AVAILABLE, ERROR FREE,
|
||||||
licensable by such Contributor that are necessarily infringed by their Contribution(s)
|
INTEROPERABLE WITH THE SAMPLE CODE, SUITABLE FOR ANY PARTICULAR PURPOSE OR NONINFRINGING.
|
||||||
alone or by combination of their Contribution(s) with the Work to which such
|
YOU ASSUME ALL RISKS ASSOCIATED WITH THE USE OF THIRD
|
||||||
Contribution(s) was submitted. If You institute patent litigation against
|
PARTY DEPENDENCIES, APIS, PRODUCTS AND SERVICES, INCLUDING WITHOUT LIMITATION
|
||||||
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
RISKS RELATING TO QUALITY, AVAILABILITY, PERFORMANCE, DATA LOSS, UTILITY IN A
|
||||||
that the Work or a Contribution incorporated within the Work constitutes direct
|
PRODUCTION ENVIRONMENT, AND NON-INFRINGEMENT. IN NO EVENT WILL SAP BE LIABLE
|
||||||
or contributory patent infringement, then any patent licenses granted to You
|
DIRECTLY OR INDIRECTLY IN RESPECT OF ANY USE OF THIRD PARTY DEPENDENCIES, APIS,
|
||||||
under this License for that Work shall terminate as of the date such litigation
|
PRODUCTS AND SERVICES BY YOU.
|
||||||
is filed.
|
|
||||||
|
8. LIMITATION OF LIABILITY:
|
||||||
4. Redistribution. You may reproduce and distribute copies of the Work or
|
a) If You are located outside the US or Canada: IRRESPECTIVE OF THE LEGAL
|
||||||
Derivative Works thereof in any medium, with or without modifications, and
|
REASONS, SAP SHALL ONLY BE LIABLE FOR DAMAGES UNDER THIS AGREEMENT IF SUCH
|
||||||
in Source or Object form, provided that You meet the following conditions:
|
DAMAGE (I) CAN BE CLAIMED UNDER THE GERMAN PRODUCT LIABILITY ACT OR (II) IS
|
||||||
|
CAUSED BY INTENTIONAL MISCONDUCT OF SAP OR (III) CONSISTS OF PERSONAL INJURY.
|
||||||
(a) You must give any other recipients of the Work or Derivative Works a copy
|
IN ALL OTHER CASES, NEITHER SAP NOR ITS EMPLOYEES, AGENTS AND SUBCONTRACTORS
|
||||||
of this License; and
|
SHALL BE LIABLE FOR ANY KIND OF DAMAGE OR CLAIMS HEREUNDER.
|
||||||
|
b) If You are located in the US or Canada: IN NO EVENT SHALL SAP BE LIABLE TO
|
||||||
(b) You must cause any modified files to carry prominent notices stating that
|
YOU, YOUR COMPANY OR TO ANY THIRD PARTY FOR ANY DAMAGES IN AN AMOUNT IN EXCESS
|
||||||
You changed the files; and
|
OF $100 ARISING IN CONNECTION WITH YOUR USE OF OR INABILITY TO USE THE SAMPLE
|
||||||
|
CODE OR IN CONNECTION WITH SAP'S PROVISION OF OR FAILURE TO PROVIDE SERVICES
|
||||||
(c) You must retain, in the Source form of any Derivative Works that You distribute,
|
PERTAINING TO THE SAMPLE CODE, OR AS A RESULT OF ANY DEFECT IN THE SAMPLE COED.
|
||||||
all copyright, patent, trademark, and attribution notices from the Source
|
THIS DISCLAIMER OF LIABILITY SHALL APPLY REGARDLESS OF THE FORM OF ACTION THAT
|
||||||
form of the Work, excluding those notices that do not pertain to any part
|
MAY BE BROUGHT AGAINST SAP, WHETHER IN CONTRACT OR TORT, INCLUDING WITHOUT
|
||||||
of the Derivative Works; and
|
LIMITATION ANY ACTION FOR NEGLIGENCE. YOUR SOLE REMEDY IN THE EVENT OF BREACH
|
||||||
|
OF THIS AGREEMENT BY SAP OR FOR ANY OTHER CLAIM RELATED TO THE SAMPLE CODE SHALL
|
||||||
(d) If the Work includes a "NOTICE" text file as part of its distribution,
|
BE TERMINATION OF THIS AGREEMENT. NOTWITHSTANDING ANYTHING TO THE CONTRARY
|
||||||
then any Derivative Works that You distribute must include a readable copy
|
HEREIN, UNDER NO CIRCUMSTANCES SHALL SAP OR ITS LICENSORS BE LIABLE TO YOU OR
|
||||||
of the attribution notices contained within such NOTICE file, excluding those
|
ANY OTHER PERSON OR ENTITY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR
|
||||||
notices that do not pertain to any part of the Derivative Works, in at least
|
INDIRECT DAMAGES, LOSS OF GOOD WILL OR BUSINESS PROFITS, WORK STOPPAGE, DATA
|
||||||
one of the following places: within a NOTICE text file distributed as part
|
LOSS, COMPUTER FAILURE OR MALFUNCTION, ANY AND ALL OTHER COMMERCIAL DAMAGES OR
|
||||||
of the Derivative Works; within the Source form or documentation, if provided
|
LOSS, OR EXEMPLARY OR PUNITIVE DAMAGES.
|
||||||
along with the Derivative Works; or, within a display generated by the Derivative
|
|
||||||
Works, if and wherever such third-party notices normally appear. The contents
|
9. INDEMNITY: You will fully indemnify, hold harmless and defend SAP against
|
||||||
of the NOTICE file are for informational purposes only and do not modify the
|
law suits based on any claim: (a) that any of Your products, services or related
|
||||||
License. You may add Your own attribution notices within Derivative Works
|
technology that are based on or incorporate the Sample Code infringes or
|
||||||
that You distribute, alongside or as an addendum to the NOTICE text from the
|
misappropriates any patent, copyright, trademark, trade secrets, or other
|
||||||
Work, provided that such additional attribution notices cannot be construed
|
proprietary rights of a third party, or (b) related to Your alleged violation
|
||||||
as modifying the License.
|
of the terms of this Agreement.
|
||||||
|
|
||||||
You may add Your own copyright statement to Your modifications and may provide
|
10. EXPORT: The Sample Code is subject to German, EU and US export control
|
||||||
additional or different license terms and conditions for use, reproduction,
|
regulations. You confirm that: a) You will not use the Sample Code for, and
|
||||||
or distribution of Your modifications, or for any such Derivative Works as
|
will not allow the Sample Code to be used for, any purposes prohibited by
|
||||||
a whole, provided Your use, reproduction, and distribution of the Work otherwise
|
German, EU and US law, including, without limitation, for the development,
|
||||||
complies with the conditions stated in this License.
|
design, manufacture or production of nuclear, chemical or biological weapons of
|
||||||
|
mass destruction; b) You are not located in Cuba, Iran, Sudan, Iraq, North
|
||||||
5. Submission of Contributions. Unless You explicitly state otherwise, any
|
Korea, Syria, nor any other country to which the United States has prohibited
|
||||||
Contribution intentionally submitted for inclusion in the Work by You to the
|
export or that has been designated by the U.S. Government as a "terrorist
|
||||||
Licensor shall be under the terms and conditions of this License, without
|
supporting" country (any, an "US Embargoed Country"); c) You are not a citizen,
|
||||||
any additional terms or conditions. Notwithstanding the above, nothing herein
|
national or resident of, and are not under the control of, a US Embargoed
|
||||||
shall supersede or modify the terms of any separate license agreement you
|
Country; d) You will not download or otherwise export or re-export the Sample
|
||||||
may have executed with Licensor regarding such Contributions.
|
Code, directly or indirectly, to a US Embargoed Country nor to citizens,
|
||||||
|
nationals or residents of a US Embargoed Country; e) You are not listed on the
|
||||||
6. Trademarks. This License does not grant permission to use the trade names,
|
United States Department of Treasury lists of Specially Designated Nationals,
|
||||||
trademarks, service marks, or product names of the Licensor, except as required
|
Specially Designated Terrorists, and Specially Designated Narcotic Traffickers,
|
||||||
for reasonable and customary use in describing the origin of the Work and
|
nor listed on the United States Department of Commerce Table of Denial Orders
|
||||||
reproducing the content of the NOTICE file.
|
or any other U.S. government list of prohibited or restricted parties and f)
|
||||||
|
You will not download or otherwise export or re-export the Sample Code, directly
|
||||||
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
|
or indirectly, to persons on the above-mentioned lists.
|
||||||
in writing, Licensor provides the Work (and each Contributor provides its
|
|
||||||
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
|
11. SUPPORT: SAP does not offer support for the Sample Code.
|
||||||
KIND, either express or implied, including, without limitation, any warranties
|
|
||||||
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
|
12. TERM AND TERMINATION: You may terminate this Agreement by destroying all
|
||||||
A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
|
copies of the Sample Code in Your possession or control. SAP may terminate Your
|
||||||
of using or redistributing the Work and assume any risks associated with Your
|
license to use the Sample Code immediately if You fail to comply with any of
|
||||||
exercise of permissions under this License.
|
the terms of this Agreement, or, for SAP's convenience by providing you with
|
||||||
|
ten (10) days written notice of termination. In case of termination or
|
||||||
8. Limitation of Liability. In no event and under no legal theory, whether
|
expiration of this Agreement, You must immediately destroy all copies of the
|
||||||
in tort (including negligence), contract, or otherwise, unless required by
|
Sample Code in your possession or control. In the event Your Company is acquired
|
||||||
applicable law (such as deliberate and grossly negligent acts) or agreed to
|
(by merger, purchase of stock, assets or intellectual property or exclusive
|
||||||
in writing, shall any Contributor be liable to You for damages, including
|
license), or You become employed, by a direct competitor of SAP, then this
|
||||||
any direct, indirect, special, incidental, or consequential damages of any
|
Agreement and all licenses granted to You in this Agreement shall immediately
|
||||||
character arising as a result of this License or out of the use or inability
|
terminate upon the date of such acquisition or change of employment.
|
||||||
to use the Work (including but not limited to damages for loss of goodwill,
|
|
||||||
work stoppage, computer failure or malfunction, or any and all other commercial
|
13. LAW/VENUE:
|
||||||
damages or losses), even if such Contributor has been advised of the possibility
|
a) If You are located outside the US or Canada: This Agreement is governed by
|
||||||
of such damages.
|
and construed in accordance with the laws of Germany without reference to its
|
||||||
|
conflicts of law principles. You and SAP agree to submit to the exclusive
|
||||||
9. Accepting Warranty or Additional Liability. While redistributing the Work
|
jurisdiction of, and venue in, the courts located in Karlsruhe, Germany in any
|
||||||
or Derivative Works thereof, You may choose to offer, and charge a fee for,
|
dispute arising out of or relating to this Agreement or the Sample Code. The
|
||||||
acceptance of support, warranty, indemnity, or other liability obligations
|
United Nations Convention on Contracts for the International Sale of Goods shall
|
||||||
and/or rights consistent with this License. However, in accepting such obligations,
|
not apply to this Agreement.
|
||||||
You may act only on Your own behalf and on Your sole responsibility, not on
|
b) If You are located in the US or Canada: This Agreement shall be governed by
|
||||||
behalf of any other Contributor, and only if You agree to indemnify, defend,
|
and construed in accordance with the laws of the State of New York, USA without
|
||||||
and hold each Contributor harmless for any liability incurred by, or claims
|
reference to its conflicts of law principles. You and SAP agree to submit to
|
||||||
asserted against, such Contributor by reason of your accepting any such warranty
|
the exclusive jurisdiction of, and venue in, the courts located in New York,
|
||||||
or additional liability. END OF TERMS AND CONDITIONS
|
New York, USA in any dispute arising out of or relating to this Agreement or
|
||||||
|
the Sample Code. The United Nations Convention on Contracts for the
|
||||||
APPENDIX: How to apply the Apache License to your work.
|
International Sale of Goods shall not apply to this Agreement.
|
||||||
|
|
||||||
To apply the Apache License to your work, attach the following boilerplate
|
14. MISCELLANEOUS: This Agreement is the complete agreement between the parties
|
||||||
notice, with the fields enclosed by brackets "[]" replaced with your own identifying
|
respecting the Sample Code. This Agreement supersedes all prior or
|
||||||
information. (Don't include the brackets!) The text should be enclosed in
|
contemporaneous agreements or representations with regards to the Sample Code.
|
||||||
the appropriate comment syntax for the file format. We also recommend that
|
If any term of this Agreement is found to be invalid or unenforceable, the
|
||||||
a file or class name and description of purpose be included on the same "printed
|
surviving provisions shall remain effective. SAP's failure to enforce any right
|
||||||
page" as the copyright notice for easier identification within third-party
|
or provisions stipulated in this Agreement will not constitute a waiver of such
|
||||||
archives.
|
provision, or any other provision of this Agreement.
|
||||||
|
|
||||||
Copyright [yyyy] [name of copyright owner]
|
v1.0-071618
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
http://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
|
|
||||||
limitations under the License.
|
|
||||||
|
|||||||
1
NOTICE
Normal file
1
NOTICE
Normal file
@@ -0,0 +1 @@
|
|||||||
|
Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved.
|
||||||
81
README.md
81
README.md
@@ -1,87 +1,42 @@
|
|||||||
# Welcome to cap/samples
|
# Welcome to SAP Cloud Application Programming model samples
|
||||||
|
|
||||||
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo). → See [**Overview** of contained samples](samples.md)
|
Find here the samples for the openSAP course [Building Applications with the SAP Cloud Application Programming Model](https://open.sap.com/courses/cp7).
|
||||||
|
|
||||||

|
## Get Access to SAP Business Application Studio
|
||||||
<!--[](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples)-->
|
The recommended environment for the course is SAP Business Application Studio. Watch [unit 2 of week 1](https://open.sap.com/courses/cp7/items/51pzQUzbXHr2kdbOmVs6jI) for how to get access.
|
||||||
|
|
||||||
|
## Setup
|
||||||
|
|
||||||
### Preliminaries
|
In SAP Business Application Studio, open a terminal.
|
||||||
|
Then clone the repo with this specific branch:
|
||||||
1. Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/) globally:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm i -g @sap/cds-dk
|
|
||||||
```
|
|
||||||
|
|
||||||
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/tools#vscode)
|
|
||||||
|
|
||||||
### Download
|
|
||||||
|
|
||||||
If you've [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/master.zip).
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
git clone https://github.com/sap-samples/cloud-cap-samples samples
|
git clone https://github.com/sap-samples/cloud-cap-samples projects/cloud-cap-samples -b openSAP-week4-unit1-final
|
||||||
cd samples
|
cd projects/cloud-cap-samples
|
||||||
```
|
```
|
||||||
|
|
||||||
### Setup
|
In the `cloud-cap-samples` folder run:
|
||||||
|
|
||||||
In the samples folder run:
|
|
||||||
|
|
||||||
```sh
|
```sh
|
||||||
npm install
|
npm install
|
||||||
```
|
```
|
||||||
|
|
||||||
### Run
|
## Run
|
||||||
|
|
||||||
With that you're ready to run the samples, for example:
|
|
||||||
|
|
||||||
|
Now you're ready to run the samples, for example:
|
||||||
```sh
|
```sh
|
||||||
cds watch bookshop
|
cd packages/bookshop
|
||||||
|
cds watch
|
||||||
```
|
```
|
||||||
|
|
||||||
After that open this link in your browser: [http://localhost:4004](http://localhost:4004)
|
After that, watch out for the little popup in the lower right corner of SAP Business Application Studio that asks you to open the application in your browser.
|
||||||
|
|
||||||
When asked to log in, type `alice` as user and leave the password field blank, which is the [default user](https://cap.cloud.sap/docs/node.js/authentication#mocked).
|
|
||||||
|
|
||||||
### Testing
|
|
||||||
|
|
||||||
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npx jest
|
|
||||||
```
|
|
||||||
> While mocha is a bit smaller and faster, jest runs tests in parallel and isolation, which allows to run all tests.
|
|
||||||
|
|
||||||
|
|
||||||
### Serve `npm`
|
|
||||||
|
|
||||||
We've included a simple npm registry mock, which allows you to do an `npm install @capire/<package>` locally. Use it as follows:
|
|
||||||
|
|
||||||
1. Start the @capire registry:
|
|
||||||
```sh
|
|
||||||
npm run registry
|
|
||||||
```
|
|
||||||
> While running this will have `@capire:registry=http://localhost:4444` set with npmrc.
|
|
||||||
|
|
||||||
2. Install one of the @capire packages wherever you like, for example:
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm add @capire/common @capire/bookshop
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Code Tours
|
|
||||||
|
|
||||||
Take one of the [guided tours](.tours) in VS Code through our CAP samples and learn which CAP features are showcased by the different parts of the repository. Just install the [CodeTour extension](https://marketplace.visualstudio.com/items?itemName=vsls-contrib.codetour) for VS Code. We'll add more code tours in the future. Stay tuned!
|
|
||||||
|
|
||||||
## Get Support
|
## Get Support
|
||||||
|
|
||||||
Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br>
|
Check out the cap docs at https://cap.cloud.sap. <br>
|
||||||
In case you've a question, find a bug, or otherwise need support, use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce) to get more visibility.
|
In case you find a bug or need support, please [open an issue in here](https://github.com/SAP-samples/cloud-cap-samples/issues/new).
|
||||||
|
|
||||||
|
|
||||||
## License
|
## License
|
||||||
|
|
||||||
Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt) file.
|
Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under SAP Sample Code License Agreement, except as noted otherwise in the [LICENSE](/LICENSE) file.
|
||||||
|
|||||||
25
bookshop/.vscode/launch.json
vendored
25
bookshop/.vscode/launch.json
vendored
@@ -1,25 +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": "Attach to...",
|
|
||||||
"type": "node",
|
|
||||||
"request": "attach",
|
|
||||||
"processId": "${command:PickProcess}",
|
|
||||||
"skipFiles": ["<node_internals>/**"]
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "cds run",
|
|
||||||
"type": "node",
|
|
||||||
"request": "launch",
|
|
||||||
"runtimeExecutable": "npx",
|
|
||||||
"runtimeArgs": ["-n"],
|
|
||||||
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"],
|
|
||||||
"console": "integratedTerminal",
|
|
||||||
"skipFiles": ["<node_internals>/**"]
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
// Incorporate pre-build extensions from...
|
|
||||||
using from '@capire/common';
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
/* global Vue axios */ //> from vue.html
|
|
||||||
const $ = sel => document.querySelector(sel)
|
|
||||||
const GET = (url) => axios.get('/browse'+url)
|
|
||||||
const POST = (cmd,data) => axios.post('/browse'+cmd,data)
|
|
||||||
|
|
||||||
const books = new Vue ({
|
|
||||||
|
|
||||||
el:'#app',
|
|
||||||
|
|
||||||
data: {
|
|
||||||
list: [],
|
|
||||||
book: undefined,
|
|
||||||
order: { amount:1, succeeded:'', failed:'' }
|
|
||||||
},
|
|
||||||
|
|
||||||
methods: {
|
|
||||||
|
|
||||||
search: ({target:{value:v}}) => books.fetch(v && '&$search='+v),
|
|
||||||
|
|
||||||
async fetch (etc='') {
|
|
||||||
const {data} = await GET(`/ListOfBooks?$expand=genre,currency${etc}`)
|
|
||||||
books.list = data.value
|
|
||||||
},
|
|
||||||
|
|
||||||
async inspect (eve) {
|
|
||||||
const book = books.book = books.list [eve.currentTarget.rowIndex-1]
|
|
||||||
const res = await GET(`/Books/${book.ID}?$select=descr,stock,image`)
|
|
||||||
Object.assign (book, res.data)
|
|
||||||
books.order = { amount:1 }
|
|
||||||
setTimeout (()=> $('form > input').focus(), 111)
|
|
||||||
},
|
|
||||||
|
|
||||||
async submitOrder () {
|
|
||||||
const {book,order} = books, amount = parseInt (order.amount) || 1 // REVISIT: Okra should be less strict
|
|
||||||
try {
|
|
||||||
const res = await POST(`/submitOrder`, { amount, book: book.ID })
|
|
||||||
book.stock = res.data.stock
|
|
||||||
books.order = { amount, succeeded: `Successfully orderd ${amount} item(s).` }
|
|
||||||
} catch (e) {
|
|
||||||
books.order = { amount, failed: e.response.data.error.message }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// initially fill list of books
|
|
||||||
books.fetch()
|
|
||||||
@@ -1,65 +0,0 @@
|
|||||||
<!DOCTYPE html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<title> Capire Books </title>
|
|
||||||
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
|
||||||
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
|
|
||||||
<style>
|
|
||||||
.hovering tr:hover td { color:cyan; background: #123; cursor: pointer; }
|
|
||||||
.rating-stars { color:teal }
|
|
||||||
.succeeded { color:teal }
|
|
||||||
.failed { color:red }
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
|
|
||||||
<body class="small-container", style="margin-top: 70px;">
|
|
||||||
<div id='app'>
|
|
||||||
|
|
||||||
<h1> {{ document.title }} </h1>
|
|
||||||
|
|
||||||
<input type="text" placeholder="Search..." @input="search">
|
|
||||||
|
|
||||||
<table id='books' class="hovering">
|
|
||||||
<thead>
|
|
||||||
<th> Book </th>
|
|
||||||
<th> Author </th>
|
|
||||||
<th> Genre </th>
|
|
||||||
<th> Rating </th>
|
|
||||||
<th> Price </th>
|
|
||||||
</thead>
|
|
||||||
<tr v-for="book in list" v-bind:id="book.ID" v-on:click="inspect">
|
|
||||||
<td>{{ book.title }}</td>
|
|
||||||
<td>{{ book.author }}</td>
|
|
||||||
<td>{{ book.genre.name }}</td>
|
|
||||||
<td class="rating-stars">
|
|
||||||
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }}
|
|
||||||
</td>
|
|
||||||
<td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
|
|
||||||
<div v-if="book">
|
|
||||||
<img v-bind:src="book.image" alt=""/>
|
|
||||||
<label style="text-align:right">
|
|
||||||
<span class="succeeded"> {{ order.succeeded }} </span>
|
|
||||||
<span class="failed"> {{ order.failed }} </span>
|
|
||||||
{{ book.stock }} in stock
|
|
||||||
</label>
|
|
||||||
<form @submit.prevent="submitOrder" style="float:right; display:flex; flex-direction:row-reverse">
|
|
||||||
<input type="number" v-model="order.amount" v-bind:class="{ failed: order.failed }" style="width:5em">
|
|
||||||
<input type="submit" value="Order:" class="muted-button">
|
|
||||||
</form>
|
|
||||||
<h4> {{ book.title }} </h4>
|
|
||||||
<p> {{ book.descr }} </p>
|
|
||||||
</div>
|
|
||||||
<div v-else>
|
|
||||||
( click on a row to see details... )
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
<script src="app.js"></script>
|
|
||||||
</html>
|
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
ID;parent_ID;name
|
|
||||||
10;;Fiction
|
|
||||||
11;10;Drama
|
|
||||||
12;10;Poetry
|
|
||||||
13;10;Fantasy
|
|
||||||
14;10;Science Fiction
|
|
||||||
15;10;Romance
|
|
||||||
16;10;Mystery
|
|
||||||
17;10;Thriller
|
|
||||||
18;10;Dystopia
|
|
||||||
19;10;Fairy Tale
|
|
||||||
20;;Non-Fiction
|
|
||||||
21;20;Biography
|
|
||||||
22;21;Autobiography
|
|
||||||
23;20;Essay
|
|
||||||
24;20;Speech
|
|
||||||
|
@@ -1,31 +0,0 @@
|
|||||||
using { Currency, managed, sap } from '@sap/cds/common';
|
|
||||||
namespace sap.capire.bookshop;
|
|
||||||
|
|
||||||
entity Books : managed {
|
|
||||||
key ID : Integer;
|
|
||||||
title : localized String(111);
|
|
||||||
descr : localized String(1111);
|
|
||||||
author : Association to Authors;
|
|
||||||
genre : Association to Genres;
|
|
||||||
stock : Integer;
|
|
||||||
price : Decimal;
|
|
||||||
currency : Currency;
|
|
||||||
image : LargeBinary @Core.MediaType : 'image/png';
|
|
||||||
}
|
|
||||||
|
|
||||||
entity Authors : managed {
|
|
||||||
key ID : Integer;
|
|
||||||
name : String(111);
|
|
||||||
dateOfBirth : Date;
|
|
||||||
dateOfDeath : Date;
|
|
||||||
placeOfBirth : String;
|
|
||||||
placeOfDeath : String;
|
|
||||||
books : Association to many Books on books.author = $self;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Hierarchically organized Code List for Genres */
|
|
||||||
entity Genres : sap.common.CodeList {
|
|
||||||
key ID : Integer;
|
|
||||||
parent : Association to Genres;
|
|
||||||
children : Composition of many Genres on children.parent = $self;
|
|
||||||
}
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
namespace sap.capire.bookshop; //> important for reflection
|
|
||||||
using from './db/schema';
|
|
||||||
using from './srv/cat-service';
|
|
||||||
using from './srv/admin-service';
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
exports.CatalogService = require('./srv/cat-service')
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@capire/bookshop",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"description": "A simple self-contained bookshop service.",
|
|
||||||
"dependencies": {
|
|
||||||
"@capire/common": "*",
|
|
||||||
"@sap/cds": "^4",
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"passport": "0.4.1"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"genres": "cds serve test/genres.cds",
|
|
||||||
"start": "cds run",
|
|
||||||
"watch": "cds watch"
|
|
||||||
},
|
|
||||||
"cds": {
|
|
||||||
"requires": {
|
|
||||||
"db": {
|
|
||||||
"kind": "sql"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
# Bookshop Getting Started Sample
|
|
||||||
|
|
||||||
This stand-alone sample introduces the essential tasks in the development of CAP-based services as also covered in the [Getting Started guide in capire](https://cap.cloud.sap/docs/get-started/in-a-nutshell).
|
|
||||||
|
|
||||||
## Hypothetical Use Cases
|
|
||||||
|
|
||||||
1. Build a service that allows to browse _Books_ and _Authors_.
|
|
||||||
2. Books have assigned _Genres_, which are organized hierarchically.
|
|
||||||
3. All users may browse books without login.
|
|
||||||
4. All entries are maintained by Administrators.
|
|
||||||
5. End users may order books (the actual order mgmt being out of scope).
|
|
||||||
|
|
||||||
## Running the Sample
|
|
||||||
|
|
||||||
```sh
|
|
||||||
npm run watch
|
|
||||||
```
|
|
||||||
|
|
||||||
## Content & Best Practices
|
|
||||||
|
|
||||||
| Links to capire | Sample files / folders |
|
|
||||||
| --------------------------------------------------------------------------------------------------------- | ------------------------------------ |
|
|
||||||
| [Project Setup & Layouts](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./`](./) |
|
|
||||||
| [Domain Modeling with CDS](https://cap.cloud.sap/docs/guides/domain-models) | [`./db/schema.cds`](./db/schema.cds) |
|
|
||||||
| [Defining Services](https://cap.cloud.sap/docs/guides/services#defining-services) | [`./srv/*.cds`](./srv) |
|
|
||||||
| [Single-purposed Services](https://cap.cloud.sap/docs/guides/services#single-purposed-services) | [`./srv/*.cds`](./srv) |
|
|
||||||
| [Providing & Consuming Providers](https://cap.cloud.sap/docs/guides/providing-services) | http://localhost:4004 |
|
|
||||||
| [Using Databases](https://cap.cloud.sap/docs/guides/databases) | [`./db/data/*.csv`](./db/data) |
|
|
||||||
| [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) | [`./srv/*.js`](./srv) |
|
|
||||||
| Adding Tests | [`./test`](./test) |
|
|
||||||
| [Sharing for Reuse](https://cap.cloud.sap/docs/guides/reuse-and-compose) | [`./index.cds`](./index.cds) |
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
using { sap.capire.bookshop as my } from '../db/schema';
|
|
||||||
service AdminService @(requires:'admin') {
|
|
||||||
entity Books as projection on my.Books;
|
|
||||||
entity Authors as projection on my.Authors;
|
|
||||||
}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
const cds = require('@sap/cds')
|
|
||||||
|
|
||||||
module.exports = cds.service.impl (function(){
|
|
||||||
this.before ('NEW','Authors', genid)
|
|
||||||
this.before ('NEW','Books', genid)
|
|
||||||
})
|
|
||||||
|
|
||||||
/** Generate primary keys for target entity in request */
|
|
||||||
async function genid (req) {
|
|
||||||
const {ID} = await cds.tx(req).run (SELECT.one.from(req.target).columns('max(ID) as ID'))
|
|
||||||
req.data.ID = ID - ID % 100 + 100 + 1
|
|
||||||
}
|
|
||||||
@@ -1,14 +0,0 @@
|
|||||||
using { sap.capire.bookshop as my } from '../db/schema';
|
|
||||||
service CatalogService @(path:'/browse') {
|
|
||||||
|
|
||||||
@readonly entity Books as SELECT from my.Books { *,
|
|
||||||
author.name as author
|
|
||||||
} excluding { createdBy, modifiedBy };
|
|
||||||
|
|
||||||
@readonly entity ListOfBooks as SELECT from Books
|
|
||||||
excluding { descr };
|
|
||||||
|
|
||||||
@requires: 'authenticated-user'
|
|
||||||
action submitOrder ( book: Books:ID, amount: Integer ) returns { stock: Integer };
|
|
||||||
event OrderedBook : { book: Books:ID; amount: Integer; buyer: String };
|
|
||||||
}
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
const cds = require('@sap/cds')
|
|
||||||
const { Books } = cds.entities ('sap.capire.bookshop')
|
|
||||||
|
|
||||||
class CatalogService extends cds.ApplicationService { init(){
|
|
||||||
|
|
||||||
// Reduce stock of ordered books if available stock suffices
|
|
||||||
this.on ('submitOrder', async req => {
|
|
||||||
const {book,amount} = req.data, tx = cds.tx(req)
|
|
||||||
let {stock} = await tx.read('stock').from(Books,book)
|
|
||||||
if (stock >= amount) {
|
|
||||||
await tx.update (Books,book).with ({ stock: stock -= amount })
|
|
||||||
await this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
|
|
||||||
return { stock }
|
|
||||||
}
|
|
||||||
else return req.error (409,`${amount} exceeds stock for book #${book}`)
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add some discount for overstocked books
|
|
||||||
this.after ('READ','Books', each => {
|
|
||||||
if (each.stock > 111) {
|
|
||||||
each.title += ` -- 11% discount!`
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
return super.init()
|
|
||||||
}}
|
|
||||||
|
|
||||||
module.exports = { CatalogService }
|
|
||||||
@@ -1,4 +0,0 @@
|
|||||||
using { sap.capire.bookshop as my } from '../db/schema';
|
|
||||||
service TestService {
|
|
||||||
entity Genres as projection on my.Genres;
|
|
||||||
}
|
|
||||||
@@ -1,38 +0,0 @@
|
|||||||
#################################################
|
|
||||||
#
|
|
||||||
# Genres
|
|
||||||
#
|
|
||||||
|
|
||||||
GET http://localhost:4004/test/Genres?
|
|
||||||
###
|
|
||||||
|
|
||||||
GET http://localhost:4004/test/Genres?
|
|
||||||
&$filter=parent_ID eq null&$select=name
|
|
||||||
&$expand=children($select=name)
|
|
||||||
###
|
|
||||||
|
|
||||||
POST http://localhost:4004/test/Genres?
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{ "ID":100, "name":"Some Sample Genres...", "children":[
|
|
||||||
{ "ID":101, "name":"Cat", "children":[
|
|
||||||
{ "ID":102, "name":"Kitty", "children":[
|
|
||||||
{ "ID":103, "name":"Kitty Cat", "children":[
|
|
||||||
{ "ID":104, "name":"Aristocat" } ]},
|
|
||||||
{ "ID":105, "name":"Kitty Bat" } ]},
|
|
||||||
{ "ID":106, "name":"Catwoman", "children":[
|
|
||||||
{ "ID":107, "name":"Catalina" } ]} ]},
|
|
||||||
{ "ID":108, "name":"Catweazle" }
|
|
||||||
]}
|
|
||||||
###
|
|
||||||
|
|
||||||
GET http://localhost:4004/test/Genres(100)?
|
|
||||||
# &$expand=children
|
|
||||||
# &$expand=children($expand=children($expand=children($expand=children)))
|
|
||||||
###
|
|
||||||
|
|
||||||
DELETE http://localhost:4004/test/Genres(103)
|
|
||||||
###
|
|
||||||
|
|
||||||
DELETE http://localhost:4004/test/Genres(100)
|
|
||||||
###
|
|
||||||
@@ -1,82 +0,0 @@
|
|||||||
@server = http://localhost:4004
|
|
||||||
@me = Authorization: Basic {{$processEnv USER}}:
|
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Get service info
|
|
||||||
GET {{server}}/browse
|
|
||||||
{{me}}
|
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Get $metadata document
|
|
||||||
GET {{server}}/browse/$metadata
|
|
||||||
{{me}}
|
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Browse Books as any user
|
|
||||||
GET {{server}}/browse/Books?
|
|
||||||
# &$select=title,stock
|
|
||||||
# &$expand=currency
|
|
||||||
# &sap-language=de
|
|
||||||
{{me}}
|
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Fetch Authors as admin
|
|
||||||
GET {{server}}/admin/Authors?
|
|
||||||
# &$select=name,dateOfBirth,placeOfBirth
|
|
||||||
# &$expand=books($select=title;$expand=currency)
|
|
||||||
# &$filter=ID eq 101
|
|
||||||
# &sap-language=de
|
|
||||||
Authorization: Basic alice:
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Create book
|
|
||||||
POST {{server}}/admin/Books
|
|
||||||
Content-Type: application/json;IEEE754Compatible=true
|
|
||||||
Authorization: Basic alice:
|
|
||||||
|
|
||||||
{
|
|
||||||
"ID": 2,
|
|
||||||
"title": "Poems : Pocket Poets",
|
|
||||||
"descr": "The Everyman's Library Pocket Poets hardcover series is popular for its compact size and reasonable price which does not compromise content. Poems: Bronte contains poems that demonstrate a sensibility elemental in its force with an imaginative discipline and flexibility of the highest order. Also included are an Editor's Note and an index of first lines.",
|
|
||||||
"author": { "ID": 101 },
|
|
||||||
"genre": { "ID": 12 },
|
|
||||||
"stock": 5,
|
|
||||||
"price": "12.05",
|
|
||||||
"currency": { "code": "USD" }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Put image to books
|
|
||||||
PUT {{server}}/admin/Books(2)/image
|
|
||||||
Content-Type: image/png
|
|
||||||
Authorization: Basic alice:
|
|
||||||
|
|
||||||
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAGwElEQVR4Ae3cwZFbNxBFUY5rkrDTmKAUk5QT03Aa44U22KC7NHptw+DRikVAXf8fzC3u8Hj4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgZzAW26USQT+e4HPx+Mz+RRvj0e0kT+SD2cWAQK1gOBqH6sEogKCi3IaRqAWEFztY5VAVEBwUU7DCNQCgqt9rBKICgguymkYgVpAcLWPVQJRAcFFOQ0jUAsIrvaxSiAqILgop2EEagHB1T5WCUQFBBflNIxALSC42scqgaiA4KKchhGoBQRX+1glEBUQXJTTMAK1gOBqH6sEogKCi3IaRqAWeK+Xb1z9iN558fHxcSPS9p2ezx/ROz4e4TtIHt+3j/61hW9f+2+7/+UXbifjewIDAoIbQDWSwE5AcDsZ3xMYEBDcAKqRBHYCgtvJ+J7AgIDgBlCNJLATENxOxvcEBgQEN4BqJIGdgOB2Mr4nMCAguAFUIwnsBAS3k/E9gQEBwQ2gGklgJyC4nYzvCQwICG4A1UgCOwHB7WR8T2BAQHADqEYS2AkIbifjewIDAoIbQDWSwE5AcDsZ3xMYEEjfTzHwiK91B8npd6Q8n8/oGQ/ckRJ9vvQwv3BpUfMIFAKCK3AsEUgLCC4tah6BQkBwBY4lAmkBwaVFzSNQCAiuwLFEIC0guLSoeQQKAcEVOJYIpAUElxY1j0AhILgCxxKBtIDg0qLmESgEBFfgWCKQFhBcWtQ8AoWA4AocSwTSAoJLi5pHoBAQXIFjiUBaQHBpUfMIFAKCK3AsEUgLCC4tah6BQmDgTpPsHSTFs39p6fQ7Q770UsV/Ov19X+2OFL9wxR+rJQJpAcGlRc0jUAgIrsCxRCAtILi0qHkECgHBFTiWCKQFBJcWNY9AISC4AscSgbSA4NKi5hEoBARX4FgikBYQXFrUPAKFgOAKHEsE0gKCS4uaR6AQEFyBY4lAWkBwaVHzCBQCgitwLBFICwguLWoegUJAcAWOJQJpAcGlRc0jUAgIrsCxRCAt8J4eePq89B0ar3ZnyOnve/rfn1+400/I810lILirjtPLnC4guNNPyPNdJSC4q47Ty5wuILjTT8jzXSUguKuO08ucLiC400/I810lILirjtPLnC4guNNPyPNdJSC4q47Ty5wuILjTT8jzXSUguKuO08ucLiC400/I810lILirjtPLnC4guNNPyPNdJSC4q47Ty5wuILjTT8jzXSUguKuO08ucLiC400/I810l8JZ/m78+szP/zI47fJo7Q37vgJ7PHwN/07/3TOv/9gu3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhg4P6H9J0maYHXuiMlrXf+vOfA33Turf3C5SxNItAKCK4lsoFATkBwOUuTCLQCgmuJbCCQExBcztIkAq2A4FoiGwjkBASXszSJQCsguJbIBgI5AcHlLE0i0AoIriWygUBOQHA5S5MItAKCa4lsIJATEFzO0iQCrYDgWiIbCOQEBJezNIlAKyC4lsgGAjkBweUsTSLQCgiuJbKBQE5AcDlLkwi0Akff//Dz6U+/I6U1/sUNr3bnytl3kPzi4bXb/cK1RDYQyAkILmdpEoFWQHAtkQ0EcgKCy1maRKAVEFxLZAOBnIDgcpYmEWgFBNcS2UAgJyC4nKVJBFoBwbVENhDICQguZ2kSgVZAcC2RDQRyAoLLWZpEoBUQXEtkA4GcgOByliYRaAUE1xLZQCAnILicpUkEWgHBtUQ2EMgJCC5naRKBVkBwLZENBHIC/4M7TXIv+3PS22d24qvdQfL3C/7N5P5i/MLlLE0i0AoIriWygUBOQHA5S5MItAKCa4lsIJATEFzO0iQCrYDgWiIbCOQEBJezNIlAKyC4lsgGAjkBweUsTSLQCgiuJbKBQE5AcDlLkwi0AoJriWwgkBMQXM7SJAKtgOBaIhsI5AQEl7M0iUArILiWyAYCOQHB5SxNItAKCK4lsoFATkBwOUuTCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIDAvyrwDySEJ2VQgUSoAAAAAElFTkSuQmCC
|
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Reading image from from the server directly
|
|
||||||
GET {{server}}/browse/Books(2)/image
|
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Submit Order as authenticated user
|
|
||||||
# (send that three times to get out-of-stock message)
|
|
||||||
POST {{server}}/browse/submitOrder
|
|
||||||
Content-Type: application/json
|
|
||||||
{{me}}
|
|
||||||
|
|
||||||
{ "book":201, "amount":5 }
|
|
||||||
|
|
||||||
|
|
||||||
### ------------------------------------------------------------------------
|
|
||||||
# Browse Genres
|
|
||||||
GET {{server}}/browse/Genres?
|
|
||||||
# &$filter=parent_ID eq null&$select=name
|
|
||||||
# &$expand=children($select=name)
|
|
||||||
{{me}}
|
|
||||||
@@ -1,12 +0,0 @@
|
|||||||
code;name;descr
|
|
||||||
AU;Australia;Commonwealth of Australia
|
|
||||||
CA;Canada;Canada
|
|
||||||
CN;China;People's Republic of China (PRC)
|
|
||||||
FR;France;French Republic
|
|
||||||
DE;Germany;Federal Republic of Germany
|
|
||||||
IN;India;Republic of India
|
|
||||||
IL;Israel;State of Israel
|
|
||||||
MM;Myanmar;Republic of the Union of Myanmar
|
|
||||||
GB;United Kingdom;United Kingdom of Great Britain and Northern Ireland
|
|
||||||
US;United States;United States of America (USA)
|
|
||||||
EU;European Union;European Union
|
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
code;locale;name;descr
|
|
||||||
AU;de;Australien;Commonwealth Australien
|
|
||||||
CA;de;Kanada;Canada
|
|
||||||
CN;de;China;Volksrepublik China
|
|
||||||
FR;de;Frankreich;Republik Frankreich
|
|
||||||
DE;de;Deutschland;Bundesrepublik Deutschland
|
|
||||||
IN;de;Indien;Republik Indien
|
|
||||||
IL;de;Israel;Staat Israel
|
|
||||||
MM;de;Myanmar;Republik der Union Myanmar
|
|
||||||
GB;de;Vereinigtes Königreich;Vereinigtes Königreich Großbritannien und Nordirland
|
|
||||||
US;de;Vereinigte Staaten;Vereinigte Staaten von Amerika
|
|
||||||
EU;de;Europäische Union;Europäische Union
|
|
||||||
|
@@ -1,12 +0,0 @@
|
|||||||
code;symbol;name;descr;numcode;minor;exponent
|
|
||||||
EUR;€;Euro;European Euro;978;Cent;2
|
|
||||||
USD;$;US Dollar;United States Dollar;840;Cent;2
|
|
||||||
CAD;$;Canadian Dollar;Canadian Dollar;124;Cent;2
|
|
||||||
AUD;$;Australian Dollar;Australian Dollar;036;Cent;2
|
|
||||||
GBP;£;British Pound;Great Britain Pound;826;Penny;2
|
|
||||||
ILS;₪;Shekel;Israeli New Shekel;376;Agorat;2
|
|
||||||
INR;₹;Rupee;Indian Rupee;356;Paise;2
|
|
||||||
QAR;﷼;Riyal;Katar Riyal;356;Dirham;2
|
|
||||||
SAR;﷼;Riyal;Saudi Riyal;682;Halala;2
|
|
||||||
JPY;¥;Yen;Japanese Yen;392;Sen;2
|
|
||||||
CNY;¥;Yuan;Chinese Yuan Renminbi;156;Jiao;1
|
|
||||||
|
@@ -1,5 +0,0 @@
|
|||||||
code;name
|
|
||||||
de;German
|
|
||||||
fr;French
|
|
||||||
en;English
|
|
||||||
en_GB;British English
|
|
||||||
|
@@ -1,10 +0,0 @@
|
|||||||
code;locale;name
|
|
||||||
de;en;German
|
|
||||||
de;de;Deutsch
|
|
||||||
de;fr;Allemande
|
|
||||||
fr;en;French
|
|
||||||
fr;de;Französisch
|
|
||||||
fr;fr;Francais
|
|
||||||
en;en;English
|
|
||||||
en;de;Englisch
|
|
||||||
en;fr;Anglais
|
|
||||||
|
@@ -1,45 +0,0 @@
|
|||||||
using { sap } from '@sap/cds/common';
|
|
||||||
|
|
||||||
extend sap.common.Currencies with {
|
|
||||||
// Currencies.code = ISO 4217 alphabetic three-letter code
|
|
||||||
// with the first two letters being equal to ISO 3166 alphabetic country codes
|
|
||||||
// See also:
|
|
||||||
// [1] https://www.iso.org/iso-4217-currency-codes.html
|
|
||||||
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
|
|
||||||
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
|
|
||||||
numcode : Integer;
|
|
||||||
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
|
|
||||||
minor : String; //> e.g. 'Cent'
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* The Code Lists below are designed as optional extensions to
|
|
||||||
* the base schema. Switch them on by adding an Association to
|
|
||||||
* one of the code list entities in your models or by:
|
|
||||||
* annotate sap.common.Countries with @cds.persistence.skip:false;
|
|
||||||
*/
|
|
||||||
|
|
||||||
context sap.common_countries {
|
|
||||||
|
|
||||||
extend sap.common.Countries {
|
|
||||||
regions : Composition of many Regions on regions._parent = $self.code;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity Regions : sap.common.CodeList {
|
|
||||||
key code : String(5); // ISO 3166-2 alpha5 codes, e.g. DE-BW
|
|
||||||
children : Composition of many Regions on children._parent = $self.code;
|
|
||||||
cities : Composition of many Cities on cities.region = $self;
|
|
||||||
_parent : String(11);
|
|
||||||
}
|
|
||||||
entity Cities : sap.common.CodeList {
|
|
||||||
key code : String(11);
|
|
||||||
region : Association to Regions;
|
|
||||||
districts : Composition of many Districts on districts.city = $self;
|
|
||||||
}
|
|
||||||
entity Districts : sap.common.CodeList {
|
|
||||||
key code : String(11);
|
|
||||||
city : Association to Cities;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@capire/common",
|
|
||||||
"description": "Provides a pre-built extension package for std @sap/cds/common",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@sap/cds": "*"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
# cds.requires.messaging.kind = file-based-messaging
|
|
||||||
PORT = 4004
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
Books = Books
|
|
||||||
Book = Book
|
|
||||||
ID = ID
|
|
||||||
Title = Title
|
|
||||||
Author = Author
|
|
||||||
AuthorID = Author ID
|
|
||||||
Stock = Stock
|
|
||||||
Name = Name
|
|
||||||
AuthorName = Author's Name
|
|
||||||
DateOfBirth = Date of Birth
|
|
||||||
DateOfDeath = Date of Death
|
|
||||||
PlaceOfBirth = Place of Birth
|
|
||||||
PlaceOfDeath = Place of Death
|
|
||||||
Age = Age
|
|
||||||
Authors = Authors
|
|
||||||
Order = Order
|
|
||||||
Orders = Orders
|
|
||||||
Price = Price
|
|
||||||
|
|
||||||
Genre = Genre
|
|
||||||
Genres = Genres
|
|
||||||
SubGenres = Sub Genres
|
|
||||||
|
|
||||||
NumCode = Numeric Code
|
|
||||||
MinorUnit = Minor Unit
|
|
||||||
Exponent = Exponent
|
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
using { AdminService } from '../../db';
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Books Object Page
|
|
||||||
//
|
|
||||||
|
|
||||||
annotate AdminService.Books with @(
|
|
||||||
UI: {
|
|
||||||
Facets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'},
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Translations}', Target: 'texts/@UI.LineItem'},
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'},
|
|
||||||
],
|
|
||||||
FieldGroup#General: {
|
|
||||||
Data: [
|
|
||||||
{Value: title},
|
|
||||||
{Value: author_ID},
|
|
||||||
{Value: genre_ID},
|
|
||||||
{Value: descr},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
FieldGroup#Details: {
|
|
||||||
Data: [
|
|
||||||
{Value: stock},
|
|
||||||
{Value: price},
|
|
||||||
{Value: currency_code, Label: '{i18n>Currency}'},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
FieldGroup#Admin: {
|
|
||||||
Data: [
|
|
||||||
{Value: createdBy},
|
|
||||||
{Value: createdAt},
|
|
||||||
{Value: modifiedBy},
|
|
||||||
{Value: modifiedAt}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
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}'},
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Draft for Localized Data
|
|
||||||
//
|
|
||||||
|
|
||||||
annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
|
|
||||||
annotate AdminService.Books with @odata.draft.enabled;
|
|
||||||
|
|
||||||
annotate AdminService.Books_texts with @(
|
|
||||||
UI: {
|
|
||||||
Identification: [{Value:title}],
|
|
||||||
SelectionFields: [ locale, title ],
|
|
||||||
LineItem: [
|
|
||||||
{Value: locale, Label: 'Locale'},
|
|
||||||
{Value: title, Label: 'Title'},
|
|
||||||
{Value: descr, Label: 'Description'},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
// Add Value Help for Locales
|
|
||||||
annotate AdminService.Books_texts {
|
|
||||||
locale @ValueList:{entity:'Languages',type:#fixed}
|
|
||||||
}
|
|
||||||
// In addition we need to expose Languages through AdminService
|
|
||||||
using { sap } from '@sap/cds/common';
|
|
||||||
extend service AdminService {
|
|
||||||
entity Languages as projection on sap.common.Languages;
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
|
|
||||||
"use strict";
|
|
||||||
return AppComponent.extend("admin.Component", {
|
|
||||||
metadata: { manifest: "json" }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/* eslint no-undef:0 */
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0;url=bookshop/index.html">
|
|
||||||
</head>
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
|
|
||||||
"use strict";
|
|
||||||
return AppComponent.extend("bookshop.Component", {
|
|
||||||
metadata: { manifest: "json" }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
/* eslint no-undef:0 */
|
|
||||||
@@ -1,257 +0,0 @@
|
|||||||
/*
|
|
||||||
Common Annotations shared by all apps
|
|
||||||
*/
|
|
||||||
|
|
||||||
using { sap.capire.bookshop as my } from '@capire/bookshop';
|
|
||||||
using { sap.common } from '@capire/common';
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Books Lists
|
|
||||||
//
|
|
||||||
annotate my.Books with @(
|
|
||||||
Common.SemanticKey: [title],
|
|
||||||
UI: {
|
|
||||||
Identification: [{Value:title}],
|
|
||||||
SelectionFields: [ ID, author_ID, price, currency_code ],
|
|
||||||
LineItem: [
|
|
||||||
{Value: ID},
|
|
||||||
{Value: title},
|
|
||||||
{Value: author.name, Label:'{i18n>Author}'},
|
|
||||||
{Value: genre.name},
|
|
||||||
{Value: stock},
|
|
||||||
{Value: price},
|
|
||||||
{Value: currency.symbol, Label:' '},
|
|
||||||
]
|
|
||||||
}
|
|
||||||
) {
|
|
||||||
author @ValueList.entity:'Authors';
|
|
||||||
};
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Books Details
|
|
||||||
//
|
|
||||||
annotate my.Books with @(
|
|
||||||
UI: {
|
|
||||||
HeaderInfo: {
|
|
||||||
TypeName: '{i18n>Book}',
|
|
||||||
TypeNamePlural: '{i18n>Books}',
|
|
||||||
Title: {Value: title},
|
|
||||||
Description: {Value: author.name}
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Books Elements
|
|
||||||
//
|
|
||||||
annotate my.Books with {
|
|
||||||
ID @title:'{i18n>ID}' @UI.HiddenFilter;
|
|
||||||
title @title:'{i18n>Title}';
|
|
||||||
genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
|
|
||||||
author @title:'{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly };
|
|
||||||
price @title:'{i18n>Price}';
|
|
||||||
stock @title:'{i18n>Stock}';
|
|
||||||
descr @UI.MultiLineText;
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Genres List
|
|
||||||
//
|
|
||||||
annotate my.Genres with @(
|
|
||||||
Common.SemanticKey: [name],
|
|
||||||
UI: {
|
|
||||||
SelectionFields: [ name ],
|
|
||||||
LineItem:[
|
|
||||||
{Value: name},
|
|
||||||
{Value: parent.name, Label: 'Main Genre'},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Genre Details
|
|
||||||
//
|
|
||||||
annotate my.Genres with @(
|
|
||||||
UI: {
|
|
||||||
Identification: [{Value:name}],
|
|
||||||
HeaderInfo: {
|
|
||||||
TypeName: '{i18n>Genre}',
|
|
||||||
TypeNamePlural: '{i18n>Genres}',
|
|
||||||
Title: {Value: name},
|
|
||||||
Description: {Value: ID}
|
|
||||||
},
|
|
||||||
Facets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>SubGenres}', Target: 'children/@UI.LineItem'},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Genres Elements
|
|
||||||
//
|
|
||||||
annotate my.Genres with {
|
|
||||||
ID @title: '{i18n>ID}';
|
|
||||||
name @title: '{i18n>Genre}';
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Authors List
|
|
||||||
//
|
|
||||||
annotate my.Authors with @(
|
|
||||||
Common.SemanticKey: [name],
|
|
||||||
UI: {
|
|
||||||
Identification: [{Value:name}],
|
|
||||||
SelectionFields: [ name ],
|
|
||||||
LineItem:[
|
|
||||||
{Value: ID},
|
|
||||||
{Value: name},
|
|
||||||
{Value: dateOfBirth},
|
|
||||||
{Value: dateOfDeath},
|
|
||||||
{Value: placeOfBirth},
|
|
||||||
{Value: placeOfDeath},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Author Details
|
|
||||||
//
|
|
||||||
annotate my.Authors with @(
|
|
||||||
UI: {
|
|
||||||
HeaderInfo: {
|
|
||||||
TypeName: '{i18n>Author}',
|
|
||||||
TypeNamePlural: '{i18n>Authors}',
|
|
||||||
Title: {Value: name},
|
|
||||||
Description: {Value: dateOfBirth}
|
|
||||||
},
|
|
||||||
Facets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Target: 'books/@UI.LineItem'},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Authors Elements
|
|
||||||
//
|
|
||||||
annotate my.Authors with {
|
|
||||||
ID @title:'{i18n>ID}' @UI.HiddenFilter;
|
|
||||||
name @title:'{i18n>Name}';
|
|
||||||
dateOfBirth @title:'{i18n>DateOfBirth}';
|
|
||||||
dateOfDeath @title:'{i18n>DateOfDeath}';
|
|
||||||
placeOfBirth @title:'{i18n>PlaceOfBirth}';
|
|
||||||
placeOfDeath @title:'{i18n>PlaceOfDeath}';
|
|
||||||
}
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Languages List
|
|
||||||
//
|
|
||||||
annotate common.Languages with @(
|
|
||||||
Common.SemanticKey: [code],
|
|
||||||
Identification: [{Value:code}],
|
|
||||||
UI: {
|
|
||||||
SelectionFields: [ name, descr ],
|
|
||||||
LineItem:[
|
|
||||||
{Value: code},
|
|
||||||
{Value: name},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Language Details
|
|
||||||
//
|
|
||||||
annotate common.Languages with @(
|
|
||||||
UI: {
|
|
||||||
HeaderInfo: {
|
|
||||||
TypeName: '{i18n>Language}',
|
|
||||||
TypeNamePlural: '{i18n>Languages}',
|
|
||||||
Title: {Value: name},
|
|
||||||
Description: {Value: descr}
|
|
||||||
},
|
|
||||||
Facets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
|
||||||
],
|
|
||||||
FieldGroup#Details: {
|
|
||||||
Data: [
|
|
||||||
{Value: code},
|
|
||||||
{Value: name},
|
|
||||||
{Value: descr}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Currencies List
|
|
||||||
//
|
|
||||||
annotate common.Currencies with @(
|
|
||||||
Common.SemanticKey: [code],
|
|
||||||
Identification: [{Value:code}],
|
|
||||||
UI: {
|
|
||||||
SelectionFields: [ name, descr ],
|
|
||||||
LineItem:[
|
|
||||||
{Value: descr},
|
|
||||||
{Value: symbol},
|
|
||||||
{Value: code},
|
|
||||||
],
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Currency Details
|
|
||||||
//
|
|
||||||
annotate common.Currencies with @(
|
|
||||||
UI: {
|
|
||||||
HeaderInfo: {
|
|
||||||
TypeName: '{i18n>Currency}',
|
|
||||||
TypeNamePlural: '{i18n>Currencies}',
|
|
||||||
Title: {Value: descr},
|
|
||||||
Description: {Value: code}
|
|
||||||
},
|
|
||||||
Facets: [
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Extended}', Target: '@UI.FieldGroup#Extended'},
|
|
||||||
],
|
|
||||||
FieldGroup#Details: {
|
|
||||||
Data: [
|
|
||||||
{Value: name},
|
|
||||||
{Value: symbol},
|
|
||||||
{Value: code},
|
|
||||||
{Value: descr}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
FieldGroup#Extended: {
|
|
||||||
Data: [
|
|
||||||
{Value: numcode},
|
|
||||||
{Value: minor},
|
|
||||||
{Value: exponent}
|
|
||||||
]
|
|
||||||
},
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Currencies Elements
|
|
||||||
//
|
|
||||||
annotate common.Currencies with {
|
|
||||||
numcode @title:'{i18n>NumCode}';
|
|
||||||
minor @title:'{i18n>MinorUnit}';
|
|
||||||
exponent @title:'{i18n>Exponent}';
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
<head>
|
|
||||||
<meta http-equiv="refresh" content="0;url=reviews/index.html">
|
|
||||||
</head>
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
//
|
|
||||||
// Add Author.age and .lifetime with a DB-specific function
|
|
||||||
//
|
|
||||||
|
|
||||||
using { AdminService } from '..';
|
|
||||||
|
|
||||||
extend projection AdminService.Authors with {
|
|
||||||
YEARS_BETWEEN(dateOfBirth, dateOfDeath) as age: Integer,
|
|
||||||
YEAR(dateOfBirth) || ' – ' || YEAR(dateOfDeath) as lifetime : String
|
|
||||||
}
|
|
||||||
@@ -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;
|
|
||||||
}
|
|
||||||
@@ -1,10 +0,0 @@
|
|||||||
//
|
|
||||||
// Add Author.age and .lifetime with a DB-specific function
|
|
||||||
//
|
|
||||||
|
|
||||||
using { AdminService } from '..';
|
|
||||||
|
|
||||||
extend projection AdminService.Authors with {
|
|
||||||
strftime('%Y',dateOfDeath)-strftime('%Y',dateOfBirth) as age: Integer,
|
|
||||||
strftime('%Y',dateOfBirth) || ' – ' || strftime('%Y',dateOfDeath) as lifetime : String
|
|
||||||
}
|
|
||||||
@@ -1,49 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@capire/fiori",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@capire/bookshop": "*",
|
|
||||||
"@capire/common": "*",
|
|
||||||
"@capire/orders": "*",
|
|
||||||
"@capire/reviews": "*",
|
|
||||||
"@capire/suppliers": "*",
|
|
||||||
"@sap/cds": "^4",
|
|
||||||
"express": "^4.17.1",
|
|
||||||
"passport": "^0.4.1"
|
|
||||||
},
|
|
||||||
"scripts": {
|
|
||||||
"start": "cds run --in-memory?",
|
|
||||||
"watch": "cds watch"
|
|
||||||
},
|
|
||||||
"cds": {
|
|
||||||
"hana": {
|
|
||||||
"deploy-format": "hdbtable"
|
|
||||||
},
|
|
||||||
"requires": {
|
|
||||||
"API_BUSINESS_PARTNER": {
|
|
||||||
"kind": "odata",
|
|
||||||
"model": "@capire/suppliers"
|
|
||||||
},
|
|
||||||
"auth": {
|
|
||||||
"strategy": "dummy"
|
|
||||||
},
|
|
||||||
"ReviewsService": {
|
|
||||||
"kind": "odata",
|
|
||||||
"model": "@capire/reviews"
|
|
||||||
},
|
|
||||||
"OrdersService": {
|
|
||||||
"kind": "odata",
|
|
||||||
"model": "@capire/orders"
|
|
||||||
},
|
|
||||||
"db": {
|
|
||||||
"kind": "sql",
|
|
||||||
"[development]": {
|
|
||||||
"model": "db/sqlite"
|
|
||||||
},
|
|
||||||
"[production]": {
|
|
||||||
"model": "db/hana"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
const cds = require ('@sap/cds')
|
|
||||||
|
|
||||||
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'))
|
|
||||||
cds.once('served', require('@capire/suppliers/srv/mashup'))
|
|
||||||
|
|
||||||
module.exports = cds.server
|
|
||||||
|
|
||||||
|
|
||||||
// -----------------------------------------------------------------------
|
|
||||||
// Helper for serving static content from npm-installed packages
|
|
||||||
const {static} = require('express')
|
|
||||||
const {dirname} = require('path')
|
|
||||||
const _from = target => static (dirname (require.resolve(target)))
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Mashing up imported models...
|
|
||||||
//
|
|
||||||
|
|
||||||
using { sap.capire.bookshop.Books } from '@capire/bookshop';
|
|
||||||
|
|
||||||
//
|
|
||||||
// Extend Books with access to Reviews and average ratings
|
|
||||||
//
|
|
||||||
|
|
||||||
using { ReviewsService.Reviews } from '@capire/reviews';
|
|
||||||
extend Books with {
|
|
||||||
reviews : Composition of many Reviews on reviews.subject = $self.ID;
|
|
||||||
rating : Decimal;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
// Extend Orders with Books as Products
|
|
||||||
//
|
|
||||||
|
|
||||||
using { sap.capire.orders.Orders_Items } from '@capire/orders';
|
|
||||||
extend Orders_Items with {
|
|
||||||
book : Association to Books on product.ID = book.ID
|
|
||||||
}
|
|
||||||
@@ -1,59 +0,0 @@
|
|||||||
////////////////////////////////////////////////////////////////////////////
|
|
||||||
//
|
|
||||||
// Mashing up provided and required services...
|
|
||||||
//
|
|
||||||
module.exports = async()=>{ // called by server.js
|
|
||||||
|
|
||||||
const cds = require('@sap/cds')
|
|
||||||
const CatalogService = await cds.connect.to ('CatalogService')
|
|
||||||
const ReviewsService = await cds.connect.to ('ReviewsService')
|
|
||||||
const OrdersService = await cds.connect.to ('OrdersService')
|
|
||||||
const db = await cds.connect.to ('db')
|
|
||||||
|
|
||||||
// reflect entity definitions used below...
|
|
||||||
const { Books } = db.entities ('sap.capire.bookshop')
|
|
||||||
|
|
||||||
//
|
|
||||||
// Delegate requests to read reviews to the ReviewsService
|
|
||||||
// Note: prepend is neccessary to intercept generic default handler
|
|
||||||
//
|
|
||||||
CatalogService.prepend (srv => srv.on ('READ', 'Books/reviews', (req) => {
|
|
||||||
console.debug ('> delegating request to ReviewsService')
|
|
||||||
const [id] = req.params, { columns, limit } = req.query.SELECT
|
|
||||||
return ReviewsService.tx(req).read ('Reviews',columns).limit(limit).where({subject:String(id)})
|
|
||||||
}))
|
|
||||||
|
|
||||||
//
|
|
||||||
// Create an order with the OrdersService when CatalogService signals a new order
|
|
||||||
//
|
|
||||||
CatalogService.on ('OrderedBook', async (msg) => {
|
|
||||||
const { book, amount, buyer } = msg.data
|
|
||||||
const { title, price } = await db.tx(msg).read (Books, book, b => { b.title, b.price })
|
|
||||||
return OrdersService.tx(msg).create ('Orders').entries({
|
|
||||||
OrderNo: 'Order at '+ (new Date).toLocaleString(),
|
|
||||||
Items: [{ product:{ID:`${book}`}, title, price, amount }],
|
|
||||||
buyer, createdBy: buyer
|
|
||||||
})
|
|
||||||
})
|
|
||||||
|
|
||||||
//
|
|
||||||
// Update Books' average ratings when ReviewsService signals updatd reviews
|
|
||||||
//
|
|
||||||
ReviewsService.on ('reviewed', (msg) => {
|
|
||||||
console.debug ('> received:', msg.event, msg.data)
|
|
||||||
const { subject, rating } = msg.data
|
|
||||||
return UPDATE(Books,subject).with({rating})
|
|
||||||
// ^ Note: the framework will execute this and take care for db.tx
|
|
||||||
})
|
|
||||||
|
|
||||||
//
|
|
||||||
// Reduce stock of ordered books for orders are created from Orders admin UI
|
|
||||||
//
|
|
||||||
OrdersService.on ('OrderChanged', (msg) => {
|
|
||||||
console.debug ('> received:', msg.event, msg.data)
|
|
||||||
const { product, deltaAmount } = msg.data
|
|
||||||
return UPDATE (Books) .where ('ID =', product)
|
|
||||||
.and ('stock >=', deltaAmount)
|
|
||||||
.set ('stock -=', deltaAmount)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
@@ -1,77 +0,0 @@
|
|||||||
|
|
||||||
@bookshop = http://localhost:4004
|
|
||||||
@reviews-service = {{bookshop}}/reviews
|
|
||||||
# Uncomment this when running a separate reviews service
|
|
||||||
@reviews-service = http://localhost:4005/reviews
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#################################################
|
|
||||||
#
|
|
||||||
# Reviews Service
|
|
||||||
#
|
|
||||||
|
|
||||||
GET {{reviews-service}}/Reviews
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
POST {{reviews-service}}/Reviews
|
|
||||||
Authorization: Basic {{$processEnv USER}}:
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"subject":"201", "title":"boo", "rating":3 }
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#################################################
|
|
||||||
#
|
|
||||||
# Bookshop Services
|
|
||||||
#
|
|
||||||
|
|
||||||
GET {{bookshop}}/browse/Books/201/reviews?
|
|
||||||
&$select=rating,date,title
|
|
||||||
&$top=3
|
|
||||||
|
|
||||||
###
|
|
||||||
|
|
||||||
GET {{bookshop}}/browse/Books(201)?
|
|
||||||
&$select=ID,title,rating
|
|
||||||
&$expand=reviews
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
#################################################
|
|
||||||
#
|
|
||||||
# Orders Service, incl. draft choreography
|
|
||||||
#
|
|
||||||
@newOrderID = e939604c-ab83-4d4f-bdb6-95fe30b3773e
|
|
||||||
|
|
||||||
GET {{bookshop}}/orders/Orders
|
|
||||||
|
|
||||||
### Create order, still inactive
|
|
||||||
POST {{bookshop}}/orders/Orders
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{"ID": "{{newOrderID}}"}
|
|
||||||
|
|
||||||
### Get inactive order. We have to specify `IsActiveEntity`.
|
|
||||||
GET {{bookshop}}/orders/Orders(ID={{newOrderID}},IsActiveEntity=false)
|
|
||||||
|
|
||||||
### Activate order using `.../<servicename>.draftActivate`
|
|
||||||
POST {{bookshop}}/orders/Orders(ID={{newOrderID}},IsActiveEntity=false)/OrdersService.draftActivate
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
### Get active order
|
|
||||||
GET {{bookshop}}/orders/Orders(ID={{newOrderID}},IsActiveEntity=true)
|
|
||||||
|
|
||||||
### Create author
|
|
||||||
POST {{bookshop}}/admin/Authors
|
|
||||||
Content-Type: application/json
|
|
||||||
Authorization: Basic alice:
|
|
||||||
|
|
||||||
{
|
|
||||||
"ID": 200,
|
|
||||||
"name": "William Shakespeare",
|
|
||||||
"dateOfBirth": "1564-04-26",
|
|
||||||
"dateOfDeath": "1616-04-23"
|
|
||||||
}
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@capire/hello-world",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"scripts": {
|
|
||||||
"watch": "cds serve world.cds"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
GET http://localhost:4004/say/hello(to='world')
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
service say {
|
|
||||||
function hello (to:String) returns String;
|
|
||||||
}
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
module.exports = class say {
|
|
||||||
hello(req) { return `Hello ${req.data.to}!` }
|
|
||||||
}
|
|
||||||
1
lerna.json
Normal file
1
lerna.json
Normal file
@@ -0,0 +1 @@
|
|||||||
|
{"packages":["packages/*"],"version":"1.0.0"}
|
||||||
@@ -1,13 +0,0 @@
|
|||||||
namespace sap.capire.media;
|
|
||||||
|
|
||||||
entity Media {
|
|
||||||
|
|
||||||
key id:Integer;
|
|
||||||
@Core.MediaType: mediaType
|
|
||||||
content : LargeBinary ;
|
|
||||||
|
|
||||||
@Core.IsMediaType: true
|
|
||||||
mediaType : String;
|
|
||||||
fileName : String;
|
|
||||||
applicationName:String;
|
|
||||||
}
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
using from './db/data-model';
|
|
||||||
using from './srv/media-service';
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@capire/media",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"lokijs": "^1.5.6"
|
|
||||||
},
|
|
||||||
"files": [
|
|
||||||
"db",
|
|
||||||
"srv",
|
|
||||||
"index.cds"
|
|
||||||
],
|
|
||||||
"cds": {
|
|
||||||
"requires": {
|
|
||||||
"db": {
|
|
||||||
"kind": "sql"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
using { sap.capire.media as db } from '../db/data-model';
|
|
||||||
namespace sap.capire.media;
|
|
||||||
|
|
||||||
service MediaServer {
|
|
||||||
entity Media as projection on db.Media ;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@@ -1,68 +0,0 @@
|
|||||||
const loki = require('lokijs')
|
|
||||||
const db = new loki('DB')
|
|
||||||
const mediaDB = db.addCollection('Media')
|
|
||||||
const { Readable, PassThrough } = require('stream')
|
|
||||||
|
|
||||||
module.exports = srv => {
|
|
||||||
srv.before('CREATE', 'Media', req => {
|
|
||||||
const obj = mediaDB.insert({ media: '' })
|
|
||||||
req.data.id = obj.$loki
|
|
||||||
})
|
|
||||||
|
|
||||||
srv.on('UPDATE', 'Media', (req, next) => {
|
|
||||||
const url = req._.req.path
|
|
||||||
if (url.includes('content')) {
|
|
||||||
const id = req.data.id
|
|
||||||
const obj = mediaDB.get(id)
|
|
||||||
if (!obj) {
|
|
||||||
req.reject(404, 'No record found for the ID')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const stream = new PassThrough()
|
|
||||||
const chunks = []
|
|
||||||
stream.on('data', chunk => {
|
|
||||||
chunks.push(chunk)
|
|
||||||
})
|
|
||||||
stream.on('end', () => {
|
|
||||||
obj.media = Buffer.concat(chunks).toString('base64')
|
|
||||||
mediaDB.update(obj)
|
|
||||||
})
|
|
||||||
req.data.content.pipe(stream)
|
|
||||||
} else return next()
|
|
||||||
})
|
|
||||||
|
|
||||||
srv.on('READ', 'Media', (req, next) => {
|
|
||||||
const url = req._.req.path
|
|
||||||
if (url.includes('content')) {
|
|
||||||
const id = req.data.id
|
|
||||||
const mediaObj = mediaDB.get(id)
|
|
||||||
if (!mediaObj) {
|
|
||||||
req.reject(404, 'Media not found for the ID')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
const decodedMedia = new Buffer(
|
|
||||||
mediaObj.media.split(';base64,').pop(),
|
|
||||||
'base64'
|
|
||||||
)
|
|
||||||
return _formatResult(decodedMedia)
|
|
||||||
} else return next() //> delegate to next/default handlers
|
|
||||||
})
|
|
||||||
|
|
||||||
srv.on('DELETE', 'Media', (req, next) => {
|
|
||||||
const id = req.data.id
|
|
||||||
mediaDB
|
|
||||||
.chain()
|
|
||||||
.find({ $loki: id })
|
|
||||||
.remove()
|
|
||||||
return next() //> delegate to next/default handlers
|
|
||||||
})
|
|
||||||
|
|
||||||
function _formatResult (decodedMedia) {
|
|
||||||
const readable = new Readable()
|
|
||||||
const result = new Array()
|
|
||||||
readable.push(decodedMedia)
|
|
||||||
readable.push(null)
|
|
||||||
result.push({ value: readable })
|
|
||||||
return result
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Binary file not shown.
|
Before Width: | Height: | Size: 3.0 KiB |
@@ -1,35 +0,0 @@
|
|||||||
### Requires REST Client for VS Code
|
|
||||||
### https://marketplace.visualstudio.com/items?itemName=humao.rest-client
|
|
||||||
###
|
|
||||||
@protocol = http
|
|
||||||
@host = localhost
|
|
||||||
@port = 4004
|
|
||||||
### Read Pictures
|
|
||||||
GET {{protocol}}://{{host}}:{{port}}/media-server/Media
|
|
||||||
Authorization: Basic admin:
|
|
||||||
|
|
||||||
### Create Picture with mediatype
|
|
||||||
POST {{protocol}}://{{host}}:{{port}}/media-server/Media
|
|
||||||
Authorization: Basic admin:
|
|
||||||
Accept: application/json
|
|
||||||
Content-Type: application/json
|
|
||||||
|
|
||||||
{
|
|
||||||
"id": 1,
|
|
||||||
"mediaType": "image/png"
|
|
||||||
}
|
|
||||||
|
|
||||||
### Upload Binary PNG
|
|
||||||
PUT {{protocol}}://{{host}}:{{port}}/media-server/Media(1)/content
|
|
||||||
Authorization: Basic admin:
|
|
||||||
Content-Type: image/png
|
|
||||||
|
|
||||||
< ./Test.png
|
|
||||||
|
|
||||||
### Read Binary
|
|
||||||
GET {{protocol}}://{{host}}:{{port}}/media-server/Media(1)/content
|
|
||||||
Authorization: Basic admin:
|
|
||||||
|
|
||||||
### Delete Image
|
|
||||||
DELETE {{protocol}}://{{host}}:{{port}}/media-server/Media(1)
|
|
||||||
Authorization: Basic admin:
|
|
||||||
@@ -1,2 +0,0 @@
|
|||||||
cds.requires.messaging.kind = file-based-messaging
|
|
||||||
PORT = 4006
|
|
||||||
@@ -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>Bookshop</title>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
window["sap-ushell-config"] = {
|
|
||||||
defaultRenderer: "fiori2",
|
|
||||||
applications: {
|
|
||||||
"manage-orders": {
|
|
||||||
title: "Manage Orders",
|
|
||||||
description: "... testing FE v42",
|
|
||||||
additionalInformation: "SAPUI5.Component=orders",
|
|
||||||
applicationType : "URL",
|
|
||||||
url: "/orders/webapp",
|
|
||||||
navigationMode: "embedded"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
|
||||||
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
|
|
||||||
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
|
|
||||||
data-sap-ui-compatVersion="edge"
|
|
||||||
data-sap-ui-theme="sap_fiori_3"
|
|
||||||
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,5 +0,0 @@
|
|||||||
/*
|
|
||||||
This model controls what gets served to Fiori frontends...
|
|
||||||
*/
|
|
||||||
|
|
||||||
using from './orders/fiori-service';
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
|
|
||||||
"use strict";
|
|
||||||
return AppComponent.extend("orders.Component", {
|
|
||||||
metadata: { manifest: "json" }
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
/* eslint no-undef:0 */
|
|
||||||
@@ -1,3 +0,0 @@
|
|||||||
ID;createdAt;createdBy;buyer;OrderNo;currency_code
|
|
||||||
7e2f2640-6866-4dcf-8f4d-3027aa831cad;2019-01-31;john.doe@test.com;john.doe@test.com;1;EUR
|
|
||||||
64e718c9-ff99-47f1-8ca3-950c850777d4;2019-01-30;jane.doe@test.com;jane.doe@test.com;2;EUR
|
|
||||||
|
@@ -1,4 +0,0 @@
|
|||||||
ID;up__ID;amount;product_ID;title;price
|
|
||||||
58040e66-1dcd-4ffb-ab10-fdce32028b79;7e2f2640-6866-4dcf-8f4d-3027aa831cad;1;201;Wuthering Heights;11.11
|
|
||||||
64e718c9-ff99-47f1-8ca3-950c850777d4;7e2f2640-6866-4dcf-8f4d-3027aa831cad;1;271;Catweazle;15
|
|
||||||
e9641166-e050-4261-bfee-d1e797e6cb7f;64e718c9-ff99-47f1-8ca3-950c850777d4;2;252;Eleonora;28
|
|
||||||
|
@@ -1,23 +0,0 @@
|
|||||||
using { Currency, User, managed, cuid } from '@sap/cds/common';
|
|
||||||
namespace sap.capire.orders;
|
|
||||||
|
|
||||||
entity Orders : cuid, managed {
|
|
||||||
OrderNo : String @title:'Order Number'; //> readable key
|
|
||||||
Items : Composition of many Orders_Items on Items.up_ = $self;
|
|
||||||
buyer : User;
|
|
||||||
currency : Currency;
|
|
||||||
}
|
|
||||||
|
|
||||||
entity Orders_Items {
|
|
||||||
key ID : UUID;
|
|
||||||
up_ : Association to Orders;
|
|
||||||
product : Association to Products @assert.integrity:false; // REVISIT: this is a temporary workaround for a glitch in cds-runtime
|
|
||||||
amount : Integer;
|
|
||||||
title : String;
|
|
||||||
price : Double;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** This is a stand-in for arbitrary ordered Products */
|
|
||||||
entity Products @(cds.persistence.skip:'always') {
|
|
||||||
key ID : String;
|
|
||||||
}
|
|
||||||
@@ -1,6 +0,0 @@
|
|||||||
/*
|
|
||||||
This model controls what gets exposed
|
|
||||||
*/
|
|
||||||
namespace sap.capire.orders;
|
|
||||||
using from './srv/orders-service';
|
|
||||||
using from './db/schema';
|
|
||||||
@@ -1,7 +0,0 @@
|
|||||||
{
|
|
||||||
"name": "@capire/orders",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"dependencies": {
|
|
||||||
"@sap/cds": "^4.3.0"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1,5 +0,0 @@
|
|||||||
using { sap.capire.orders as my } from '../db/schema';
|
|
||||||
|
|
||||||
service OrdersService {
|
|
||||||
entity Orders as projection on my.Orders;
|
|
||||||
}
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
const cds = require ('@sap/cds')
|
|
||||||
class OrdersService extends cds.ApplicationService {
|
|
||||||
|
|
||||||
/** register custom handlers */
|
|
||||||
init(){
|
|
||||||
const { Orders_Items:OrderItems } = this.entities
|
|
||||||
|
|
||||||
this.before ('UPDATE', 'Orders', async function(req) {
|
|
||||||
const { ID, Items } = req.data
|
|
||||||
if (Items) for (let { product_ID, amount } of Items) {
|
|
||||||
const { amount:before } = await cds.tx(req).run (
|
|
||||||
SELECT.one.from (OrderItems, oi => oi.amount) .where ({up__ID:ID, product_ID})
|
|
||||||
)
|
|
||||||
if (amount != before) await this.orderChanged (product_ID, amount-before)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
this.before ('DELETE', 'Orders', async function(req) {
|
|
||||||
const { ID } = req.data
|
|
||||||
const Items = await cds.tx(req).run (
|
|
||||||
SELECT.from (OrderItems, oi => { oi.product_ID, oi.amount }) .where ({up__ID:ID})
|
|
||||||
)
|
|
||||||
if (Items) await Promise.all (Items.map(it => this.orderChanged (it.product_ID, -it.amount)))
|
|
||||||
})
|
|
||||||
|
|
||||||
return super.init()
|
|
||||||
}
|
|
||||||
|
|
||||||
/** order changed -> broadcast event */
|
|
||||||
orderChanged (product, deltaAmount) {
|
|
||||||
// Emit events to inform subscribers about changes in orders
|
|
||||||
console.log ('> emitting:', 'OrderChanged', { product, deltaAmount })
|
|
||||||
return this.emit ('OrderChanged', { product, deltaAmount })
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
module.exports = OrdersService
|
|
||||||
1228
package-lock.json
generated
Normal file
1228
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
55
package.json
55
package.json
@@ -1,41 +1,28 @@
|
|||||||
{
|
{
|
||||||
"name": "@capire/samples",
|
"name": "@sap/capire-samples",
|
||||||
"version": "2.0.0",
|
"description": "The umbrella project for all samples to easily setup for local development and tests.",
|
||||||
"description": "A monorepo with several samples for CAP.",
|
"repository": "https://github.com/SAP-samples/cloud-cap-samples.git",
|
||||||
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
|
|
||||||
"author": "daniel.hutzel@sap.com",
|
"author": "daniel.hutzel@sap.com",
|
||||||
"dependencies": {
|
"private": true,
|
||||||
"@capire/bookshop": "./bookshop",
|
|
||||||
"@capire/common": "./common",
|
|
||||||
"@capire/fiori": "./fiori",
|
|
||||||
"@capire/hello": "./hello",
|
|
||||||
"@capire/media": "./media",
|
|
||||||
"@capire/orders": "./orders",
|
|
||||||
"@capire/reviews": "./reviews",
|
|
||||||
"@capire/suppliers": "./suppliers"
|
|
||||||
},
|
|
||||||
"devDependencies": {
|
|
||||||
"chai": "^4.2.0",
|
|
||||||
"chai-as-promised": "^7.1.1",
|
|
||||||
"chai-subset": "^1.6.0",
|
|
||||||
"sqlite3": "5.0.0"
|
|
||||||
},
|
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"fix-antlr": "sed -i -e 's/INVALID_ALT_NUMBER = require.*/INVALID_ALT_NUMBER = 0/' node_modules/antlr4/tree/Trees.js node_modules/antlr4/RuleContext.js",
|
"cleanup": "lerna clean -y && rm -fr node_modules",
|
||||||
"registry": "node .registry/server.js",
|
"install": "(npm -s run lerna) && lerna bootstrap --hoist",
|
||||||
"bookshop": "cds watch bookshop",
|
"lerna": "npx --no-install lerna -v > /dev/null || npm i lerna --no-save",
|
||||||
"fiori": "cds watch fiori",
|
"test": "jest",
|
||||||
"media": "cds watch media",
|
"bookshop-enhanced": "cds watch packages/bookshop-enhanced",
|
||||||
"mocha": "npx mocha || echo",
|
"bookshop": "cds watch packages/bookshop",
|
||||||
"jest": "npx jest",
|
"bookstore": "cds watch packages/bookstore",
|
||||||
"test": "npm run jest --silent"
|
"products-service": "cds watch packages/products-service",
|
||||||
|
"reviews-service": "cds watch packages/reviews-service"
|
||||||
},
|
},
|
||||||
"mocha": {
|
"dependencies": {
|
||||||
"parallel": true
|
"@sap/cds": "^3",
|
||||||
|
"express": "^4"
|
||||||
},
|
},
|
||||||
"jest": {
|
"--add-these-to-devDependencies-for-tests": {
|
||||||
"testEnvironment": "node"
|
"@types/jest": "*",
|
||||||
|
"sqlite3": "*",
|
||||||
|
"jest": "*"
|
||||||
},
|
},
|
||||||
"license": "SAP SAMPLE CODE LICENSE",
|
"license": "SAP SAMPLE CODE LICENSE"
|
||||||
"private": true
|
|
||||||
}
|
}
|
||||||
|
|||||||
13
packages/bookshop/app/_i18n/i18n.properties
Normal file
13
packages/bookshop/app/_i18n/i18n.properties
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
Books = Books
|
||||||
|
Book = Book
|
||||||
|
ID = ID
|
||||||
|
Title = Title
|
||||||
|
Author = Author
|
||||||
|
AuthorID = Author ID
|
||||||
|
Stock = Stock
|
||||||
|
Name = Name
|
||||||
|
AuthorName = Author's Name
|
||||||
|
Authors = Authors
|
||||||
|
Order = Order
|
||||||
|
Orders = Orders
|
||||||
|
Price = Price
|
||||||
@@ -6,7 +6,6 @@ Authors = Autoren
|
|||||||
Author = Autor
|
Author = Autor
|
||||||
AuthorID = ID des Autors
|
AuthorID = ID des Autors
|
||||||
AuthorName = Name des Autors
|
AuthorName = Name des Autors
|
||||||
Age = Alter
|
|
||||||
Name = Name
|
Name = Name
|
||||||
Stock = Bestand
|
Stock = Bestand
|
||||||
Order = Bestellung
|
Order = Bestellung
|
||||||
37
packages/bookshop/app/admin/fiori-service.cds
Normal file
37
packages/bookshop/app/admin/fiori-service.cds
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
using AdminService from '../../srv/admin-service';
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Books Object Page
|
||||||
|
//
|
||||||
|
annotate AdminService.Books with @(
|
||||||
|
UI: {
|
||||||
|
Facets: [
|
||||||
|
{$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'},
|
||||||
|
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
||||||
|
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'},
|
||||||
|
],
|
||||||
|
FieldGroup#General: {
|
||||||
|
Data: [
|
||||||
|
{Value: title},
|
||||||
|
{Value: author_ID},
|
||||||
|
{Value: descr},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
FieldGroup#Details: {
|
||||||
|
Data: [
|
||||||
|
{Value: stock},
|
||||||
|
{Value: price},
|
||||||
|
{Value: currency_code, Label: '{i18n>Currency}'},
|
||||||
|
]
|
||||||
|
},
|
||||||
|
FieldGroup#Admin: {
|
||||||
|
Data: [
|
||||||
|
{Value: createdBy},
|
||||||
|
{Value: createdAt},
|
||||||
|
{Value: modifiedBy},
|
||||||
|
{Value: modifiedAt}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
22
packages/bookshop/app/admin/webapp/Component.js
Normal file
22
packages/bookshop/app/admin/webapp/Component.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
sap.ui.define(["sap/fe/AppComponent"], ac => ac.extend("admin.Component", {
|
||||||
|
metadata:{ manifest:'json' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
// sap.ui.define (["sap/ui/core/UIComponent"], ui5 => ui5.extend("bookshop.Component", {
|
||||||
|
// metadata: { manifest: "json" }
|
||||||
|
// }))
|
||||||
|
// sap.ui.define (["sap/ui/generic/app/AppComponent"], ui5 => ui5.extend("bookshop.Component", {
|
||||||
|
// metadata: { manifest: "json" }
|
||||||
|
// }))
|
||||||
|
|
||||||
|
// jQuery.sap.declare("bookshop.Component");
|
||||||
|
// sap.ui.getCore().loadLibrary("sap.ui.generic.app");
|
||||||
|
// jQuery.sap.require("sap.ui.generic.app.AppComponent");
|
||||||
|
|
||||||
|
// sap.ui.generic.app.AppComponent.extend("bookshop.Component", {
|
||||||
|
// metadata: {
|
||||||
|
// manifest: "json"
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
/* eslint no-undef:0 */
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"sap.ui5": {
|
"sap.ui5": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"libs": {
|
"libs": {
|
||||||
"sap.fe.templates": {}
|
"sap.fe": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"models": {
|
"models": {
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using CatalogService from '@capire/bookshop';
|
using CatalogService from '../../srv/cat-service';
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
@@ -7,8 +7,6 @@ using CatalogService from '@capire/bookshop';
|
|||||||
annotate CatalogService.Books with @(
|
annotate CatalogService.Books with @(
|
||||||
UI: {
|
UI: {
|
||||||
HeaderInfo: {
|
HeaderInfo: {
|
||||||
TypeName: 'Book',
|
|
||||||
TypeNamePlural: 'Books',
|
|
||||||
Description: {Value: author}
|
Description: {Value: author}
|
||||||
},
|
},
|
||||||
HeaderFacets: [
|
HeaderFacets: [
|
||||||
@@ -42,7 +40,6 @@ annotate CatalogService.Books with @(
|
|||||||
LineItem: [
|
LineItem: [
|
||||||
{Value: title},
|
{Value: title},
|
||||||
{Value: author, Label:'{i18n>Author}'},
|
{Value: author, Label:'{i18n>Author}'},
|
||||||
{Value: genre.name},
|
|
||||||
{Value: price},
|
{Value: price},
|
||||||
{Value: currency.symbol, Label:' '},
|
{Value: currency.symbol, Label:' '},
|
||||||
]
|
]
|
||||||
22
packages/bookshop/app/browse/webapp/Component.js
Normal file
22
packages/bookshop/app/browse/webapp/Component.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
sap.ui.define(["sap/fe/AppComponent"], ac => ac.extend("bookshop.Component", {
|
||||||
|
metadata:{ manifest:'json' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
// sap.ui.define (["sap/ui/core/UIComponent"], ui5 => ui5.extend("bookshop.Component", {
|
||||||
|
// metadata: { manifest: "json" }
|
||||||
|
// }))
|
||||||
|
// sap.ui.define (["sap/ui/generic/app/AppComponent"], ui5 => ui5.extend("bookshop.Component", {
|
||||||
|
// metadata: { manifest: "json" }
|
||||||
|
// }))
|
||||||
|
|
||||||
|
// jQuery.sap.declare("bookshop.Component");
|
||||||
|
// sap.ui.getCore().loadLibrary("sap.ui.generic.app");
|
||||||
|
// jQuery.sap.require("sap.ui.generic.app.AppComponent");
|
||||||
|
|
||||||
|
// sap.ui.generic.app.AppComponent.extend("bookshop.Component", {
|
||||||
|
// metadata: {
|
||||||
|
// manifest: "json"
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
/* eslint no-undef:0 */
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"sap.ui5": {
|
"sap.ui5": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"libs": {
|
"libs": {
|
||||||
"sap.fe.templates": {}
|
"sap.fe": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"models": {
|
"models": {
|
||||||
74
packages/bookshop/app/common.cds
Normal file
74
packages/bookshop/app/common.cds
Normal file
@@ -0,0 +1,74 @@
|
|||||||
|
/*
|
||||||
|
Common Annotations shared by all apps
|
||||||
|
*/
|
||||||
|
|
||||||
|
using { sap.capire.bookshop as my } from '../db/schema';
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Books Lists
|
||||||
|
//
|
||||||
|
annotate my.Books with @(
|
||||||
|
UI: {
|
||||||
|
Identification: [{Value:title}],
|
||||||
|
SelectionFields: [ ID, author_ID, price, currency_code ],
|
||||||
|
LineItem: [
|
||||||
|
{Value: ID},
|
||||||
|
{Value: title},
|
||||||
|
{Value: author.name, Label:'{i18n>Author}'},
|
||||||
|
{Value: stock},
|
||||||
|
{Value: price},
|
||||||
|
{Value: currency.symbol, Label:' '},
|
||||||
|
]
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
author @ValueList.entity:'Authors';
|
||||||
|
};
|
||||||
|
|
||||||
|
annotate my.Authors with @(
|
||||||
|
UI: {
|
||||||
|
Identification: [{Value:name}],
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Books Details
|
||||||
|
//
|
||||||
|
annotate my.Books with @(
|
||||||
|
UI: {
|
||||||
|
HeaderInfo: {
|
||||||
|
TypeName: '{i18n>Book}',
|
||||||
|
TypeNamePlural: '{i18n>Books}',
|
||||||
|
Title: {Value: title},
|
||||||
|
Description: {Value: author.name}
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Books Elements
|
||||||
|
//
|
||||||
|
annotate my.Books with {
|
||||||
|
ID @title:'{i18n>ID}' @UI.HiddenFilter;
|
||||||
|
title @title:'{i18n>Title}';
|
||||||
|
author @title:'{i18n>AuthorID}';
|
||||||
|
price @title:'{i18n>Price}';
|
||||||
|
stock @title:'{i18n>Stock}';
|
||||||
|
descr @UI.MultiLineText;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Authors Elements
|
||||||
|
//
|
||||||
|
annotate my.Authors with {
|
||||||
|
ID @title:'{i18n>ID}' @UI.HiddenFilter;
|
||||||
|
name @title:'{i18n>AuthorName}';
|
||||||
|
}
|
||||||
@@ -13,7 +13,7 @@
|
|||||||
applications: {
|
applications: {
|
||||||
"browse-books": {
|
"browse-books": {
|
||||||
title: "Browse Books",
|
title: "Browse Books",
|
||||||
description: "w/ SAP Fiori Elements",
|
description: "... testing FE v42",
|
||||||
additionalInformation: "SAPUI5.Component=bookshop",
|
additionalInformation: "SAPUI5.Component=bookshop",
|
||||||
applicationType : "URL",
|
applicationType : "URL",
|
||||||
url: "/browse/webapp",
|
url: "/browse/webapp",
|
||||||
@@ -21,7 +21,7 @@
|
|||||||
},
|
},
|
||||||
"manage-books": {
|
"manage-books": {
|
||||||
title: "Manage Books",
|
title: "Manage Books",
|
||||||
description: "w/ SAP Fiori Elements",
|
description: "... testing FE v42",
|
||||||
additionalInformation: "SAPUI5.Component=admin",
|
additionalInformation: "SAPUI5.Component=admin",
|
||||||
applicationType : "URL",
|
applicationType : "URL",
|
||||||
url: "/admin/webapp",
|
url: "/admin/webapp",
|
||||||
@@ -29,7 +29,7 @@
|
|||||||
},
|
},
|
||||||
"manage-orders": {
|
"manage-orders": {
|
||||||
title: "Manage Orders",
|
title: "Manage Orders",
|
||||||
description: "w/ SAP Fiori Elements",
|
description: "... testing FE v42",
|
||||||
additionalInformation: "SAPUI5.Component=orders",
|
additionalInformation: "SAPUI5.Component=orders",
|
||||||
applicationType : "URL",
|
applicationType : "URL",
|
||||||
url: "/orders/webapp",
|
url: "/orders/webapp",
|
||||||
@@ -39,11 +39,11 @@
|
|||||||
};
|
};
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
<script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
||||||
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
|
<script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
|
||||||
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
|
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
|
||||||
data-sap-ui-compatVersion="edge"
|
data-sap-ui-compatVersion="edge"
|
||||||
data-sap-ui-theme="sap_fiori_3"
|
data-sap-ui-theme="sap_belize"
|
||||||
data-sap-ui-frameOptions="allow"
|
data-sap-ui-frameOptions="allow"
|
||||||
></script>
|
></script>
|
||||||
<script>
|
<script>
|
||||||
@@ -4,9 +4,5 @@
|
|||||||
|
|
||||||
using from './admin/fiori-service';
|
using from './admin/fiori-service';
|
||||||
using from './browse/fiori-service';
|
using from './browse/fiori-service';
|
||||||
|
using from './orders/fiori-service';
|
||||||
using from './common';
|
using from './common';
|
||||||
|
|
||||||
using from '@capire/common';
|
|
||||||
|
|
||||||
// only works in case of embedded orders service
|
|
||||||
using from '@capire/orders/app/orders/fiori-service';
|
|
||||||
@@ -1,27 +1,41 @@
|
|||||||
|
using AdminService from '../../srv/admin-service';
|
||||||
|
|
||||||
|
annotate AdminService.Books with {
|
||||||
|
price @Common.FieldControl: #ReadOnly;
|
||||||
|
}
|
||||||
////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// Note: this is designed for the OrdersService being co-located with
|
// Common
|
||||||
// bookshop. It does not work if OrdersService is run as a separate
|
|
||||||
// process, and is not intended to do so.
|
|
||||||
//
|
//
|
||||||
////////////////////////////////////////////////////////////////////////////
|
annotate AdminService.OrderItems with {
|
||||||
|
book @(
|
||||||
|
Common: {
|
||||||
|
Text: book.title,
|
||||||
|
FieldControl: #Mandatory
|
||||||
|
},
|
||||||
|
ValueList.entity:'Books',
|
||||||
|
);
|
||||||
|
amount @(
|
||||||
|
Common.FieldControl: #Mandatory
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
annotate AdminService.Orders with @(
|
||||||
using { OrdersService } from '../../srv/orders-service';
|
|
||||||
|
|
||||||
|
|
||||||
@odata.draft.enabled
|
|
||||||
annotate OrdersService.Orders with @(
|
|
||||||
UI: {
|
UI: {
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Lists of Orders
|
||||||
|
//
|
||||||
SelectionFields: [ createdAt, createdBy ],
|
SelectionFields: [ createdAt, createdBy ],
|
||||||
LineItem: [
|
LineItem: [
|
||||||
{Value: OrderNo, Label:'OrderNo'},
|
{Value: createdBy, Label:'Customer'},
|
||||||
{Value: buyer, Label:'Customer'},
|
|
||||||
{Value: createdAt, Label:'Date'}
|
{Value: createdAt, Label:'Date'}
|
||||||
],
|
],
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Order Details
|
||||||
|
//
|
||||||
HeaderInfo: {
|
HeaderInfo: {
|
||||||
TypeName: 'Order', TypeNamePlural: 'Orders',
|
TypeName: 'Order', TypeNamePlural: 'Orders',
|
||||||
Title: {
|
Title: {
|
||||||
@@ -45,7 +59,7 @@ annotate OrdersService.Orders with @(
|
|||||||
],
|
],
|
||||||
FieldGroup#Details: {
|
FieldGroup#Details: {
|
||||||
Data: [
|
Data: [
|
||||||
{Value: currency.code, Label:'Currency'}
|
{Value: currency_code, Label:'Currency'}
|
||||||
]
|
]
|
||||||
},
|
},
|
||||||
FieldGroup#Created: {
|
FieldGroup#Created: {
|
||||||
@@ -68,25 +82,36 @@ annotate OrdersService.Orders with @(
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
annotate OrdersService.Orders_Items with @(
|
//The enity types name is AdminService.my_bookshop_OrderItems
|
||||||
|
//The annotations below are not generated in edmx WHY?
|
||||||
|
annotate AdminService.OrderItems with @(
|
||||||
UI: {
|
UI: {
|
||||||
|
HeaderInfo: {
|
||||||
|
TypeName: 'Order Item', TypeNamePlural: ' ',
|
||||||
|
Title: {
|
||||||
|
Value: book.title
|
||||||
|
},
|
||||||
|
Description: {Value: book.descr}
|
||||||
|
},
|
||||||
|
// There is no filterbar for items so the selctionfileds is not needed
|
||||||
|
SelectionFields: [ book_ID ],
|
||||||
|
////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// Lists of OrderItems
|
||||||
|
//
|
||||||
LineItem: [
|
LineItem: [
|
||||||
{Value: product_ID, Label:'Product ID'},
|
{Value: book_ID, Label:'Book'},
|
||||||
{Value: title, Label:'Product Title'},
|
//The following entry is only used to have the assoication followed in the read event
|
||||||
{Value: price, Label:'Unit Price'},
|
{Value: book.price, Label:'Book Price'},
|
||||||
{Value: amount, Label:'Quantity'},
|
{Value: amount, Label:'Quantity'},
|
||||||
],
|
],
|
||||||
Identification: [ //Is the main field group
|
Identification: [ //Is the main field group
|
||||||
|
//{Value: ID, Label:'ID'}, //A guid shouldn't be on the UI
|
||||||
|
{Value: book_ID, Label:'Book'},
|
||||||
{Value: amount, Label:'Amount'},
|
{Value: amount, Label:'Amount'},
|
||||||
{Value: title, Label:'Product'},
|
|
||||||
{Value: price, Label:'Unit Price'},
|
|
||||||
],
|
],
|
||||||
Facets: [
|
Facets: [
|
||||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: '@UI.Identification'},
|
{$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: '@UI.Identification'},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
) {
|
);
|
||||||
amount @(
|
|
||||||
Common.FieldControl: #Mandatory
|
|
||||||
);
|
|
||||||
};
|
|
||||||
22
packages/bookshop/app/orders/webapp/Component.js
Normal file
22
packages/bookshop/app/orders/webapp/Component.js
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
sap.ui.define(["sap/fe/AppComponent"], ac => ac.extend("orders.Component", {
|
||||||
|
metadata:{ manifest:'json' }
|
||||||
|
}))
|
||||||
|
|
||||||
|
// sap.ui.define (["sap/ui/core/UIComponent"], ui5 => ui5.extend("bookshop.Component", {
|
||||||
|
// metadata: { manifest: "json" }
|
||||||
|
// }))
|
||||||
|
// sap.ui.define (["sap/ui/generic/app/AppComponent"], ui5 => ui5.extend("bookshop.Component", {
|
||||||
|
// metadata: { manifest: "json" }
|
||||||
|
// }))
|
||||||
|
|
||||||
|
// jQuery.sap.declare("bookshop.Component");
|
||||||
|
// sap.ui.getCore().loadLibrary("sap.ui.generic.app");
|
||||||
|
// jQuery.sap.require("sap.ui.generic.app.AppComponent");
|
||||||
|
|
||||||
|
// sap.ui.generic.app.AppComponent.extend("bookshop.Component", {
|
||||||
|
// metadata: {
|
||||||
|
// manifest: "json"
|
||||||
|
// }
|
||||||
|
// });
|
||||||
|
|
||||||
|
/* eslint no-undef:0 */
|
||||||
@@ -3,12 +3,12 @@
|
|||||||
"sap.app": {
|
"sap.app": {
|
||||||
"id": "orders",
|
"id": "orders",
|
||||||
"type": "application",
|
"type": "application",
|
||||||
"title": "Order Books",
|
"title": "Manage Orders",
|
||||||
"description": "Sample Application",
|
"description": "Sample Application",
|
||||||
"i18n": "i18n/i18n.properties",
|
"i18n": "i18n/i18n.properties",
|
||||||
"dataSources": {
|
"dataSources": {
|
||||||
"OrdersService": {
|
"AdminService": {
|
||||||
"uri": "/orders/",
|
"uri": "/admin/",
|
||||||
"type": "OData",
|
"type": "OData",
|
||||||
"settings": {
|
"settings": {
|
||||||
"odataVersion": "4.0"
|
"odataVersion": "4.0"
|
||||||
@@ -24,7 +24,7 @@
|
|||||||
"sap.ui5": {
|
"sap.ui5": {
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"libs": {
|
"libs": {
|
||||||
"sap.fe.templates": {}
|
"sap.fe": {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"models": {
|
"models": {
|
||||||
@@ -33,7 +33,7 @@
|
|||||||
"uri": "i18n/i18n.properties"
|
"uri": "i18n/i18n.properties"
|
||||||
},
|
},
|
||||||
"": {
|
"": {
|
||||||
"dataSource": "OrdersService",
|
"dataSource": "AdminService",
|
||||||
"settings": {
|
"settings": {
|
||||||
"synchronizationMode": "None",
|
"synchronizationMode": "None",
|
||||||
"operationMode": "Server",
|
"operationMode": "Server",
|
||||||
@@ -121,7 +121,7 @@
|
|||||||
"name": "sap.fe.templates.ObjectPage",
|
"name": "sap.fe.templates.ObjectPage",
|
||||||
"options": {
|
"options": {
|
||||||
"settings" : {
|
"settings" : {
|
||||||
"entitySet": "Orders_Items"
|
"entitySet": "OrderItems"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
@@ -140,7 +140,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"AuthorsDetails": {
|
"AuthorsDetails": {
|
||||||
"type": "Component",
|
"type": "Component",
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user