Compare commits

..

6 Commits

Author SHA1 Message Date
Daniel
f425a91c2d Adjusted usage of Orders_Items 2020-11-21 00:40:24 +01:00
Daniel
108e886b52 ... 2020-11-20 19:56:04 +01:00
Daniel
d9fb33a523 Investigated issues 2020-11-20 18:53:04 +01:00
Daniel
8429d14bfe Merge branch 'master' of https://github.com/sap-samples/cloud-cap-samples into managed-compositions 2020-11-20 17:26:24 +01:00
Daniel
67c95d9d48 . 2020-11-20 17:25:23 +01:00
Daniel
7a776c53d2 . 2020-11-20 17:02:57 +01:00
66 changed files with 1671 additions and 6346 deletions

View File

@@ -21,7 +21,6 @@
}, },
"rules": { "rules": {
"no-console": "off", "no-console": "off",
"require-atomic-updates": "off", "require-atomic-updates": "off"
"require-await":"warn"
} }
} }

View File

@@ -16,7 +16,7 @@ jobs:
strategy: strategy:
matrix: matrix:
node-version: [12.x, 14.x] node-version: [10.x, 12.x, 14.x]
steps: steps:
- uses: actions/checkout@v2 - uses: actions/checkout@v2

View File

@@ -1 +0,0 @@
*.tgz

View File

@@ -5,9 +5,9 @@ const app = express()
const { PORT=4444 } = process.env const { PORT=4444 } = process.env
const [,,port=PORT] = process.argv const [,,port=PORT] = process.argv
process.chdir(__dirname)
app.use('/-/:tarball', (req,res,next) => { app.use('/-/:tarball', (req,res,next) => {
const url = decodeURIComponent(req.url)
console.debug ('GET', req.params) console.debug ('GET', req.params)
try { try {
const { tarball } = req.params const { tarball } = req.params

View File

@@ -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"
}

View File

@@ -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"
}

View File

@@ -4,15 +4,14 @@
// List of extensions which should be recommended for users of this workspace. // List of extensions which should be recommended for users of this workspace.
"recommendations": [ "recommendations": [
"sapse.vscode-cds", "SAPSE.vscode-cds",
"dbaeumer.vscode-eslint", "dbaeumer.vscode-eslint",
"esbenp.prettier-vscode", "esbenp.prettier-vscode",
"mechatroner.rainbow-csv", "mechatroner.rainbow-csv",
"humao.rest-client", "humao.rest-client",
"alexcvzz.vscode-sqlite", "alexcvzz.vscode-sqlite",
"hbenl.vscode-mocha-test-adapter", "hbenl.vscode-mocha-test-adapter",
"sdras.night-owl", "sdras.night-owl"
"vsls-contrib.codetour"
], ],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace. // List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [ "unwantedRecommendations": [

View File

@@ -3,22 +3,18 @@
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 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)
![](https://github.com/SAP-samples/cloud-cap-samples/workflows/CI/badge.svg) ![](https://github.com/SAP-samples/cloud-cap-samples/workflows/CI/badge.svg)
<!--[![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-cap-samples)](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples)--> [![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-cap-samples)](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples)
### Preliminaries ### Preliminaries
1. Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/) globally: 1. [Install @sap/cds-dk](https://cap.cloud.sap/docs/get-started/) as documented in [capire](https://cap.cloud.sap)
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/in-vscode)
```sh
npm i -g @sap/cds-dk
```
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/tools#vscode)
### Download ### 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). Clone this repo as shown below, if you have [git](https://git-scm.com/downloads) installed,
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 samples
@@ -43,45 +39,37 @@ cds watch bookshop
After that open this link in your browser: [http://localhost:4004](http://localhost:4004) After that open this link in your browser: [http://localhost:4004](http://localhost:4004)
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 ### Testing
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example: Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example:
```sh ```sh
npx jest npx jest
``` ```
> While mocha is a bit smaller and faster, jest runs tests in parallel and isolation, which allows to run all tests. > While mocha is a bit smaller and faster, jest runs tests in parallel and isolation, which allows to run all tests.
### Serve `npm` ### 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: We've simple npm registry mock included which allows you to do an `npm install @capire/<package>` anywhere locally. Use it as follows:
1. Start the @capire registry: 1. Start the @capire registry:
```sh ```sh
npm run registry npm run registry
``` ```
> While running this will have `@capire:registry=http://localhost:4444` set with npmrc. > 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:
2. Install one of the @capire packages wherever you like, e.g.:
```sh ```sh
npm add @capire/common @capire/bookshop 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 documentation at [https://cap.cloud.sap](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 have a question, find a bug, or otherwise need support, please use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce).
## 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 the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt) file.

View File

@@ -36,7 +36,7 @@
<td class="rating-stars"> <td class="rating-stars">
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }} {{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }}
</td> </td>
<td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td> <td>{{ book.currency.symbol }} {{ book.price }}</td>
</tr> </tr>
</table> </table>

View File

@@ -5,10 +5,10 @@ This stand-alone sample introduces the essential tasks in the development of CAP
## Hypothetical Use Cases ## Hypothetical Use Cases
1. Build a service that allows to browse _Books_ and _Authors_. 1. Build a service that allows to browse _Books_ and _Authors_.
2. Books have assigned _Genres_, which are organized hierarchically. 2. Books have assigned _Genres_ which are organized hierarchically.
3. All users may browse books without login. 3. All users may browse books without login.
4. All entries are maintained by Administrators. 4. All entries are maintained by Administrators.
5. End users may order books (the actual order mgmt being out of scope). 5. End users may order books (the actual order mgmt being out of scope)
## Running the Sample ## Running the Sample
@@ -20,12 +20,12 @@ npm run watch
| Links to capire | Sample files / folders | | Links to capire | Sample files / folders |
| --------------------------------------------------------------------------------------------------------- | ------------------------------------ | | --------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| [Project Setup & Layouts](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./`](./) | | [Project Setup and 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 Domain Models](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) | | [Defining Services](https://cap.cloud.sap/docs/guides/providing-services) | [`./srv/*.cds`](./srv) |
| [Single-purposed Services](https://cap.cloud.sap/docs/guides/services#single-purposed-services) | [`./srv/*.cds`](./srv) | | [Single-purposed Services](https://cap.cloud.sap/docs/guides/providing-services#single-purposed-services) | [`./srv/*.cds`](./srv) |
| [Providing & Consuming Providers](https://cap.cloud.sap/docs/guides/providing-services) | http://localhost:4004 | | [Generic 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) | | Using Databases | [`./db/data/*.csv`](./db/data) |
| [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) | [`./srv/*.js`](./srv) | | [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) | [`./srv/*.js`](./srv) |
| Adding Tests | [`./test`](./test) | | Adding Tests | [`./test`](./test) |
| [Sharing for Reuse](https://cap.cloud.sap/docs/guides/reuse-and-compose) | [`./index.cds`](./index.cds) | | [Sharing for Reuse](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./index.cds`](./index.cds) |

View File

@@ -1,12 +1,12 @@
using { sap.capire.bookshop as my } from '../db/schema'; using { sap.capire.bookshop as my } from '../db/schema';
service CatalogService @(path:'/browse') { service CatalogService @(path:'/browse') {
@readonly entity Books as SELECT from my.Books { *, @readonly entity Books as SELECT from my.Books {*,
author.name as author author.name as author
} excluding { createdBy, modifiedBy }; } excluding { createdBy, modifiedBy };
@readonly entity ListOfBooks as SELECT from Books @readonly entity ListOfBooks as SELECT from Books
excluding { descr }; excluding { descr, stock };
@requires: 'authenticated-user' @requires: 'authenticated-user'
action submitOrder ( book: Books:ID, amount: Integer ) returns { stock: Integer }; action submitOrder ( book: Books:ID, amount: Integer ) returns { stock: Integer };

View File

@@ -1,7 +1,7 @@
const cds = require('@sap/cds') const cds = require('@sap/cds')
const { Books } = cds.entities ('sap.capire.bookshop') const { Books } = cds.entities ('sap.capire.bookshop')
class CatalogService extends cds.ApplicationService { init(){ class CatalogService extends cds.ApplicationService { async init(){
// Reduce stock of ordered books if available stock suffices // Reduce stock of ordered books if available stock suffices
this.on ('submitOrder', async req => { this.on ('submitOrder', async req => {
@@ -9,7 +9,7 @@ class CatalogService extends cds.ApplicationService { init(){
let {stock} = await tx.read('stock').from(Books,book) let {stock} = await tx.read('stock').from(Books,book)
if (stock >= amount) { if (stock >= amount) {
await tx.update (Books,book).with ({ stock: stock -= amount }) await tx.update (Books,book).with ({ stock: stock -= amount })
await this.emit ('OrderedBook', { book, amount, buyer:req.user.id }) this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
return { stock } return { stock }
} }
else return req.error (409,`${amount} exceeds stock for book #${book}`) else return req.error (409,`${amount} exceeds stock for book #${book}`)

View File

@@ -1,8 +1,4 @@
{ {
"name": "@capire/common", "name": "@capire/common",
"description": "Provides a pre-built extension package for std @sap/cds/common", "version": "1.0.0"
"version": "1.0.0",
"dependencies": {
"@sap/cds": "*"
}
} }

View File

@@ -1,2 +1,3 @@
# cds.requires.messaging.kind = file-based-messaging # cds.requires.messaging.kind = file-based-messaging
cds.cdsc.severities.extend-for-generated = info
PORT = 4004 PORT = 4004

View File

@@ -11,7 +11,6 @@ DateOfBirth = Date of Birth
DateOfDeath = Date of Death DateOfDeath = Date of Death
PlaceOfBirth = Place of Birth PlaceOfBirth = Place of Birth
PlaceOfDeath = Place of Death PlaceOfDeath = Place of Death
Age = Age
Authors = Authors Authors = Authors
Order = Order Order = Order
Orders = Orders Orders = Orders

View File

@@ -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

View File

@@ -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",
@@ -40,7 +40,8 @@
</script> </script>
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></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" <!-- <script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js" -->
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/1.78.6/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_fiori_3"

View File

@@ -1,4 +1,4 @@
using { AdminService } from '../../db'; using AdminService from '@capire/bookshop';
//////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////
// //
@@ -39,27 +39,6 @@ annotate AdminService.Books with @(
} }
); );
annotate AdminService.Authors with @(
UI: {
HeaderInfo: {
Description: {Value: lifetime}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Books}', Target: 'books/@UI.LineItem'},
],
FieldGroup#Details: {
Data: [
{Value: placeOfBirth},
{Value: placeOfDeath},
{Value: dateOfBirth},
{Value: dateOfDeath},
{Value: age, Label: '{i18n>Age}'},
]
},
}
);
//////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////

View File

@@ -1,3 +1,3 @@
<head> <head>
<meta http-equiv="refresh" content="0;url=bookshop/index.html"> <meta http-equiv="refresh" content="0;url=vue/bookshop/index.html">
</head> </head>

View File

@@ -1,3 +1,3 @@
<head> <head>
<meta http-equiv="refresh" content="0;url=reviews/index.html"> <meta http-equiv="refresh" content="0;url=vue/reviews/index.html">
</head> </head>

View File

@@ -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
}

View File

@@ -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;
}

View File

@@ -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
}

View File

@@ -3,47 +3,28 @@
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@capire/bookshop": "*", "@capire/bookshop": "*",
"@capire/common": "*",
"@capire/orders": "*",
"@capire/reviews": "*", "@capire/reviews": "*",
"@capire/suppliers": "*", "@capire/orders": "*",
"@capire/common": "*",
"@sap/cds": "^4", "@sap/cds": "^4",
"express": "^4.17.1", "express": "^4.17.1",
"passport": "^0.4.1" "passport": "0.4.1"
}, },
"scripts": { "scripts": {
"start": "cds run --in-memory?", "start": "cds run --in-memory?",
"watch": "cds watch" "watch": "cds watch"
}, },
"cds": { "cds": {
"hana": {
"deploy-format": "hdbtable"
},
"requires": { "requires": {
"API_BUSINESS_PARTNER": {
"kind": "odata",
"model": "@capire/suppliers"
},
"auth": {
"strategy": "dummy"
},
"ReviewsService": { "ReviewsService": {
"kind": "odata", "kind": "odata", "model": "@capire/reviews"
"model": "@capire/reviews"
}, },
"OrdersService": { "OrdersService": {
"kind": "odata", "kind": "odata", "model": "@capire/orders"
"model": "@capire/orders"
}, },
"db": { "db": {
"kind": "sql", "kind": "sql"
"[development]": {
"model": "db/sqlite"
},
"[production]": {
"model": "db/hana"
}
} }
} }
} }
} }

View File

@@ -1,19 +1,19 @@
const express = require ('express')
const cds = require ('@sap/cds') const cds = require ('@sap/cds')
cds.once('bootstrap',(app)=>{ cds.once('bootstrap',(app)=>{
app.use ('/orders/webapp', _from('@capire/orders/app/orders/webapp/manifest.json')) const {dirname} = require ('path')
app.use ('/bookshop', _from('@capire/bookshop/app/vue/index.html')) // serving the orders app imported from @capire/orders
app.use ('/reviews', _from('@capire/reviews/app/vue/index.html')) const orders_app = dirname (require.resolve('@capire/orders/app/orders/webapp/manifest.json'))
app.use ('/orders/webapp', express.static(orders_app))
// serving the vue.js app imported from @capire/bookshop
const bookshop_app = dirname (require.resolve('@capire/bookshop/app/vue/index.html'))
app.use ('/vue/bookshop', express.static(bookshop_app))
// serving the vue.js app imported from @capire/reviews
const reviews_app = dirname (require.resolve('@capire/reviews/app/vue/index.html'))
app.use ('/vue/reviews', express.static(reviews_app))
}) })
cds.once('served', require('./srv/mashup')) cds.once('served', require('./srv/mashup'))
cds.once('served', require('@capire/suppliers/srv/mashup'))
module.exports = cds.server 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)))

View File

@@ -49,7 +49,7 @@ module.exports = async()=>{ // called by server.js
// //
// Reduce stock of ordered books for orders are created from Orders admin UI // Reduce stock of ordered books for orders are created from Orders admin UI
// //
OrdersService.on ('OrderChanged', (msg) => { OrdersService.on ('OrderChanged', async (msg) => {
console.debug ('> received:', msg.event, msg.data) console.debug ('> received:', msg.event, msg.data)
const { product, deltaAmount } = msg.data const { product, deltaAmount } = msg.data
return UPDATE (Books) .where ('ID =', product) return UPDATE (Books) .where ('ID =', product)

View File

@@ -42,36 +42,7 @@ GET {{bookshop}}/browse/Books(201)?
################################################# #################################################
# #
# Orders Service, incl. draft choreography # Orders Service
# #
@newOrderID = e939604c-ab83-4d4f-bdb6-95fe30b3773e
GET {{bookshop}}/orders/Orders 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"
}

View File

@@ -121,7 +121,7 @@
"name": "sap.fe.templates.ObjectPage", "name": "sap.fe.templates.ObjectPage",
"options": { "options": {
"settings" : { "settings" : {
"entitySet": "Orders_Items" "entitySet": "OrderItems"
} }
} }
}, },

View File

@@ -1,23 +1,2 @@
using { Currency, User, managed, cuid } from '@sap/cds/common'; // using from './without-managed-composition/schema';
namespace sap.capire.orders; using from './with-managed-composition/schema';
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;
}

View File

@@ -0,0 +1,23 @@
using { Currency, User, managed, cuid } from '@sap/cds/common';
using from '@capire/common';
namespace sap.capire.orders;
entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many {
key ID : UUID;
@assert.integrity:false // REVISIT: this is a temporary workaround for a glitch in cds-runtime
product : Association to Products;
amount : Integer;
title : String;
price : Double;
};
buyer : User;
currency : Currency;
}
/** This is a stand-in for arbitrary ordered Products */
@cds.persistence.skip:'always'
entity Products {
key ID : String;
}

View File

@@ -0,0 +1,26 @@
using { Currency, User, managed, cuid } from '@sap/cds/common';
using from '@capire/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 {
up_ : Association to Orders not null; //> IMPORTANT for Draft: not key(!)
key ID : UUID;
@assert.integrity:false // REVISIT: this is a temporary workaround for a glitch in cds-runtime
product : Association to Products;
amount : Integer;
title : String;
price : Double;
}
/** This is a stand-in for arbitrary ordered Products */
@cds.persistence.skip:'always'
entity Products {
key ID : String;
}

465
orders/managed-comp.xml Normal file
View File

@@ -0,0 +1,465 @@
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml">
<edmx:Include Alias="Common" Namespace="com.sap.vocabularies.Common.v1"/>
</edmx:Reference>
<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1"/>
</edmx:Reference>
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml">
<edmx:Include Alias="UI" Namespace="com.sap.vocabularies.UI.v1"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="OrdersService" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer">
<EntitySet Name="Currencies" EntityType="OrdersService.Currencies">
<NavigationPropertyBinding Path="texts" Target="Currencies_texts"/>
<NavigationPropertyBinding Path="localized" Target="Currencies_texts"/>
</EntitySet>
<EntitySet Name="Currencies_texts" EntityType="OrdersService.Currencies_texts"/>
<EntitySet Name="Orders" EntityType="OrdersService.Orders">
<NavigationPropertyBinding Path="Items" Target="Orders_Items"/>
<NavigationPropertyBinding Path="currency" Target="Currencies"/>
<NavigationPropertyBinding Path="SiblingEntity" Target="Orders"/>
</EntitySet>
<EntitySet Name="Orders_Items" EntityType="OrdersService.Orders_Items">
<NavigationPropertyBinding Path="up_" Target="Orders"/>
<NavigationPropertyBinding Path="SiblingEntity" Target="Orders_Items"/>
</EntitySet>
</EntityContainer>
<EntityType Name="Currencies">
<Key>
<PropertyRef Name="code"/>
</Key>
<Property Name="name" Type="Edm.String" MaxLength="255"/>
<Property Name="descr" Type="Edm.String" MaxLength="1000"/>
<Property Name="code" Type="Edm.String" MaxLength="3" Nullable="false"/>
<Property Name="symbol" Type="Edm.String" MaxLength="5"/>
<Property Name="numcode" Type="Edm.Int32"/>
<Property Name="exponent" Type="Edm.Int32"/>
<Property Name="minor" Type="Edm.String"/>
<NavigationProperty Name="texts" Type="Collection(OrdersService.Currencies_texts)">
<OnDelete Action="Cascade"/>
</NavigationProperty>
<NavigationProperty Name="localized" Type="OrdersService.Currencies_texts">
<ReferentialConstraint Property="code" ReferencedProperty="code"/>
</NavigationProperty>
</EntityType>
<EntityType Name="Currencies_texts">
<Key>
<PropertyRef Name="locale"/>
<PropertyRef Name="code"/>
</Key>
<Property Name="locale" Type="Edm.String" MaxLength="14" Nullable="false"/>
<Property Name="name" Type="Edm.String" MaxLength="255"/>
<Property Name="descr" Type="Edm.String" MaxLength="1000"/>
<Property Name="code" Type="Edm.String" MaxLength="3" Nullable="false"/>
</EntityType>
<EntityType Name="DraftAdministrativeData">
<Key>
<PropertyRef Name="DraftUUID"/>
</Key>
<Property Name="DraftUUID" Type="Edm.Guid" Nullable="false"/>
<Property Name="CreationDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="CreatedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsCreatedByMe" Type="Edm.Boolean"/>
<Property Name="LastChangeDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="LastChangedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="InProcessByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsProcessedByMe" Type="Edm.Boolean"/>
</EntityType>
<EntityType Name="Orders">
<Key>
<PropertyRef Name="ID"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<Property Name="createdAt" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="createdBy" Type="Edm.String" MaxLength="255"/>
<Property Name="modifiedAt" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="modifiedBy" Type="Edm.String" MaxLength="255"/>
<Property Name="OrderNo" Type="Edm.String"/>
<NavigationProperty Name="Items" Type="Collection(OrdersService.Orders_Items)" Partner="up_">
<OnDelete Action="Cascade"/>
</NavigationProperty>
<Property Name="buyer" Type="Edm.String" MaxLength="255"/>
<NavigationProperty Name="currency" Type="OrdersService.Currencies">
<ReferentialConstraint Property="currency_code" ReferencedProperty="code"/>
</NavigationProperty>
<Property Name="currency_code" Type="Edm.String" MaxLength="3"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="OrdersService.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="OrdersService.Orders"/>
</EntityType>
<EntityType Name="Orders_Items">
<Key>
<PropertyRef Name="up__ID"/>
<PropertyRef Name="ID"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="up__ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="up_" Type="OrdersService.Orders" Nullable="false" Partner="Items">
<ReferentialConstraint Property="up__ID" ReferencedProperty="ID"/>
</NavigationProperty>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<Property Name="amount" Type="Edm.Int32"/>
<Property Name="title" Type="Edm.String"/>
<Property Name="price" Type="Edm.Double"/>
<Property Name="product_ID" Type="Edm.String"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="OrdersService.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="OrdersService.Orders_Items"/>
</EntityType>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="OrdersService.Orders"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="OrdersService.Orders"/>
</Action>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="OrdersService.Orders_Items"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="OrdersService.Orders_Items"/>
</Action>
<Action Name="draftActivate" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="OrdersService.Orders"/>
<ReturnType Type="OrdersService.Orders"/>
</Action>
<Action Name="draftEdit" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="OrdersService.Orders"/>
<Parameter Name="PreserveChanges" Type="Edm.Boolean"/>
<ReturnType Type="OrdersService.Orders"/>
</Action>
<Annotations Target="OrdersService.Currencies">
<Annotation Term="UI.Identification">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="name"/>
</Record>
</Collection>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.Currencies/name">
<Annotation Term="Common.Label" String="{i18n>Name}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies/descr">
<Annotation Term="Common.Label" String="{i18n>Description}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies/code">
<Annotation Term="Common.Label" String="{i18n>CurrencyCode}"/>
<Annotation Term="Common.Text" Path="name"/>
</Annotations>
<Annotations Target="OrdersService.Currencies/symbol">
<Annotation Term="Common.Label" String="{i18n>CurrencySymbol}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies_texts/name">
<Annotation Term="Common.Label" String="{i18n>Name}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies_texts/descr">
<Annotation Term="Common.Label" String="{i18n>Description}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies_texts/code">
<Annotation Term="Common.Label" String="{i18n>CurrencyCode}"/>
<Annotation Term="Common.Text" Path="name"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftAdministrativeData}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/DraftUUID">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftUUID}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/CreationDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_CreationDateTime}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/CreatedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_CreatedByUser}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/DraftIsCreatedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsCreatedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/LastChangeDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangeDateTime}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/LastChangedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangedByUser}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/InProcessByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_InProcessByUser}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/DraftIsProcessedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsProcessedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders">
<Annotation Term="UI.Facets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>Details}"/>
<PropertyValue Property="Target" AnnotationPath="@UI.FieldGroup#Details"/>
</Record>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>OrderItems}"/>
<PropertyValue Property="Target" AnnotationPath="Items/@UI.LineItem"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.FieldGroup" Qualifier="Created">
<Record Type="UI.FieldGroupType">
<PropertyValue Property="Data">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdBy"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdAt"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="UI.FieldGroup" Qualifier="Details">
<Record Type="UI.FieldGroupType">
<PropertyValue Property="Data">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="currency/code"/>
<PropertyValue Property="Label" String="Currency"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="UI.FieldGroup" Qualifier="Modified">
<Record Type="UI.FieldGroupType">
<PropertyValue Property="Data">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="modifiedBy"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="modifiedAt"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="UI.HeaderFacets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>Created}"/>
<PropertyValue Property="Target" AnnotationPath="@UI.FieldGroup#Created"/>
</Record>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>Modified}"/>
<PropertyValue Property="Target" AnnotationPath="@UI.FieldGroup#Modified"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.HeaderInfo">
<Record Type="UI.HeaderInfoType">
<PropertyValue Property="Description">
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdBy"/>
</Record>
</PropertyValue>
<PropertyValue Property="Title">
<Record Type="UI.DataField">
<PropertyValue Property="Label" String="Order number "/>
<PropertyValue Property="Value" Path="OrderNo"/>
</Record>
</PropertyValue>
<PropertyValue Property="TypeName" String="Order"/>
<PropertyValue Property="TypeNamePlural" String="Orders"/>
</Record>
</Annotation>
<Annotation Term="UI.Identification">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdBy"/>
<PropertyValue Property="Label" String="Customer"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdAt"/>
<PropertyValue Property="Label" String="Date"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="OrderNo"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="OrderNo"/>
<PropertyValue Property="Label" String="OrderNo"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="buyer"/>
<PropertyValue Property="Label" String="Customer"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdAt"/>
<PropertyValue Property="Label" String="Date"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.SelectionFields">
<Collection>
<PropertyPath>createdAt</PropertyPath>
<PropertyPath>createdBy</PropertyPath>
</Collection>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.EntityContainer/Orders">
<Annotation Term="Common.DraftRoot">
<Record Type="Common.DraftRootType">
<PropertyValue Property="ActivationAction" String="OrdersService.draftActivate"/>
<PropertyValue Property="EditAction" String="OrdersService.draftEdit"/>
<PropertyValue Property="PreparationAction" String="OrdersService.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.Orders/createdAt">
<Annotation Term="Common.Label" String="{i18n>CreatedAt}"/>
<Annotation Term="Core.Computed" Bool="true"/>
<Annotation Term="Core.Immutable" Bool="true"/>
<Annotation Term="UI.HiddenFilter" Bool="false"/>
</Annotations>
<Annotations Target="OrdersService.Orders/createdBy">
<Annotation Term="Common.Label" String="{i18n>CreatedBy}"/>
<Annotation Term="Core.Computed" Bool="true"/>
<Annotation Term="Core.Description" String="{i18n>UserID.Description}"/>
<Annotation Term="Core.Immutable" Bool="true"/>
<Annotation Term="UI.HiddenFilter" Bool="false"/>
</Annotations>
<Annotations Target="OrdersService.Orders/modifiedAt">
<Annotation Term="Common.Label" String="{i18n>ChangedAt}"/>
<Annotation Term="Core.Computed" Bool="true"/>
<Annotation Term="UI.HiddenFilter" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/modifiedBy">
<Annotation Term="Common.Label" String="{i18n>ChangedBy}"/>
<Annotation Term="Core.Computed" Bool="true"/>
<Annotation Term="Core.Description" String="{i18n>UserID.Description}"/>
<Annotation Term="UI.HiddenFilter" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/OrderNo">
<Annotation Term="Common.Label" String="Order Number"/>
</Annotations>
<Annotations Target="OrdersService.Orders/buyer">
<Annotation Term="Common.Label" String="{i18n>UserID}"/>
<Annotation Term="Core.Description" String="{i18n>UserID.Description}"/>
</Annotations>
<Annotations Target="OrdersService.Orders/currency">
<Annotation Term="Common.Label" String="{i18n>Currency}"/>
<Annotation Term="Core.Description" String="{i18n>CurrencyCode.Description}"/>
</Annotations>
<Annotations Target="OrdersService.Orders/currency_code">
<Annotation Term="Common.Label" String="{i18n>Currency}"/>
<Annotation Term="Common.ValueList">
<Record Type="Common.ValueListType">
<PropertyValue Property="Label" String="{i18n>Currency}"/>
<PropertyValue Property="CollectionPath" String="Currencies"/>
<PropertyValue Property="Parameters">
<Collection>
<Record Type="Common.ValueListParameterInOut">
<PropertyValue Property="LocalDataProperty" PropertyPath="currency_code"/>
<PropertyValue Property="ValueListProperty" String="code"/>
</Record>
<Record Type="Common.ValueListParameterDisplayOnly">
<PropertyValue Property="ValueListProperty" String="name"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="Core.Description" String="{i18n>CurrencyCode.Description}"/>
</Annotations>
<Annotations Target="OrdersService.Orders/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items">
<Annotation Term="UI.Facets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>OrderItems}"/>
<PropertyValue Property="Target" AnnotationPath="@UI.Identification"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.Identification">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="amount"/>
<PropertyValue Property="Label" String="Amount"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="title"/>
<PropertyValue Property="Label" String="Product"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="price"/>
<PropertyValue Property="Label" String="Unit Price"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="product_ID"/>
<PropertyValue Property="Label" String="Product ID"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="title"/>
<PropertyValue Property="Label" String="Product Title"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="price"/>
<PropertyValue Property="Label" String="Unit Price"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="amount"/>
<PropertyValue Property="Label" String="Quantity"/>
</Record>
</Collection>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.EntityContainer/Orders_Items">
<Annotation Term="Common.DraftNode">
<Record Type="Common.DraftNodeType">
<PropertyValue Property="PreparationAction" String="OrdersService.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/amount">
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/Mandatory"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

464
orders/metadata.xml Normal file
View File

@@ -0,0 +1,464 @@
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml">
<edmx:Include Alias="Common" Namespace="com.sap.vocabularies.Common.v1"/>
</edmx:Reference>
<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1"/>
</edmx:Reference>
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml">
<edmx:Include Alias="UI" Namespace="com.sap.vocabularies.UI.v1"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="OrdersService" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer">
<EntitySet Name="Currencies" EntityType="OrdersService.Currencies">
<NavigationPropertyBinding Path="texts" Target="Currencies_texts"/>
<NavigationPropertyBinding Path="localized" Target="Currencies_texts"/>
</EntitySet>
<EntitySet Name="Currencies_texts" EntityType="OrdersService.Currencies_texts"/>
<EntitySet Name="Orders" EntityType="OrdersService.Orders">
<NavigationPropertyBinding Path="Items" Target="Orders_Items"/>
<NavigationPropertyBinding Path="currency" Target="Currencies"/>
<NavigationPropertyBinding Path="SiblingEntity" Target="Orders"/>
</EntitySet>
<EntitySet Name="Orders_Items" EntityType="OrdersService.Orders_Items">
<NavigationPropertyBinding Path="up_" Target="Orders"/>
<NavigationPropertyBinding Path="SiblingEntity" Target="Orders_Items"/>
</EntitySet>
</EntityContainer>
<EntityType Name="Currencies">
<Key>
<PropertyRef Name="code"/>
</Key>
<Property Name="name" Type="Edm.String" MaxLength="255"/>
<Property Name="descr" Type="Edm.String" MaxLength="1000"/>
<Property Name="code" Type="Edm.String" MaxLength="3" Nullable="false"/>
<Property Name="symbol" Type="Edm.String" MaxLength="5"/>
<Property Name="numcode" Type="Edm.Int32"/>
<Property Name="exponent" Type="Edm.Int32"/>
<Property Name="minor" Type="Edm.String"/>
<NavigationProperty Name="texts" Type="Collection(OrdersService.Currencies_texts)">
<OnDelete Action="Cascade"/>
</NavigationProperty>
<NavigationProperty Name="localized" Type="OrdersService.Currencies_texts">
<ReferentialConstraint Property="code" ReferencedProperty="code"/>
</NavigationProperty>
</EntityType>
<EntityType Name="Currencies_texts">
<Key>
<PropertyRef Name="locale"/>
<PropertyRef Name="code"/>
</Key>
<Property Name="locale" Type="Edm.String" MaxLength="14" Nullable="false"/>
<Property Name="name" Type="Edm.String" MaxLength="255"/>
<Property Name="descr" Type="Edm.String" MaxLength="1000"/>
<Property Name="code" Type="Edm.String" MaxLength="3" Nullable="false"/>
</EntityType>
<EntityType Name="DraftAdministrativeData">
<Key>
<PropertyRef Name="DraftUUID"/>
</Key>
<Property Name="DraftUUID" Type="Edm.Guid" Nullable="false"/>
<Property Name="CreationDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="CreatedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsCreatedByMe" Type="Edm.Boolean"/>
<Property Name="LastChangeDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="LastChangedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="InProcessByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsProcessedByMe" Type="Edm.Boolean"/>
</EntityType>
<EntityType Name="Orders">
<Key>
<PropertyRef Name="ID"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<Property Name="createdAt" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="createdBy" Type="Edm.String" MaxLength="255"/>
<Property Name="modifiedAt" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="modifiedBy" Type="Edm.String" MaxLength="255"/>
<Property Name="OrderNo" Type="Edm.String"/>
<NavigationProperty Name="Items" Type="Collection(OrdersService.Orders_Items)" Partner="up_">
<OnDelete Action="Cascade"/>
</NavigationProperty>
<Property Name="buyer" Type="Edm.String" MaxLength="255"/>
<NavigationProperty Name="currency" Type="OrdersService.Currencies">
<ReferentialConstraint Property="currency_code" ReferencedProperty="code"/>
</NavigationProperty>
<Property Name="currency_code" Type="Edm.String" MaxLength="3"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="OrdersService.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="OrdersService.Orders"/>
</EntityType>
<EntityType Name="Orders_Items">
<Key>
<PropertyRef Name="ID"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="up__ID" Type="Edm.Guid"/>
<NavigationProperty Name="up_" Type="OrdersService.Orders" Partner="Items">
<ReferentialConstraint Property="up__ID" ReferencedProperty="ID"/>
</NavigationProperty>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<Property Name="amount" Type="Edm.Int32"/>
<Property Name="title" Type="Edm.String"/>
<Property Name="price" Type="Edm.Double"/>
<Property Name="product_ID" Type="Edm.String"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="OrdersService.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="OrdersService.Orders_Items"/>
</EntityType>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="OrdersService.Orders"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="OrdersService.Orders"/>
</Action>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="OrdersService.Orders_Items"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="OrdersService.Orders_Items"/>
</Action>
<Action Name="draftActivate" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="OrdersService.Orders"/>
<ReturnType Type="OrdersService.Orders"/>
</Action>
<Action Name="draftEdit" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="OrdersService.Orders"/>
<Parameter Name="PreserveChanges" Type="Edm.Boolean"/>
<ReturnType Type="OrdersService.Orders"/>
</Action>
<Annotations Target="OrdersService.Currencies">
<Annotation Term="UI.Identification">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="name"/>
</Record>
</Collection>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.Currencies/name">
<Annotation Term="Common.Label" String="{i18n>Name}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies/descr">
<Annotation Term="Common.Label" String="{i18n>Description}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies/code">
<Annotation Term="Common.Label" String="{i18n>CurrencyCode}"/>
<Annotation Term="Common.Text" Path="name"/>
</Annotations>
<Annotations Target="OrdersService.Currencies/symbol">
<Annotation Term="Common.Label" String="{i18n>CurrencySymbol}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies_texts/name">
<Annotation Term="Common.Label" String="{i18n>Name}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies_texts/descr">
<Annotation Term="Common.Label" String="{i18n>Description}"/>
</Annotations>
<Annotations Target="OrdersService.Currencies_texts/code">
<Annotation Term="Common.Label" String="{i18n>CurrencyCode}"/>
<Annotation Term="Common.Text" Path="name"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftAdministrativeData}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/DraftUUID">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftUUID}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/CreationDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_CreationDateTime}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/CreatedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_CreatedByUser}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/DraftIsCreatedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsCreatedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/LastChangeDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangeDateTime}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/LastChangedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangedByUser}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/InProcessByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_InProcessByUser}"/>
</Annotations>
<Annotations Target="OrdersService.DraftAdministrativeData/DraftIsProcessedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsProcessedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders">
<Annotation Term="UI.Facets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>Details}"/>
<PropertyValue Property="Target" AnnotationPath="@UI.FieldGroup#Details"/>
</Record>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>OrderItems}"/>
<PropertyValue Property="Target" AnnotationPath="Items/@UI.LineItem"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.FieldGroup" Qualifier="Created">
<Record Type="UI.FieldGroupType">
<PropertyValue Property="Data">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdBy"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdAt"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="UI.FieldGroup" Qualifier="Details">
<Record Type="UI.FieldGroupType">
<PropertyValue Property="Data">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="currency/code"/>
<PropertyValue Property="Label" String="Currency"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="UI.FieldGroup" Qualifier="Modified">
<Record Type="UI.FieldGroupType">
<PropertyValue Property="Data">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="modifiedBy"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="modifiedAt"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="UI.HeaderFacets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>Created}"/>
<PropertyValue Property="Target" AnnotationPath="@UI.FieldGroup#Created"/>
</Record>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>Modified}"/>
<PropertyValue Property="Target" AnnotationPath="@UI.FieldGroup#Modified"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.HeaderInfo">
<Record Type="UI.HeaderInfoType">
<PropertyValue Property="Description">
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdBy"/>
</Record>
</PropertyValue>
<PropertyValue Property="Title">
<Record Type="UI.DataField">
<PropertyValue Property="Label" String="Order number "/>
<PropertyValue Property="Value" Path="OrderNo"/>
</Record>
</PropertyValue>
<PropertyValue Property="TypeName" String="Order"/>
<PropertyValue Property="TypeNamePlural" String="Orders"/>
</Record>
</Annotation>
<Annotation Term="UI.Identification">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdBy"/>
<PropertyValue Property="Label" String="Customer"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdAt"/>
<PropertyValue Property="Label" String="Date"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="OrderNo"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="OrderNo"/>
<PropertyValue Property="Label" String="OrderNo"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="buyer"/>
<PropertyValue Property="Label" String="Customer"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="createdAt"/>
<PropertyValue Property="Label" String="Date"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.SelectionFields">
<Collection>
<PropertyPath>createdAt</PropertyPath>
<PropertyPath>createdBy</PropertyPath>
</Collection>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.EntityContainer/Orders">
<Annotation Term="Common.DraftRoot">
<Record Type="Common.DraftRootType">
<PropertyValue Property="ActivationAction" String="OrdersService.draftActivate"/>
<PropertyValue Property="EditAction" String="OrdersService.draftEdit"/>
<PropertyValue Property="PreparationAction" String="OrdersService.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.Orders/createdAt">
<Annotation Term="Common.Label" String="{i18n>CreatedAt}"/>
<Annotation Term="Core.Computed" Bool="true"/>
<Annotation Term="Core.Immutable" Bool="true"/>
<Annotation Term="UI.HiddenFilter" Bool="false"/>
</Annotations>
<Annotations Target="OrdersService.Orders/createdBy">
<Annotation Term="Common.Label" String="{i18n>CreatedBy}"/>
<Annotation Term="Core.Computed" Bool="true"/>
<Annotation Term="Core.Description" String="{i18n>UserID.Description}"/>
<Annotation Term="Core.Immutable" Bool="true"/>
<Annotation Term="UI.HiddenFilter" Bool="false"/>
</Annotations>
<Annotations Target="OrdersService.Orders/modifiedAt">
<Annotation Term="Common.Label" String="{i18n>ChangedAt}"/>
<Annotation Term="Core.Computed" Bool="true"/>
<Annotation Term="UI.HiddenFilter" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/modifiedBy">
<Annotation Term="Common.Label" String="{i18n>ChangedBy}"/>
<Annotation Term="Core.Computed" Bool="true"/>
<Annotation Term="Core.Description" String="{i18n>UserID.Description}"/>
<Annotation Term="UI.HiddenFilter" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/OrderNo">
<Annotation Term="Common.Label" String="Order Number"/>
</Annotations>
<Annotations Target="OrdersService.Orders/buyer">
<Annotation Term="Common.Label" String="{i18n>UserID}"/>
<Annotation Term="Core.Description" String="{i18n>UserID.Description}"/>
</Annotations>
<Annotations Target="OrdersService.Orders/currency">
<Annotation Term="Common.Label" String="{i18n>Currency}"/>
<Annotation Term="Core.Description" String="{i18n>CurrencyCode.Description}"/>
</Annotations>
<Annotations Target="OrdersService.Orders/currency_code">
<Annotation Term="Common.Label" String="{i18n>Currency}"/>
<Annotation Term="Common.ValueList">
<Record Type="Common.ValueListType">
<PropertyValue Property="Label" String="{i18n>Currency}"/>
<PropertyValue Property="CollectionPath" String="Currencies"/>
<PropertyValue Property="Parameters">
<Collection>
<Record Type="Common.ValueListParameterInOut">
<PropertyValue Property="LocalDataProperty" PropertyPath="currency_code"/>
<PropertyValue Property="ValueListProperty" String="code"/>
</Record>
<Record Type="Common.ValueListParameterDisplayOnly">
<PropertyValue Property="ValueListProperty" String="name"/>
</Record>
</Collection>
</PropertyValue>
</Record>
</Annotation>
<Annotation Term="Core.Description" String="{i18n>CurrencyCode.Description}"/>
</Annotations>
<Annotations Target="OrdersService.Orders/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items">
<Annotation Term="UI.Facets">
<Collection>
<Record Type="UI.ReferenceFacet">
<PropertyValue Property="Label" String="{i18n>OrderItems}"/>
<PropertyValue Property="Target" AnnotationPath="@UI.Identification"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.Identification">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="amount"/>
<PropertyValue Property="Label" String="Amount"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="title"/>
<PropertyValue Property="Label" String="Product"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="price"/>
<PropertyValue Property="Label" String="Unit Price"/>
</Record>
</Collection>
</Annotation>
<Annotation Term="UI.LineItem">
<Collection>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="product_ID"/>
<PropertyValue Property="Label" String="Product ID"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="title"/>
<PropertyValue Property="Label" String="Product Title"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="price"/>
<PropertyValue Property="Label" String="Unit Price"/>
</Record>
<Record Type="UI.DataField">
<PropertyValue Property="Value" Path="amount"/>
<PropertyValue Property="Label" String="Quantity"/>
</Record>
</Collection>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.EntityContainer/Orders_Items">
<Annotation Term="Common.DraftNode">
<Record Type="Common.DraftNodeType">
<PropertyValue Property="PreparationAction" String="OrdersService.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/amount">
<Annotation Term="Common.FieldControl" EnumMember="Common.FieldControlType/Mandatory"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="OrdersService.Orders_Items/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@@ -2,6 +2,12 @@
"name": "@capire/orders", "name": "@capire/orders",
"version": "1.0.0", "version": "1.0.0",
"dependencies": { "dependencies": {
"@capire/common": "*",
"@sap/cds": "^4.3.0" "@sap/cds": "^4.3.0"
},
"cds": {
"odata": {
"--containment": true
}
} }
} }

View File

@@ -11,7 +11,7 @@ class OrdersService extends cds.ApplicationService {
const { amount:before } = await cds.tx(req).run ( const { amount:before } = await cds.tx(req).run (
SELECT.one.from (OrderItems, oi => oi.amount) .where ({up__ID:ID, product_ID}) SELECT.one.from (OrderItems, oi => oi.amount) .where ({up__ID:ID, product_ID})
) )
if (amount != before) await this.orderChanged (product_ID, amount-before) if (amount != before) this.orderChanged (product_ID, amount-before)
} }
}) })
@@ -20,7 +20,7 @@ class OrdersService extends cds.ApplicationService {
const Items = await cds.tx(req).run ( const Items = await cds.tx(req).run (
SELECT.from (OrderItems, oi => { oi.product_ID, oi.amount }) .where ({up__ID:ID}) SELECT.from (OrderItems, oi => { oi.product_ID, oi.amount }) .where ({up__ID:ID})
) )
if (Items) await Promise.all (Items.map(it => this.orderChanged (it.product_ID, -it.amount))) if (Items) for (let it of Items) this.orderChanged (it.product_ID, -it.amount)
}) })
return super.init() return super.init()

14
orders/test/model.cds Normal file
View File

@@ -0,0 +1,14 @@
service WithDraft {
@odata.draft.enabled
entity Boo as projection on Foo;
}
service WithoutDraft {
entity Boo as projection on Foo;
}
entity Foo {
key ID : UUID;
bar : Composition of many {
key pos : Integer; //> meant to be a local key only
}
}

View File

@@ -0,0 +1,142 @@
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml">
<edmx:Include Alias="Common" Namespace="com.sap.vocabularies.Common.v1"/>
</edmx:Reference>
<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1"/>
</edmx:Reference>
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml">
<edmx:Include Alias="UI" Namespace="com.sap.vocabularies.UI.v1"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="WithDraft" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer">
<EntitySet Name="Boo" EntityType="WithDraft.Boo">
<NavigationPropertyBinding Path="SiblingEntity" Target="Boo"/>
</EntitySet>
</EntityContainer>
<EntityType Name="Boo">
<Key>
<PropertyRef Name="ID"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="bar" Type="Collection(WithDraft.Foo_bar)" Partner="up_" ContainsTarget="true"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="WithDraft.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="WithDraft.Boo"/>
</EntityType>
<EntityType Name="DraftAdministrativeData">
<Key>
<PropertyRef Name="DraftUUID"/>
</Key>
<Property Name="DraftUUID" Type="Edm.Guid" Nullable="false"/>
<Property Name="CreationDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="CreatedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsCreatedByMe" Type="Edm.Boolean"/>
<Property Name="LastChangeDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="LastChangedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="InProcessByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsProcessedByMe" Type="Edm.Boolean"/>
</EntityType>
<EntityType Name="Foo_bar">
<Key>
<PropertyRef Name="pos"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<NavigationProperty Name="up_" Type="WithDraft.Boo" Nullable="false" Partner="bar"/>
<Property Name="pos" Type="Edm.Int32" Nullable="false"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="WithDraft.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="WithDraft.Foo_bar"/>
</EntityType>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Foo_bar"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="WithDraft.Foo_bar"/>
</Action>
<Action Name="draftActivate" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Action Name="draftEdit" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<Parameter Name="PreserveChanges" Type="Edm.Boolean"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Annotations Target="WithDraft.EntityContainer/Boo">
<Annotation Term="Common.DraftRoot">
<Record Type="Common.DraftRootType">
<PropertyValue Property="ActivationAction" String="WithDraft.draftActivate"/>
<PropertyValue Property="EditAction" String="WithDraft.draftEdit"/>
<PropertyValue Property="PreparationAction" String="WithDraft.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="WithDraft.Boo/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftAdministrativeData}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftUUID">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftUUID}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/CreationDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_CreationDateTime}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/CreatedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_CreatedByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftIsCreatedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsCreatedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/LastChangeDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangeDateTime}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/LastChangedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangedByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/InProcessByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_InProcessByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftIsProcessedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsProcessedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@@ -0,0 +1,24 @@
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="WithoutDraft" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer">
<EntitySet Name="Boo" EntityType="WithoutDraft.Boo"/>
</EntityContainer>
<EntityType Name="Boo">
<Key>
<PropertyRef Name="ID"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="bar" Type="Collection(WithoutDraft.Foo_bar)" Partner="up_" ContainsTarget="true"/>
</EntityType>
<EntityType Name="Foo_bar">
<Key>
<PropertyRef Name="pos"/>
</Key>
<NavigationProperty Name="up_" Type="WithoutDraft.Boo" Nullable="false" Partner="bar"/>
<Property Name="pos" Type="Edm.Int32" Nullable="false"/>
</EntityType>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@@ -0,0 +1,159 @@
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml">
<edmx:Include Alias="Common" Namespace="com.sap.vocabularies.Common.v1"/>
</edmx:Reference>
<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1"/>
</edmx:Reference>
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml">
<edmx:Include Alias="UI" Namespace="com.sap.vocabularies.UI.v1"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="WithDraft" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer">
<EntitySet Name="Boo" EntityType="WithDraft.Boo">
<NavigationPropertyBinding Path="bar" Target="Foo_bar"/>
<NavigationPropertyBinding Path="SiblingEntity" Target="Boo"/>
</EntitySet>
<EntitySet Name="Foo_bar" EntityType="WithDraft.Foo_bar">
<NavigationPropertyBinding Path="up_" Target="Boo"/>
<NavigationPropertyBinding Path="SiblingEntity" Target="Foo_bar"/>
</EntitySet>
</EntityContainer>
<EntityType Name="Boo">
<Key>
<PropertyRef Name="ID"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="bar" Type="Collection(WithDraft.Foo_bar)" Partner="up_">
<OnDelete Action="Cascade"/>
</NavigationProperty>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="WithDraft.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="WithDraft.Boo"/>
</EntityType>
<EntityType Name="DraftAdministrativeData">
<Key>
<PropertyRef Name="DraftUUID"/>
</Key>
<Property Name="DraftUUID" Type="Edm.Guid" Nullable="false"/>
<Property Name="CreationDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="CreatedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsCreatedByMe" Type="Edm.Boolean"/>
<Property Name="LastChangeDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="LastChangedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="InProcessByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsProcessedByMe" Type="Edm.Boolean"/>
</EntityType>
<EntityType Name="Foo_bar">
<Key>
<PropertyRef Name="pos"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="up__ID" Type="Edm.Guid"/>
<NavigationProperty Name="up_" Type="WithDraft.Boo" Nullable="false" Partner="bar">
<ReferentialConstraint Property="up__ID" ReferencedProperty="ID"/>
</NavigationProperty>
<Property Name="pos" Type="Edm.Int32" Nullable="false"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="WithDraft.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="WithDraft.Foo_bar"/>
</EntityType>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Foo_bar"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="WithDraft.Foo_bar"/>
</Action>
<Action Name="draftActivate" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Action Name="draftEdit" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<Parameter Name="PreserveChanges" Type="Edm.Boolean"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Annotations Target="WithDraft.EntityContainer/Boo">
<Annotation Term="Common.DraftRoot">
<Record Type="Common.DraftRootType">
<PropertyValue Property="ActivationAction" String="WithDraft.draftActivate"/>
<PropertyValue Property="EditAction" String="WithDraft.draftEdit"/>
<PropertyValue Property="PreparationAction" String="WithDraft.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="WithDraft.Boo/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftAdministrativeData}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftUUID">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftUUID}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/CreationDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_CreationDateTime}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/CreatedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_CreatedByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftIsCreatedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsCreatedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/LastChangeDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangeDateTime}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/LastChangedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangedByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/InProcessByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_InProcessByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftIsProcessedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsProcessedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.EntityContainer/Foo_bar">
<Annotation Term="Common.DraftNode">
<Record Type="Common.DraftNodeType">
<PropertyValue Property="PreparationAction" String="WithDraft.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="WithoutDraft" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer">
<EntitySet Name="Boo" EntityType="WithoutDraft.Boo">
<NavigationPropertyBinding Path="bar" Target="Foo_bar"/>
</EntitySet>
<EntitySet Name="Foo_bar" EntityType="WithoutDraft.Foo_bar">
<NavigationPropertyBinding Path="up_" Target="Boo"/>
</EntitySet>
</EntityContainer>
<EntityType Name="Boo">
<Key>
<PropertyRef Name="ID"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="bar" Type="Collection(WithoutDraft.Foo_bar)" Partner="up_">
<OnDelete Action="Cascade"/>
</NavigationProperty>
</EntityType>
<EntityType Name="Foo_bar">
<Key>
<PropertyRef Name="up__ID"/>
<PropertyRef Name="pos"/>
</Key>
<Property Name="up__ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="up_" Type="WithoutDraft.Boo" Nullable="false" Partner="bar">
<ReferentialConstraint Property="up__ID" ReferencedProperty="ID"/>
</NavigationProperty>
<Property Name="pos" Type="Edm.Int32" Nullable="false"/>
</EntityType>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@@ -0,0 +1,160 @@
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/Common.xml">
<edmx:Include Alias="Common" Namespace="com.sap.vocabularies.Common.v1"/>
</edmx:Reference>
<edmx:Reference Uri="https://oasis-tcs.github.io/odata-vocabularies/vocabularies/Org.OData.Core.V1.xml">
<edmx:Include Alias="Core" Namespace="Org.OData.Core.V1"/>
</edmx:Reference>
<edmx:Reference Uri="https://sap.github.io/odata-vocabularies/vocabularies/UI.xml">
<edmx:Include Alias="UI" Namespace="com.sap.vocabularies.UI.v1"/>
</edmx:Reference>
<edmx:DataServices>
<Schema Namespace="WithDraft" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer">
<EntitySet Name="Boo" EntityType="WithDraft.Boo">
<NavigationPropertyBinding Path="bar" Target="Foo_bar"/>
<NavigationPropertyBinding Path="SiblingEntity" Target="Boo"/>
</EntitySet>
<EntitySet Name="Foo_bar" EntityType="WithDraft.Foo_bar">
<NavigationPropertyBinding Path="up_" Target="Boo"/>
<NavigationPropertyBinding Path="SiblingEntity" Target="Foo_bar"/>
</EntitySet>
</EntityContainer>
<EntityType Name="Boo">
<Key>
<PropertyRef Name="ID"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="bar" Type="Collection(WithDraft.Foo_bar)" Partner="up_">
<OnDelete Action="Cascade"/>
</NavigationProperty>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="WithDraft.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="WithDraft.Boo"/>
</EntityType>
<EntityType Name="DraftAdministrativeData">
<Key>
<PropertyRef Name="DraftUUID"/>
</Key>
<Property Name="DraftUUID" Type="Edm.Guid" Nullable="false"/>
<Property Name="CreationDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="CreatedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsCreatedByMe" Type="Edm.Boolean"/>
<Property Name="LastChangeDateTime" Type="Edm.DateTimeOffset" Precision="7"/>
<Property Name="LastChangedByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="InProcessByUser" Type="Edm.String" MaxLength="256"/>
<Property Name="DraftIsProcessedByMe" Type="Edm.Boolean"/>
</EntityType>
<EntityType Name="Foo_bar">
<Key>
<PropertyRef Name="up__ID"/>
<PropertyRef Name="pos"/>
<PropertyRef Name="IsActiveEntity"/>
</Key>
<Property Name="up__ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="up_" Type="WithDraft.Boo" Nullable="false" Partner="bar">
<ReferentialConstraint Property="up__ID" ReferencedProperty="ID"/>
</NavigationProperty>
<Property Name="pos" Type="Edm.Int32" Nullable="false"/>
<Property Name="IsActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasActiveEntity" Type="Edm.Boolean" Nullable="false"/>
<Property Name="HasDraftEntity" Type="Edm.Boolean" Nullable="false"/>
<NavigationProperty Name="DraftAdministrativeData" Type="WithDraft.DraftAdministrativeData" ContainsTarget="true"/>
<NavigationProperty Name="SiblingEntity" Type="WithDraft.Foo_bar"/>
</EntityType>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Action Name="draftPrepare" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Foo_bar"/>
<Parameter Name="SideEffectsQualifier" Type="Edm.String"/>
<ReturnType Type="WithDraft.Foo_bar"/>
</Action>
<Action Name="draftActivate" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Action Name="draftEdit" IsBound="true" EntitySetPath="in">
<Parameter Name="in" Type="WithDraft.Boo"/>
<Parameter Name="PreserveChanges" Type="Edm.Boolean"/>
<ReturnType Type="WithDraft.Boo"/>
</Action>
<Annotations Target="WithDraft.EntityContainer/Boo">
<Annotation Term="Common.DraftRoot">
<Record Type="Common.DraftRootType">
<PropertyValue Property="ActivationAction" String="WithDraft.draftActivate"/>
<PropertyValue Property="EditAction" String="WithDraft.draftEdit"/>
<PropertyValue Property="PreparationAction" String="WithDraft.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="WithDraft.Boo/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Boo/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftAdministrativeData}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftUUID">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftUUID}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/CreationDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_CreationDateTime}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/CreatedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_CreatedByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftIsCreatedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsCreatedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/LastChangeDateTime">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangeDateTime}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/LastChangedByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_LastChangedByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/InProcessByUser">
<Annotation Term="Common.Label" String="{i18n>Draft_InProcessByUser}"/>
</Annotations>
<Annotations Target="WithDraft.DraftAdministrativeData/DraftIsProcessedByMe">
<Annotation Term="Common.Label" String="{i18n>Draft_DraftIsProcessedByMe}"/>
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.EntityContainer/Foo_bar">
<Annotation Term="Common.DraftNode">
<Record Type="Common.DraftNodeType">
<PropertyValue Property="PreparationAction" String="WithDraft.draftPrepare"/>
</Record>
</Annotation>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/IsActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/HasActiveEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/HasDraftEntity">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
<Annotations Target="WithDraft.Foo_bar/DraftAdministrativeData">
<Annotation Term="UI.Hidden" Bool="true"/>
</Annotations>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="utf-8"?>
<edmx:Edmx Version="4.0" xmlns:edmx="http://docs.oasis-open.org/odata/ns/edmx">
<edmx:DataServices>
<Schema Namespace="WithoutDraft" xmlns="http://docs.oasis-open.org/odata/ns/edm">
<EntityContainer Name="EntityContainer">
<EntitySet Name="Boo" EntityType="WithoutDraft.Boo">
<NavigationPropertyBinding Path="bar" Target="Foo_bar"/>
</EntitySet>
<EntitySet Name="Foo_bar" EntityType="WithoutDraft.Foo_bar">
<NavigationPropertyBinding Path="up_" Target="Boo"/>
</EntitySet>
</EntityContainer>
<EntityType Name="Boo">
<Key>
<PropertyRef Name="ID"/>
</Key>
<Property Name="ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="bar" Type="Collection(WithoutDraft.Foo_bar)" Partner="up_">
<OnDelete Action="Cascade"/>
</NavigationProperty>
</EntityType>
<EntityType Name="Foo_bar">
<Key>
<PropertyRef Name="up__ID"/>
<PropertyRef Name="pos"/>
</Key>
<Property Name="up__ID" Type="Edm.Guid" Nullable="false"/>
<NavigationProperty Name="up_" Type="WithoutDraft.Boo" Nullable="false" Partner="bar">
<ReferentialConstraint Property="up__ID" ReferencedProperty="ID"/>
</NavigationProperty>
<Property Name="pos" Type="Edm.Int32" Nullable="false"/>
</EntityType>
</Schema>
</edmx:DataServices>
</edmx:Edmx>

View File

@@ -11,18 +11,16 @@
"@capire/hello": "./hello", "@capire/hello": "./hello",
"@capire/media": "./media", "@capire/media": "./media",
"@capire/orders": "./orders", "@capire/orders": "./orders",
"@capire/reviews": "./reviews", "@capire/reviews": "./reviews"
"@capire/suppliers": "./suppliers"
}, },
"devDependencies": { "devDependencies": {
"chai": "^4.2.0", "chai": "^4.2.0",
"chai-as-promised": "^7.1.1", "chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0", "chai-subset": "^1.6.0",
"sqlite3": "5.0.0" "sqlite3": "^5"
}, },
"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", "registry": "cd .registry && node server.js",
"registry": "node .registry/server.js",
"bookshop": "cds watch bookshop", "bookshop": "cds watch bookshop",
"fiori": "cds watch fiori", "fiori": "cds watch fiori",
"media": "cds watch media", "media": "cds watch media",

View File

@@ -37,7 +37,7 @@ const reviews = new Vue ({
reviews.message = {} reviews.message = {}
}, },
newReview () { async newReview () {
reviews.review = {} reviews.review = {}
reviews.message = {} reviews.message = {}
setTimeout (()=> $('form > input').focus(), 111) setTimeout (()=> $('form > input').focus(), 111)

View File

@@ -3,22 +3,19 @@
## Run all-in-one ## Run all-in-one
Open a terminal window and run the bookshop in it: Open a terminal window and run the bookshop in it:
```sh ```sh
npm run bookshop npm run bookshop
``` ```
## Run as Separate Services ## Run as separate services
Open two terminal windows. In the first one start the reviews service stand-alone:
Open two terminal windows, and in the first one start the reviews service stand-alone:
```sh ```sh
npm run reviews-service npm run reviews-service
``` ```
In the second one start the bookshop: In the the second one start the bookshop:
```sh ```sh
npm run bookshop npm run bookshop
``` ```

View File

@@ -8,9 +8,9 @@ service ReviewsService {
action unlike (review: type of Reviews:ID); action unlike (review: type of Reviews:ID);
// Async API // Async API
event reviewed : projection on Reviews { event reviewed : {
subject, subject: type of Reviews:subject;
rating rating: Decimal(2,1)
} }
// Input validation // Input validation

View File

@@ -1,5 +1,5 @@
const cds = require ('@sap/cds') const cds = require ('@sap/cds')
module.exports = cds.service.impl (function(){ module.exports = cds.service.impl (async function(){
// Get the CSN definition for Reviews from the db schema for sub-sequent queries // Get the CSN definition for Reviews from the db schema for sub-sequent queries
// ( Note: we explicitly specify the namespace to support embedded reuse ) // ( Note: we explicitly specify the namespace to support embedded reuse )
@@ -16,7 +16,7 @@ module.exports = cds.service.impl (function(){
SELECT.one (['round(avg(rating),2) as rating']) .from (Reviews) .where ({subject}) SELECT.one (['round(avg(rating),2) as rating']) .from (Reviews) .where ({subject})
) )
global.it || console.log ('< emitting:', 'reviewed', { subject, rating }) global.it || console.log ('< emitting:', 'reviewed', { subject, rating })
await this.emit ('reviewed', { subject, rating }) this.emit ('reviewed', { subject, rating })
}) })
// Increment counter for reviews considered helpful // Increment counter for reviews considered helpful

View File

@@ -1,7 +1,7 @@
# Overview of Samples # Overview of Samples
The following list gives an overview of the samples provided in subdirectories. The list below gives an overview of the samples provided in subdirectories.
Each sub directory essentially is an individual npm package arranged in an [all-in-one monorepo](all-in-one-monorepo) umbrella setup. Each sub directory essentially is a individual npm package arranged in an [all-in-one monorepo](all-in-one-monorepo) umbrella setup.
## [@capire/hello-world](hello) ## [@capire/hello-world](hello)
@@ -13,7 +13,7 @@ Each sub directory essentially is an individual npm package arranged in an [all-
- [Getting Started](https://cap.cloud.sap/docs/get-started/in-a-nutshell) with CAP, briefly introducing: - [Getting Started](https://cap.cloud.sap/docs/get-started/in-a-nutshell) with CAP, briefly introducing:
- [Project Setup](https://cap.cloud.sap/docs/get-started/) and [Layouts](https://cap.cloud.sap/docs/get-started/projects) - [Project Setup](https://cap.cloud.sap/docs/get-started/) and [Layouts](https://cap.cloud.sap/docs/get-started/projects)
- [Domain Modeling](https://cap.cloud.sap/docs/guides/domain-models) - [Domain Modelling](https://cap.cloud.sap/docs/guides/domain-models)
- [Defining Services](https://cap.cloud.sap/docs/guides/providing-services) - [Defining Services](https://cap.cloud.sap/docs/guides/providing-services)
- [Generic Providers](https://cap.cloud.sap/docs/guides/generic-providers) - [Generic Providers](https://cap.cloud.sap/docs/guides/generic-providers)
- [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) - [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl)
@@ -22,7 +22,7 @@ Each sub directory essentially is an individual npm package arranged in an [all-
## [@capire/common](common) ## [@capire/common](common)
- Showcases how to extend [@sap/cds/common](https://cap.cloud.sap/docs/cds/common) thereby covering: - Showcases how to extend [@sap/cds/common](https://cap.cloud.sap/docs/cds/common) thereby covering...
- Building [extension packages](https://cap.cloud.sap/docs/guides/domain-models#aspects-extensibility) - Building [extension packages](https://cap.cloud.sap/docs/guides/domain-models#aspects-extensibility)
- Providing [reuse packages](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) - Providing [reuse packages](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content)
- [Verticalization](https://cap.cloud.sap/docs/cds/common#adapting-to-your-needs) - [Verticalization](https://cap.cloud.sap/docs/cds/common#adapting-to-your-needs)
@@ -32,46 +32,39 @@ Each sub directory essentially is an individual npm package arranged in an [all-
## [@capire/orders](orders) ## [@capire/orders](orders)
- A standalone orders management service, demonstrating: - A standalone orders mgmt service, demonstrating...
- Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with - Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with
- [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data) - [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data)
## [@capire/reviews](reviews) ## [@capire/reviews](reviews)
- Shows how to implement a modular service to manage product reviews, including: - Shows how to implement a modular service to manage product reviews, including...
- Consuming other services synchronously and asynchronously - Consuming other services synchronously and asynchronously
- Serving requests synchronously - Serving requests synchronously
- Emitting events asynchronously - Emitting events asynchronously
- Grow as you go, with: - Grow as you go, with...
- Mocking app services - Mocking app services
- Running service meshes - Running service meshes
- Late-cut Micro Services - Late-cut Micro Services
- As well as managed data, input validations, and authorization - As well as managed data, input validations and authorization
## [@capire/suppliers](suppliers)
- Shows how to integrate remote services, in this case the BusinessPartner service from SAP S/4HANA.
- Extending [@capire/bookshop](bookshop) with suppliers from SAP S/4HANA
- Providing that as a pre-built integration & extension package
- Used in [@capire/fiori](fiori)
## [@capire/fiori](fiori) ## [@capire/fiori](fiori)
- A [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages: - A [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages:
- [@capire/bookshop](bookshop) - [@capire/bookshop](bookshop)
- [@capire/common](common)
- [@capire/orders](orders)
- [@capire/reviews](reviews) - [@capire/reviews](reviews)
- [@capire/suppliers](suppliers) - [@capire/orders](orders)
- [Adds an SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to: - [@capire/common](common)
- [Adds a Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to...
- [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files - [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files
- Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft) - Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)
- Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help) - Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)
- Serving SAP Fiori apps locally - Serving Fiori apps locally
- [The Vue.js app](bookshop/app/vue) imported from bookshop is served as well - [The Vue.js app](bookshop/app/vue) imported from bookshop is served as well
<br> <br>
# All-in-one Monorepo # All-in-one Monorepo

View File

@@ -1 +0,0 @@
using from './srv/mashup';

View File

@@ -1,24 +0,0 @@
{
"name": "@capire/suppliers",
"version": "1.0.0",
"description": "Shows integration with SAP S/4HANA, in turn provided as a reusable extension package to bookshop.",
"private": true,
"dependencies": {
"@capire/common": "*",
"@sap/cds": "^4",
"express": "^4"
},
"scripts": {
"start": "cds run --in-memory?",
"watch": "cds watch",
"mocked-s4": "cds mock API_BUSINESS_PARTNER"
},
"cds": {
"requires": {
"API_BUSINESS_PARTNER": {
"kind": "odata",
"model": "srv/external/API_BUSINESS_PARTNER"
}
}
}
}

View File

@@ -1,3 +0,0 @@
const cds = require ('@sap/cds')
cds.once('served', require('./srv/mashup'))
module.exports = cds.server

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,7 +0,0 @@
BusinessPartner;BusinessPartnerFullName
ACME;A Company Making Everything
B4U;Books for You
S&C;Shakespeare & Co.
WSL;Waterstones
TLD;Thalia
PNG;Penguin Books
1 BusinessPartner BusinessPartnerFullName
2 ACME A Company Making Everything
3 B4U Books for You
4 S&C Shakespeare & Co.
5 WSL Waterstones
6 TLD Thalia
7 PNG Penguin Books

View File

@@ -1,5 +0,0 @@
ID;name
ACME;A Company Making Everything
B4U;Books for You
S&C;Shakespeare & Co.
WSL;Waterstones
1 ID name
2 ACME A Company Making Everything
3 B4U Books for You
4 S&C Shakespeare & Co.
5 WSL Waterstones

View File

@@ -1,25 +0,0 @@
/*
Optionally add projections to external entities, to capture what
you actually want to use from there.
*/
using {API_BUSINESS_PARTNER as S4} from './external/API_BUSINESS_PARTNER.csn';
@cds.autoexpose // or expose explicitly in Catalog and AdminService
@cds.persistence: {table,skip:false}
entity sap.capire.bookshop.Suppliers as projection on S4.A_BusinessPartner {
key BusinessPartner as ID, BusinessPartnerFullName as name
}
using { sap.capire.bookshop.Books, CatalogService } from '@capire/bookshop';
extend Books with {
supplier: Association to sap.capire.bookshop.Suppliers;
}
extend service AdminService with { // why is AdminService visible?
entity Suppliers as projection on sap.capire.bookshop.Suppliers;
}
extend projection CatalogService.ListOfBooks with {
supplier
}

View File

@@ -1,57 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Mashing up provided and required services...
//
module.exports = async()=>{ // called by server.js
if (!cds.services.AdminService) return //> mocking S4 service only
// Connect to services we want to mashup below...
const S4bupa = await cds.connect.to('API_BUSINESS_PARTNER') //> external S4 service
const admin = await cds.connect.to('AdminService') //> local domain service
const db = await cds.connect.to('db') //> our primary database
// Reflect CDS definition of the Suppliers entity
const { Suppliers } = S4bupa.entities
admin.prepend (()=>{ //> to ensure our .on handlers below go before the default ones
// Delegate Value Help reads for Suppliers to S4 backend
admin.on ('READ', 'Suppliers', req => {
console.log ('>> delegating to S4 service...')
return S4bupa.run(req.query)
})
// Replicate Supplier data when edited Books have suppliers
admin.on (['CREATE','UPDATE'], 'Books', ({data:{supplier}}, next) => {
// Using Promise.all(...) to parallelize local write, i.e. next(), and replication
if (supplier) return Promise.all ([ next(), async()=>{
let replicated = await db.exists (Suppliers, supplier)
if (!replicated) await replicate (supplier, 'initial')
}])
else return next() //> don't forget to pass down the interceptor stack
})
})
// Subscribe to changes in the S4 origin of Suppliers data
S4bupa.on ('BusinessPartners/Changed', async msg => { //> would be great if we had batch events from S/4
let replicas = await SELECT('ID').from (Suppliers) .where ('ID in', msg.businessPartners)
return replicate (replicas.map(each => each.ID))
})
/**
* Helper function to replicate Suppliers data.
* @param {string|string[]} IDs a single ID or an array of IDs
* @param {truthy|falsy} _initial indicates whether an insert or an update is required
*/
async function replicate (IDs,_initial) {
if (!Array.isArray(IDs)) IDs = [ IDs ]
let suppliers = await S4bupa.read (Suppliers).where('ID in',IDs)
if (_initial) return db.insert (suppliers) .into (Suppliers) //> using bulk insert
else return Promise.all(suppliers.map ( //> parallelizing updates
each => db.update (Suppliers,each.ID) .with (each)
))
}
}

View File

@@ -1,5 +1,5 @@
const { expect } = require('../test')
const cds = require('@sap/cds/lib') const cds = require('@sap/cds/lib')
const { expect } = cds.test
const CQL = ([cql]) => cds.parse.cql(cql) const CQL = ([cql]) => cds.parse.cql(cql)
const Foo = { name: 'Foo' } const Foo = { name: 'Foo' }
const Books = { name: 'capire.bookshop.Books' } const Books = { name: 'capire.bookshop.Books' }
@@ -325,26 +325,7 @@ describe('cds.ql → cqn', () => {
}) })
// using CQL fragments -> uses cds.parse.expr // using CQL fragments -> uses cds.parse.expr
const is_v2 = !!cds.parse.expr('(1,2)').list expect((cqn = CQL`SELECT from Foo where ID=11 and x in ( foo, 'bar', 3)`)).to.eql({
if (is_v2) expect((cqn = CQL`SELECT from Foo where ID=11 and x in ( foo, 'bar', 3)`)).to.eql({
SELECT: {
from: { ref: ['Foo'] },
where: [
{ ref: ['ID'] },
'=',
{ val: ID },
'and',
{ ref: ['x'] },
'in',
{list:[
{ ref: ['foo'] },
{ val: 'bar' },
{ val: 3 },
]}
],
},
})
else expect((cqn = CQL`SELECT from Foo where ID=11 and x in ( foo, 'bar', 3)`)).to.eql({
SELECT: { SELECT: {
from: { ref: ['Foo'] }, from: { ref: ['Foo'] },
where: [ where: [
@@ -365,7 +346,7 @@ describe('cds.ql → cqn', () => {
}, },
}) })
if (!is_v2) expect( expect(
SELECT.from(Foo).where(`x=`, 1, `or y.z is null and (a>`, 2, `or b=`, 3, `)`) SELECT.from(Foo).where(`x=`, 1, `or y.z is null and (a>`, 2, `or b=`, 3, `)`)
).to.eql(CQL`SELECT from Foo where x=1 or y.z is null and (a>2 or b=3)`) ).to.eql(CQL`SELECT from Foo where x=1 or y.z is null and (a>2 or b=3)`)

View File

@@ -1,7 +1,7 @@
const { expect } = require('../test') .run (
'serve', 'AdminService', '--from', '@capire/bookshop,@capire/common', '--in-memory'
)
const cds = require('@sap/cds/lib') const cds = require('@sap/cds/lib')
const { expect } = cds.test (
'serve', 'AdminService', '--from', '@capire/bookshop,@capire/common', '--in-memory'
).in(__dirname)
describe('Consuming Services locally', () => { describe('Consuming Services locally', () => {
// //

View File

@@ -1,7 +1,5 @@
const { GET, POST, expect } = require('../test') .run ('bookshop') const cds = require('@sap/cds/lib'); cds.User = cds.User.Privileged // skip auth
const cds = require('@sap/cds/lib') const { GET, POST, expect } = cds.test('bookshop').in(__dirname,'..')
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
describe('Custom Handlers', () => { describe('Custom Handlers', () => {

View File

@@ -1,4 +1,5 @@
const { GET, expect } = require('../test') .run ('serve','hello/world.cds') const cds = require('@sap/cds/lib')
const { GET, expect } = cds.test('serve','hello/world.cds').in(__dirname,'..')
describe('Hello world!', () => { describe('Hello world!', () => {

View File

@@ -1,5 +1,6 @@
const {expect} = require('../test') const cwd = process.cwd(); process.chdir (__dirname) //> only for internal CI/CD@SAP
const cds = require('@sap/cds/lib') const cds = require('@sap/cds/lib')
const {expect} = cds.test
// monkey patching older releases: // monkey patching older releases:
if (!cds.compile.cdl) cds.compile.cdl = cds.parse if (!cds.compile.cdl) cds.compile.cdl = cds.parse
@@ -24,6 +25,8 @@ describe('Hierarchical Data', ()=>{
expect (cds.db.model) .to.exist expect (cds.db.model) .to.exist
}) })
after(()=> process.chdir(cwd))
it ('supports deeply nested inserts', ()=> INSERT.into (Cats, it ('supports deeply nested inserts', ()=> INSERT.into (Cats,
{ ID:100, name:'Some Cats...', children:[ { ID:100, name:'Some Cats...', children:[
{ ID:101, name:'Cat', children:[ { ID:101, name:'Cat', children:[

View File

@@ -1,6 +0,0 @@
const test = require('@sap/cds/lib/utils/tests').in(__dirname,'..')
module.exports = Object.assign(test,{run:test})
// REVISIT: With upcoming release of @sap/cds this should become:
// module.exports = require('@sap/cds/tests').in(__dirname,'..')

View File

@@ -1,7 +1,5 @@
const { GET, expect } = require('../test') .run ('serve', 'test/localized-data.cds', '--in-memory') const cds = require('@sap/cds/lib'); cds.User = cds.User.Privileged // skip auth
const cds = require('@sap/cds/lib') const { GET, expect } = cds.test ('serve', __dirname+'/localized-data.cds', '--in-memory')
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
describe('Localized Data', () => { describe('Localized Data', () => {

View File

@@ -1,11 +1,13 @@
const { expect } = require('../test')
const cds = require('@sap/cds/lib') const cds = require('@sap/cds/lib')
const cwd = process.cwd(); process.chdir (__dirname) //> only for internal CI/CD@SAP
const {expect} = cds.test
const _model = '@capire/reviews' const _model = '@capire/reviews'
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch cds.User = cds.User.Privileged // hard core monkey patch
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
describe('Messaging', ()=>{ describe('Messaging', ()=>{
after(()=> process.chdir(cwd))
it ('should bootstrap sqlite in-memory db', async()=>{ it ('should bootstrap sqlite in-memory db', async()=>{
const db = await cds.deploy (_model) .to ('sqlite::memory:') const db = await cds.deploy (_model) .to ('sqlite::memory:')
await db.delete('Reviews') await db.delete('Reviews')
@@ -42,16 +44,16 @@ describe('Messaging', ()=>{
// { ID: 111 + (++N), subject: "201", title: "Captivating", rating: N }, // { ID: 111 + (++N), subject: "201", title: "Captivating", rating: N },
// ), // ),
srv.create ('Reviews') .entries ( srv.create ('Reviews') .entries (
{ ID: String(111 + (++N)), subject: "201", title: "Captivating", rating: N } { ID: 111 + (++N), subject: "201", title: "Captivating", rating: N }
), ),
srv.create ('Reviews') .entries ( srv.create ('Reviews') .entries (
{ ID: String(111 + (++N)), subject: "201", title: "Captivating", rating: N } { ID: 111 + (++N), subject: "201", title: "Captivating", rating: N }
), ),
srv.create ('Reviews') .entries ( srv.create ('Reviews') .entries (
{ ID: String(111 + (++N)), subject: "201", title: "Captivating", rating: N } { ID: 111 + (++N), subject: "201", title: "Captivating", rating: N }
), ),
srv.create ('Reviews') .entries ( srv.create ('Reviews') .entries (
{ ID: String(111 + (++N)), subject: "201", title: "Captivating", rating: N } { ID: 111 + (++N), subject: "201", title: "Captivating", rating: N }
), ),
])) ]))

View File

@@ -1,11 +1,8 @@
const { GET, expect } = require('../test') .run ('bookshop') const cds = require('@sap/cds/lib'); cds.User = cds.User.Privileged // skip auth
const cds = require('@sap/cds/lib') const { GET, expect } = cds.test('bookshop').in(__dirname,'..')
if (cds.User.default) cds.User.default = cds.User.Privileged // hard core monkey patch
else cds.User = cds.User.Privileged // hard core monkey patch for older cds releases
describe('OData Protocol', () => { describe('OData Protocol', () => {
it('serves $metadata documents in v4', async () => { it('serves $metadata documents in v4', async () => {
const { headers, status, data } = await GET `/browse/$metadata` const { headers, status, data } = await GET `/browse/$metadata`
expect(status).to.equal(200) expect(status).to.equal(200)