Compare commits
4 Commits
hierarch-d
...
ord-servic
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
7ccd11062a | ||
|
|
20348e0776 | ||
|
|
6acd5338d9 | ||
|
|
413d4de745 |
2425
.deploy/app-router/package-lock.json
generated
2425
.deploy/app-router/package-lock.json
generated
File diff suppressed because it is too large
Load Diff
@@ -1,9 +0,0 @@
|
||||
{
|
||||
"name": "approuter",
|
||||
"dependencies": {
|
||||
"@sap/approuter": "^20.0.0"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "node node_modules/@sap/approuter/approuter.js"
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
../../../bookshop/app/vue
|
||||
@@ -1 +0,0 @@
|
||||
../../../orders/app/orders
|
||||
@@ -1 +0,0 @@
|
||||
../../../reviews/app/vue
|
||||
@@ -1,41 +0,0 @@
|
||||
{
|
||||
"welcomeFile": "app/bookshop/index.html",
|
||||
"routes": [
|
||||
{
|
||||
"source": "^/app/(.*)$",
|
||||
"target": "$1",
|
||||
"localDir": "resources",
|
||||
"cacheControl": "no-cache, no-store, must-revalidate"
|
||||
},
|
||||
{
|
||||
"source": "^/admin/(.*)$",
|
||||
"target": "/admin/$1",
|
||||
"destination": "bookstore-api",
|
||||
"csrfProtection": true
|
||||
},
|
||||
{
|
||||
"source": "^/browse/(.*)$",
|
||||
"target": "/browse/$1",
|
||||
"destination": "bookstore-api",
|
||||
"csrfProtection": true
|
||||
},
|
||||
{
|
||||
"source": "^/user/(.*)$",
|
||||
"target": "/user/$1",
|
||||
"destination": "bookstore-api",
|
||||
"csrfProtection": true
|
||||
},
|
||||
{
|
||||
"source": "^/odata/v4/orders/(.*)$",
|
||||
"target": "/odata/v4/orders/$1",
|
||||
"destination": "orders-api",
|
||||
"csrfProtection": true
|
||||
},
|
||||
{
|
||||
"source": "^/reviews/(.*)$",
|
||||
"target": "/reviews/$1",
|
||||
"destination": "reviews-api",
|
||||
"csrfProtection": true
|
||||
}
|
||||
]
|
||||
}
|
||||
3
.github/dependabot.yml
vendored
3
.github/dependabot.yml
vendored
@@ -18,6 +18,3 @@ updates:
|
||||
- dependency-name: "chai-as-promised"
|
||||
# chai-as-promised 8 doesn't work atm w/ cds.test, TODO fix that in cds.test
|
||||
versions: ["8.x"]
|
||||
- dependency-name: "express"
|
||||
# express 5 not supported atm
|
||||
versions: ["5.x"]
|
||||
|
||||
2
.github/workflows/node.js.yml
vendored
2
.github/workflows/node.js.yml
vendored
@@ -15,7 +15,7 @@ jobs:
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [22.x, 20.x]
|
||||
node-version: [22.x, 20.x, 18.x]
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
3
.npmrc
Normal file
3
.npmrc
Normal file
@@ -0,0 +1,3 @@
|
||||
# Ensure we always use public packages, i.e. avoid using local registries from ~/.npmrc
|
||||
@sap:registry=https://registry.npmjs.org/
|
||||
registry=https://registry.npmjs.org/
|
||||
@@ -24,6 +24,6 @@ Disclaimer: The code in this project may include calls to APIs (“API Calls”)
|
||||
you any rights to use or access any SAP External Product, or provide any third
|
||||
parties the right to use of access any SAP External Product, through API Calls.
|
||||
|
||||
Files: *.*
|
||||
Copyright: 2019-2025 SAP SE or an SAP affiliate company and cap-cloud-samples
|
||||
Files: *
|
||||
Copyright: 2019-2020 SAP SE or an SAP affiliate company and cap-cloud-samples
|
||||
License: Apache-2.0
|
||||
|
||||
117
.tours/db-native.tour
Normal file
117
.tours/db-native.tour
Normal file
@@ -0,0 +1,117 @@
|
||||
{
|
||||
"$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.requires` section in `package.json` is a place to configure which of the `db/sqlite` and `db/hana` folders are used for which database.\n\nWe use [Node.js profiles](https://cap.cloud.sap/docs/node.js/cds-env#profiles) to separate the configuration.\nIn the `development` profile, you can see that `db/sqlite` is set as the model, while the `db/hana` folder is configured in the `production` profile. `db-ext` is a pseudo datasource, its name doesn't matter.\n\nSee [`cds.resolve`](https://cap.cloud.sap/docs/node.js/cds-compile#cds-resolve) to learn more about how models are found.",
|
||||
"selection": {
|
||||
"start": {
|
||||
"line": 41,
|
||||
"character": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 48,
|
||||
"character": 1
|
||||
}
|
||||
},
|
||||
"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": 43,
|
||||
"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": 46,
|
||||
"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": 72,
|
||||
"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."
|
||||
}
|
||||
]
|
||||
}
|
||||
139
.tours/samples.tour
Normal file
139
.tours/samples.tour
Normal file
@@ -0,0 +1,139 @@
|
||||
{
|
||||
"$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/srv/world.cds",
|
||||
"description": "### Hello World!\n\nThis is a simplistic [Hello World](https://cap.cloud.sap/docs/get-started/hello-world) service using [CDS](https://cap.cloud.sap/docs/cds/) and [cds.services](https://cap.cloud.sap/docs/node.js/api#services-api).",
|
||||
"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": "### Orders - Compositions and Serving Documents\n\nA standalone orders management service, demonstrating:\n- Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with\n- [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data)\n",
|
||||
"line": 1,
|
||||
"selection": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"character": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 27,
|
||||
"character": 1
|
||||
}
|
||||
},
|
||||
"title": "Orders"
|
||||
},
|
||||
{
|
||||
"file": "reviews/db/schema.cds",
|
||||
"description": "### Reviews - More Modularity\n\nShows how to implement a modular service to manage product reviews, including:\n- Consuming other services synchronously and asynchronously\n- Serving requests synchronously\n- Emitting events asynchronously\n- Grow as you go, with:\n- Mocking app services\n- Running service meshes\n- Late-cut Micro Services\n- As well as managed data, input validations, and authorization\n",
|
||||
"line": 1,
|
||||
"selection": {
|
||||
"start": {
|
||||
"line": 1,
|
||||
"character": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 39,
|
||||
"character": 1
|
||||
}
|
||||
},
|
||||
"title": "Reviews"
|
||||
},
|
||||
{
|
||||
"title": "Bookstore",
|
||||
"description": "### Bookstore - Reuse and UI\n\n- A [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/reuse-and-compose) these packages:\n - [@capire/bookshop](bookshop)\n - [@capire/reviews](reviews)\n - [@capire/orders](orders)\n - [@capire/common](common)\n- [The Vue.js app](bookshop/app/vue) imported from bookshop is served as well\n- [The Vue.js app](reviews/app/vue) imported from reviews is served as well\n- [The Fiori app](orders/app) imported from orders is served as well\n- [OpenAPI export + Swagger UI](https://cap.cloud.sap/docs/advanced/openapi)"
|
||||
},
|
||||
{
|
||||
"file": "fiori/app/services.cds",
|
||||
"description": "### Annotations for SAP Fiori Elements\n\nAdds an SAP Fiori elements application to bookstore, thereby introducing:\n- OData Annotations in `.cds` files\n- Support for Fiori Draft\n- Support for Value Helps\n- Serving SAP Fiori apps locally\n\nSee the [Serving Fiori UIs](https://cap.cloud.sap/docs/advanced/fiori) documentation for more information.",
|
||||
"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",
|
||||
"selection": {
|
||||
"start": {
|
||||
"line": 8,
|
||||
"character": 1
|
||||
},
|
||||
"end": {
|
||||
"line": 16,
|
||||
"character": 1
|
||||
}
|
||||
},
|
||||
"title": "Packages"
|
||||
}
|
||||
],
|
||||
"isPrimary": true,
|
||||
"description": "Overview of CAP Samples for Node.js"
|
||||
}
|
||||
2
.vscode/extensions.json
vendored
2
.vscode/extensions.json
vendored
@@ -9,6 +9,8 @@
|
||||
"dbaeumer.vscode-eslint",
|
||||
"mechatroner.rainbow-csv",
|
||||
"humao.rest-client",
|
||||
"sdras.night-owl",
|
||||
"vsls-contrib.codetour"
|
||||
],
|
||||
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
|
||||
"unwantedRecommendations": [
|
||||
|
||||
10
.vscode/settings.json
vendored
10
.vscode/settings.json
vendored
@@ -1,8 +1,9 @@
|
||||
{
|
||||
"files.exclude": {
|
||||
"**/node_modules": true,
|
||||
"LICENSES": true,
|
||||
".reuse": true
|
||||
".reuse/**": true,
|
||||
"**/.gitignore": true,
|
||||
"**/.vscode": true,
|
||||
"LICENSES/**": true
|
||||
},
|
||||
"debug.javascript.terminalOptions": {
|
||||
"skipFiles": [
|
||||
@@ -12,6 +13,5 @@
|
||||
"**/cds/lib/req/cds-context.js",
|
||||
"**/odata-v4/okra/**"
|
||||
]
|
||||
},
|
||||
"jest.jestCommandLine": "npx jest"
|
||||
}
|
||||
}
|
||||
|
||||
75
README.md
Normal file
75
README.md
Normal file
@@ -0,0 +1,75 @@
|
||||
# Welcome to cap/samples
|
||||
|
||||
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo).
|
||||
|
||||
[See **Overview** of contained samples](samples.md):
|
||||
|
||||

|
||||
|
||||

|
||||
[](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples)
|
||||
|
||||
|
||||
|
||||
### Preliminaries
|
||||
|
||||
1. Ensure you have the latest LTS version of Node.js installed (see [Getting Started](https://cap.cloud.sap/docs/get-started/jumpstart))
|
||||
2. Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/) globally:
|
||||
|
||||
```sh
|
||||
npm i -g @sap/cds-dk
|
||||
```
|
||||
|
||||
3. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/tools#vscode)
|
||||
|
||||
### Download
|
||||
|
||||
If you've [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/main.zip).
|
||||
|
||||
```sh
|
||||
git clone https://github.com/sap-samples/cloud-cap-samples samples
|
||||
cd samples
|
||||
```
|
||||
|
||||
### Setup
|
||||
|
||||
In the samples folder run:
|
||||
|
||||
```sh
|
||||
npm ci
|
||||
```
|
||||
|
||||
### Run
|
||||
|
||||
With that you're ready to run the samples, for example:
|
||||
|
||||
```sh
|
||||
cds watch bookshop
|
||||
```
|
||||
|
||||
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
|
||||
|
||||
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example:
|
||||
|
||||
```sh
|
||||
npx jest
|
||||
```
|
||||
> While mocha is a bit smaller and faster, jest runs tests in parallel and isolation, which allows to run all tests.
|
||||
|
||||
## 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
|
||||
|
||||
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.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2022 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](LICENSE) file.
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<title> Capire Books </title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/primitive-ui/dist/css/main.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
|
||||
<style>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
ID,title,descr,author_ID,stock,price,currency_code,genre_ID
|
||||
201,Wuthering Heights,"Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym ""Ellis Bell"". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850.",101,12,11.11,GBP,11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
|
||||
207,Jane Eyre,"Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name ""Currer Bell"", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism.",107,11,12.34,GBP,11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
|
||||
251,The Raven,"""The Raven"" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word ""Nevermore"". The poem makes use of folk, mythological, religious, and classical references.",150,333,13.13,USD,16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
|
||||
252,Eleonora,"""Eleonora"" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively ""happy"" ending.",150,555,14,USD,15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
|
||||
271,Catweazle,"Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts.",170,22,150,JPY,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa
|
||||
201,Wuthering Heights,"Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym ""Ellis Bell"". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850.",101,12,11.11,GBP,11
|
||||
207,Jane Eyre,"Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name ""Currer Bell"", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism.",107,11,12.34,GBP,11
|
||||
251,The Raven,"""The Raven"" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word ""Nevermore"". The poem makes use of folk, mythological, religious, and classical references.",150,333,13.13,USD,16
|
||||
252,Eleonora,"""Eleonora"" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively ""happy"" ending.",150,555,14,USD,15
|
||||
271,Catweazle,"Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts.",170,22,150,JPY,13
|
||||
|
@@ -1,5 +1,5 @@
|
||||
ID_texts,ID,locale,title,descr
|
||||
d2a65a27-9f2a-480f-bc38-84ee8ec5c13e,201,de,Sturmhöhe,"Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (1818–1848). Der 1847 unter dem Pseudonym Ellis Bell veröffentlichte Roman wurde vom viktorianischen Publikum weitgehend abgelehnt, heute gilt er als ein Klassiker der britischen Romanliteratur des 19. Jahrhunderts."
|
||||
8c42c706-a979-41cf-9ffe-91e6cf1383a0,201,fr,Les Hauts de Hurlevent,"Les Hauts de Hurlevent (titre original : Wuthering Heights), parfois orthographié Les Hauts de Hurle-Vent, est l'unique roman d'Emily Brontë, publié pour la première fois en 1847 sous le pseudonyme d’Ellis Bell. Loin d'être un récit moralisateur, Emily Brontë achève néanmoins le roman dans une atmosphère sereine, suggérant le triomphe de la paix et du Bien sur la vengeance et le Mal."
|
||||
9e1c4c81-dc90-4600-85b1-e9dd4bf12ce0,207,de,Jane Eyre,"Jane Eyre. Eine Autobiographie (Originaltitel: Jane Eyre. An Autobiography), erstmals erschienen im Jahr 1847 unter dem Pseudonym Currer Bell, ist der erste veröffentlichte Roman der britischen Autorin Charlotte Brontë und ein Klassiker der viktorianischen Romanliteratur des 19. Jahrhunderts. Der Roman erzählt in Form einer Ich-Erzählung die Lebensgeschichte von Jane Eyre (ausgesprochen /ˌdʒeɪn ˈɛə/), die nach einer schweren Kindheit eine Stelle als Gouvernante annimmt und sich in ihren Arbeitgeber verliebt, jedoch immer wieder um ihre Freiheit und Selbstbestimmung kämpfen muss. Als klein, dünn, blass, stets schlicht dunkel gekleidet und mit strengem Mittelscheitel beschrieben, gilt die Heldin des Romans Jane Eyre nicht zuletzt aufgrund der Kino- und Fernsehversionen der melodramatischen Romanvorlage als die bekannteste englische Gouvernante der Literaturgeschichte"
|
||||
9be0524b-4cb9-4fc1-9dc2-d65b1c13cf53,252,de,Eleonora,"“Eleonora” ist eine Erzählung von Edgar Allan Poe. Sie wurde 1841 erstveröffentlicht. In ihr geht es um das Paradox der Treue in der Treulosigkeit."
|
||||
ID,locale,title,descr
|
||||
201,de,Sturmhöhe,"Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (1818–1848). Der 1847 unter dem Pseudonym Ellis Bell veröffentlichte Roman wurde vom viktorianischen Publikum weitgehend abgelehnt, heute gilt er als ein Klassiker der britischen Romanliteratur des 19. Jahrhunderts."
|
||||
201,fr,Les Hauts de Hurlevent,"Les Hauts de Hurlevent (titre original : Wuthering Heights), parfois orthographié Les Hauts de Hurle-Vent, est l'unique roman d'Emily Brontë, publié pour la première fois en 1847 sous le pseudonyme d’Ellis Bell. Loin d'être un récit moralisateur, Emily Brontë achève néanmoins le roman dans une atmosphère sereine, suggérant le triomphe de la paix et du Bien sur la vengeance et le Mal."
|
||||
207,de,Jane Eyre,"Jane Eyre. Eine Autobiographie (Originaltitel: Jane Eyre. An Autobiography), erstmals erschienen im Jahr 1847 unter dem Pseudonym Currer Bell, ist der erste veröffentlichte Roman der britischen Autorin Charlotte Brontë und ein Klassiker der viktorianischen Romanliteratur des 19. Jahrhunderts. Der Roman erzählt in Form einer Ich-Erzählung die Lebensgeschichte von Jane Eyre (ausgesprochen /ˌdʒeɪn ˈɛə/), die nach einer schweren Kindheit eine Stelle als Gouvernante annimmt und sich in ihren Arbeitgeber verliebt, jedoch immer wieder um ihre Freiheit und Selbstbestimmung kämpfen muss. Als klein, dünn, blass, stets schlicht dunkel gekleidet und mit strengem Mittelscheitel beschrieben, gilt die Heldin des Romans Jane Eyre nicht zuletzt aufgrund der Kino- und Fernsehversionen der melodramatischen Romanvorlage als die bekannteste englische Gouvernante der Literaturgeschichte"
|
||||
252,de,Eleonora,"“Eleonora” ist eine Erzählung von Edgar Allan Poe. Sie wurde 1841 erstveröffentlicht. In ihr geht es um das Paradox der Treue in der Treulosigkeit."
|
||||
|
||||
|
@@ -1,64 +0,0 @@
|
||||
ID;parent_ID;name;page;book_ID
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62514;;Foreword;3;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62515;;Chapter 1;4;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62516;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.1;5;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62517;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.2;28;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62518;f846b0b9-01d4-4f6d-82a4-d79204f62517;Subsection 1.2.1;30;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62519;f846b0b9-01d4-4f6d-82a4-d79204f62517;Subsection 1.2.2;33;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62520;f846b0b9-01d4-4f6d-82a4-d79204f62515;Section 1.3;36;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62521;;Chapter 2;54;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62522;f846b0b9-01d4-4f6d-82a4-d79204f62521;Section 2.1;56;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62523;f846b0b9-01d4-4f6d-82a4-d79204f62521;Section 2.2;58;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62524;;Conclusion;63;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62525;;Endnotes;65;207
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62526;;Copyright notice;2;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62527;;Chapter 1;3;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62528;f846b0b9-01d4-4f6d-82a4-d79204f62527;Section 1.1;5;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62529;f846b0b9-01d4-4f6d-82a4-d79204f62527;Section 1.2;13;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62530;;Chapter 2;20;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62531;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.1;21;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62532;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.2;25;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62533;f846b0b9-01d4-4f6d-82a4-d79204f62530;Section 2.3;27;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62534;;Chapter 3;30;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62535;;Endnotes;41;251
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62551;;Acknowledgements;1;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62552;;The Flight;2;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62553;;Hexwood Farm;8;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62554;f846b0b9-01d4-4f6d-82a4-d79204f62553;Castle Saburac;13;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62555;f846b0b9-01d4-4f6d-82a4-d79204f62553;The Curse of Rapkyn;27;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62556;f846b0b9-01d4-4f6d-82a4-d79204f62553;The Mannikin;35;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62557;;The Eye of Time;44;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62558;;The Enchanting;59;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62559;f846b0b9-01d4-4f6d-82a4-d79204f62558;The Telling Bone;73;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62560;f846b0b9-01d4-4f6d-82a4-d79204f62558;The Power of Adamcos;86;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62561;f846b0b9-01d4-4f6d-82a4-d79204f62558;The House of the Sorcerer;98;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62562;;The Changeling;105;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62563;f846b0b9-01d4-4f6d-82a4-d79204f62562;The Flying Broomsticks;118;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62564;f846b0b9-01d4-4f6d-82a4-d79204f62563;The Fish Out of Water;126;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62565;;The Final Magic;138;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62566;;Copyright;159;271
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62567;;Editor's Note;1;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62568;;Chapter I;2;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62569;;Chapter II;31;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62570;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.I;47;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62571;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.II;62;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62572;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.III;75;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62573;f846b0b9-01d4-4f6d-82a4-d79204f62569;Section II.IV;87;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62574;;Chapter III;105;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62575;f846b0b9-01d4-4f6d-82a4-d79204f62574;Section III.I;128;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62576;f846b0b9-01d4-4f6d-82a4-d79204f62575;Subsection III.I.I;156;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62577;f846b0b9-01d4-4f6d-82a4-d79204f62575;Subsection III.I.II;173;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62578;f846b0b9-01d4-4f6d-82a4-d79204f62574;Section III.II;185;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62579;;Chapter IV;203;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62580;;Acknowledgments;250;201
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62582;;Foreword;1;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62583;;Chapter 1;3;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62584;f846b0b9-01d4-4f6d-82a4-d79204f62583;Section 1.1;5;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62585;f846b0b9-01d4-4f6d-82a4-d79204f62583;Section 1.2;8;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62586;;Chapter 2;10;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62587;f846b0b9-01d4-4f6d-82a4-d79204f62586;Section 2.1;12;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62588;f846b0b9-01d4-4f6d-82a4-d79204f62587;Subsection 2.1.1;14;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62589;f846b0b9-01d4-4f6d-82a4-d79204f62587;Subsection 2.1.2;16;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62590;f846b0b9-01d4-4f6d-82a4-d79204f62586;Section 2.2;18;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62591;f846b0b9-01d4-4f6d-82a4-d79204f62590;Subsection 2.2.1;21;252
|
||||
f846b0b9-01d4-4f6d-82a4-d79204f62592;;Endnotes;25;252
|
||||
|
@@ -1,43 +1,16 @@
|
||||
ID,parent_ID,name
|
||||
10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,,Fiction
|
||||
11aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Drama
|
||||
12aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Poetry
|
||||
13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Fantasy
|
||||
131aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Fairy Tale
|
||||
132aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Epic Fantasy
|
||||
133aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,High Fantasy
|
||||
134aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,13aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Gothic
|
||||
14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Science Fiction
|
||||
141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Utopian and Dystopian
|
||||
1411aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Utopian
|
||||
1412aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,141aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Dystopian
|
||||
14121aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1412aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Cyberpunk
|
||||
141211aa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14121aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Steampunk
|
||||
142aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Space Opera
|
||||
143aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Time Travel
|
||||
144aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,14aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Tech Noir
|
||||
15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Romance
|
||||
151aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Contemporary Romance
|
||||
152aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Historical Romance
|
||||
153aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,15aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Romantic Suspense
|
||||
16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Mystery
|
||||
161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Crime
|
||||
1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Thriller
|
||||
16111aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Police Procedural
|
||||
16112aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Legal Thriller
|
||||
16113aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Medical Thriller
|
||||
16114aaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,1611aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Spy Thriller
|
||||
1612aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Detective
|
||||
1613aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,161aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Suspense
|
||||
162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Noir
|
||||
1621aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Nordic Noir
|
||||
1622aaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,162aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Tart Noir
|
||||
163aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,16aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Cozy Mystery
|
||||
17aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Adventure
|
||||
18aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Short Story
|
||||
19aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,10aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Graphic Novel
|
||||
20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,,Non-Fiction
|
||||
21aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Biography
|
||||
22aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,21aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Autobiography
|
||||
23aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Essay
|
||||
24aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,20aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa,Speech
|
||||
10,,Fiction
|
||||
11,10,Drama
|
||||
12,10,Poetry
|
||||
13,10,Fantasy
|
||||
14,10,Science Fiction
|
||||
15,10,Romance
|
||||
16,10,Mystery
|
||||
17,10,Thriller
|
||||
18,10,Dystopia
|
||||
19,10,Fairy Tale
|
||||
20,,Non-Fiction
|
||||
21,20,Biography
|
||||
22,21,Autobiography
|
||||
23,20,Essay
|
||||
24,20,Speech
|
||||
|
||||
|
@@ -8,10 +8,9 @@ entity Books : managed {
|
||||
author : Association to Authors @mandatory;
|
||||
genre : Association to Genres;
|
||||
stock : Integer;
|
||||
price : Price;
|
||||
price : Decimal;
|
||||
currency : Currency;
|
||||
image : LargeBinary @Core.MediaType: 'image/png';
|
||||
contents : Composition of many Contents on contents.book = $self;
|
||||
}
|
||||
|
||||
entity Authors : managed {
|
||||
@@ -26,24 +25,7 @@ entity Authors : managed {
|
||||
|
||||
/** Hierarchically organized Code List for Genres */
|
||||
entity Genres : sap.common.CodeList {
|
||||
key ID : UUID;
|
||||
key ID : Integer;
|
||||
parent : Association to Genres;
|
||||
children : Composition of many Genres on children.parent = $self;
|
||||
}
|
||||
|
||||
/** Hierarchically organized entity for Contents */
|
||||
entity Contents {
|
||||
key ID : UUID;
|
||||
name : String;
|
||||
page : Integer;
|
||||
parent : Association to Contents;
|
||||
children : Composition of many Contents on children.parent = $self;
|
||||
book : Association to Books;
|
||||
}
|
||||
|
||||
type Price : Decimal(9,2);
|
||||
|
||||
|
||||
// ------------------------------------------------------------------
|
||||
// temporary workaround for reuse in fiori sample and hana deployment
|
||||
annotate Books with @fiori.draft.enabled;
|
||||
|
||||
@@ -6,12 +6,14 @@
|
||||
"app",
|
||||
"srv",
|
||||
"db",
|
||||
"index.cds"
|
||||
"index.cds",
|
||||
"index.js"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@cap-js/sqlite": ">=1"
|
||||
"@cap-js/sqlite": "*"
|
||||
},
|
||||
"dependencies": {
|
||||
"@cap-js/ord": "2",
|
||||
"@sap/cds": ">=7",
|
||||
"express": "^4.17.1"
|
||||
},
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
using { AdminService } from './admin-service';
|
||||
annotate AdminService with @requires:'admin';
|
||||
@@ -1,7 +1,5 @@
|
||||
using { sap.capire.bookshop as my } from '../db/schema';
|
||||
service AdminService @(path:'/admin') {
|
||||
entity Authors as projection on my.Authors;
|
||||
service AdminService @(requires:'admin', path:'/admin') {
|
||||
entity Books as projection on my.Books;
|
||||
entity Genres as projection on my.Genres;
|
||||
entity Contents as projection on my.Contents;
|
||||
entity Authors as projection on my.Authors;
|
||||
}
|
||||
|
||||
@@ -3,14 +3,14 @@ service CatalogService @(path:'/browse') {
|
||||
|
||||
/** For displaying lists of Books */
|
||||
@readonly entity ListOfBooks as projection on Books
|
||||
excluding { descr, contents };
|
||||
excluding { descr };
|
||||
|
||||
/** For display in details pages */
|
||||
@readonly entity Books as projection on my.Books { *,
|
||||
author.name as author
|
||||
} excluding { createdBy, modifiedBy, contents };
|
||||
} excluding { createdBy, modifiedBy };
|
||||
|
||||
@requires: 'authenticated-user'
|
||||
action submitOrder ( book: Books:ID, quantity: Integer ) returns { stock: Integer };
|
||||
event OrderedBook : { book: Books:ID; quantity: Integer; buyer: String };
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,24 +14,25 @@ GET http://localhost:4004/odata/v4/test/Genres?
|
||||
POST http://localhost:4004/odata/v4/test/Genres?
|
||||
Content-Type: application/json
|
||||
|
||||
{ "ID":"100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Some Sample Genres...", "children":[
|
||||
{ "ID":"101aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Cat", "children":[
|
||||
{ "ID":"102aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Kitty", "children":[
|
||||
{ "ID":"103aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Aristocat" },
|
||||
{ "ID":"104aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Kitty Bat" } ]},
|
||||
{ "ID":"105aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catwoman", "children":[
|
||||
{ "ID":"106aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catalina" } ]} ]},
|
||||
{ "ID":"107aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa", "name":"Catweazle" }
|
||||
{ "ID":100, "name":"Some Sample Genres...", "children":[
|
||||
{ "ID":101, "name":"Cat", "children":[
|
||||
{ "ID":102, "name":"Kitty", "children":[
|
||||
{ "ID":103, "name":"Kitty Cat", "children":[
|
||||
{ "ID":104, "name":"Aristocat" } ]},
|
||||
{ "ID":105, "name":"Kitty Bat" } ]},
|
||||
{ "ID":106, "name":"Catwoman", "children":[
|
||||
{ "ID":107, "name":"Catalina" } ]} ]},
|
||||
{ "ID":108, "name":"Catweazle" }
|
||||
]}
|
||||
###
|
||||
|
||||
GET http://localhost:4004/odata/v4/test/Genres(100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)?
|
||||
&$expand=children
|
||||
&$expand=children($expand=children($expand=children($expand=children)))
|
||||
GET http://localhost:4004/odata/v4/test/Genres(100)?
|
||||
# &$expand=children
|
||||
# &$expand=children($expand=children($expand=children($expand=children)))
|
||||
###
|
||||
|
||||
DELETE http://localhost:4004/odata/v4/test/Genres(103aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)
|
||||
DELETE http://localhost:4004/odata/v4/test/Genres(103)
|
||||
###
|
||||
|
||||
DELETE http://localhost:4004/odata/v4/test/Genres(100aaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa)
|
||||
DELETE http://localhost:4004/odata/v4/test/Genres(100)
|
||||
###
|
||||
|
||||
@@ -40,7 +40,8 @@ Authorization: Basic alice:
|
||||
|
||||
{
|
||||
"ID": 112,
|
||||
"name": "Shakespeeeeere"
|
||||
"name": "Shakespeeeeere",
|
||||
"age": 22
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +56,7 @@ Authorization: Basic alice:
|
||||
"title": "Poems : Pocket Poets",
|
||||
"descr": "The Everyman's Library Pocket Poets hardcover series is popular for its compact size and reasonable price which does not compromise content. Poems: Bronte contains poems that demonstrate a sensibility elemental in its force with an imaginative discipline and flexibility of the highest order. Also included are an Editor's Note and an index of first lines.",
|
||||
"author": { "ID": 101 },
|
||||
"genre": { "ID": "12aaaaaa-aaaa-aaaa-aaaa-aaaaaaaaaaaa" },
|
||||
"genre": { "ID": 12 },
|
||||
"stock": 5,
|
||||
"price": "12.05",
|
||||
"currency": { "code": "USD" }
|
||||
|
||||
@@ -1 +0,0 @@
|
||||
require('./srv/server')
|
||||
@@ -7,11 +7,8 @@
|
||||
"@capire/orders": "*",
|
||||
"@capire/common": "*",
|
||||
"@capire/data-viewer": "*",
|
||||
"@sap-cloud-sdk/http-client": "^4",
|
||||
"@sap-cloud-sdk/resilience": "^4",
|
||||
"@sap/cds": ">=5",
|
||||
"express": "^4.17.1",
|
||||
"@cap-js/hana": ">=1"
|
||||
"express": "^4.17.1"
|
||||
},
|
||||
"cds": {
|
||||
"requires": {
|
||||
@@ -23,12 +20,15 @@
|
||||
"kind": "odata",
|
||||
"model": "@capire/orders"
|
||||
},
|
||||
"messaging": true,
|
||||
"db": true
|
||||
"messaging": {
|
||||
"[development]": { "kind": "file-based-messaging" },
|
||||
"[hybrid]": { "kind": "enterprise-messaging-shared" },
|
||||
"[production]": { "kind": "enterprise-messaging" }
|
||||
},
|
||||
"db": {
|
||||
"kind": "sql"
|
||||
}
|
||||
},
|
||||
"log": { "service": true }
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cds-serve"
|
||||
}
|
||||
}
|
||||
}
|
||||
20
bookstore/server.js
Normal file
20
bookstore/server.js
Normal file
@@ -0,0 +1,20 @@
|
||||
const cds = require ('@sap/cds')
|
||||
|
||||
// Add mashup logic
|
||||
cds.once('served', require('./srv/mashup'))
|
||||
|
||||
// Add routes to UIs from imported packages
|
||||
cds.once('bootstrap',(app)=>{
|
||||
try {
|
||||
app.serve ('/bookshop') .from ('@capire/bookshop','app/vue')
|
||||
app.serve ('/reviews') .from ('@capire/reviews','app/vue')
|
||||
app.serve ('/orders') .from('@capire/orders','app/orders')
|
||||
app.serve ('/data') .from('@capire/data-viewer','app/viewer')
|
||||
} catch (err) {
|
||||
if (err.code === 'MODULE_NOT_FOUND') throw new Error('Run "npm ci" to install the required dependencies', { cause: err })
|
||||
throw err
|
||||
}
|
||||
})
|
||||
|
||||
// Add Swagger UI
|
||||
require('./srv/swagger-ui')
|
||||
@@ -1,17 +1,10 @@
|
||||
const cds = require ('@sap/cds')
|
||||
|
||||
|
||||
// Add routes to UIs from imported packages
|
||||
if (!cds.env.production) cds.once ('bootstrap', (app) => {
|
||||
app.serve ('/bookshop') .from ('@capire/bookshop','app/vue')
|
||||
app.serve ('/reviews') .from ('@capire/reviews','app/vue')
|
||||
app.serve ('/orders') .from('@capire/orders','app/orders')
|
||||
})
|
||||
|
||||
|
||||
// Mashing up bookshop services with required services...
|
||||
cds.once ('served', async ()=>{
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Mashing up bookshop services with required services...
|
||||
//
|
||||
module.exports = async()=>{ // called by server.js
|
||||
|
||||
const cds = require('@sap/cds')
|
||||
const CatalogService = await cds.connect.to ('CatalogService')
|
||||
const ReviewsService = await cds.connect.to ('ReviewsService')
|
||||
const OrdersService = await cds.connect.to ('OrdersService')
|
||||
@@ -36,7 +29,7 @@ cds.once ('served', async ()=>{
|
||||
CatalogService.on ('OrderedBook', async (msg) => {
|
||||
const { book, quantity, buyer } = msg.data
|
||||
const { title, price } = await db.read (Books, book, b => { b.title, b.price })
|
||||
return OrdersService.create ('OrdersNoDraft').entries({
|
||||
return OrdersService.create ('Orders').entries({
|
||||
OrderNo: 'Order at '+ (new Date).toLocaleString(),
|
||||
Items: [{ product:{ID:`${book}`}, title, price, quantity }],
|
||||
buyer, createdBy: buyer
|
||||
@@ -62,4 +55,4 @@ cds.once ('served', async ()=>{
|
||||
.and ('stock >=', deltaQuantity)
|
||||
.set ('stock -=', deltaQuantity)
|
||||
})
|
||||
})
|
||||
}
|
||||
10
bookstore/srv/swagger-ui.js
Normal file
10
bookstore/srv/swagger-ui.js
Normal file
@@ -0,0 +1,10 @@
|
||||
|
||||
// -----------------------------------------------------------------------
|
||||
// Adding Swagger UI - see https://cap.cloud.sap/docs/advanced/openapi
|
||||
const cds = require ('@sap/cds')
|
||||
try {
|
||||
const cds_swagger = require ('cds-swagger-ui-express')
|
||||
cds.once ('bootstrap', app => app.use (cds_swagger()) )
|
||||
} catch (err) {
|
||||
if (err.code !== 'MODULE_NOT_FOUND') throw err
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using { sap } from '@sap/cds/common';
|
||||
|
||||
extend sap.common.Currencies with {
|
||||
// Currencies.code = ISO 4217 alphabetic three-letter code
|
||||
// with the first two letters being equal to ISO 3166 alphabetic country codes
|
||||
// See also:
|
||||
// [1] https://www.iso.org/iso-4217-currency-codes.html
|
||||
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
|
||||
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
|
||||
numcode : Integer;
|
||||
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
|
||||
minor : String; //> e.g. 'Cent'
|
||||
}
|
||||
@@ -1,2 +1,45 @@
|
||||
using from './currencies';
|
||||
using from './regions';
|
||||
using { sap } from '@sap/cds/common';
|
||||
|
||||
extend sap.common.Currencies with {
|
||||
// Currencies.code = ISO 4217 alphabetic three-letter code
|
||||
// with the first two letters being equal to ISO 3166 alphabetic country codes
|
||||
// See also:
|
||||
// [1] https://www.iso.org/iso-4217-currency-codes.html
|
||||
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
|
||||
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
|
||||
numcode : Integer;
|
||||
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
|
||||
minor : String; //> e.g. 'Cent'
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* The Code Lists below are designed as optional extensions to
|
||||
* the base schema. Switch them on by adding an Association to
|
||||
* one of the code list entities in your models or by:
|
||||
* annotate sap.common.Countries with @cds.persistence.skip:false;
|
||||
*/
|
||||
|
||||
context sap.common.countries {
|
||||
|
||||
extend sap.common.Countries {
|
||||
regions : Composition of many Regions on regions._parent = $self.code;
|
||||
}
|
||||
|
||||
entity Regions : sap.common.CodeList {
|
||||
key code : String(5); // ISO 3166-2 alpha5 codes, e.g. DE-BW
|
||||
children : Composition of many Regions on children._parent = $self.code;
|
||||
cities : Composition of many Cities on cities.region = $self;
|
||||
_parent : String(11);
|
||||
}
|
||||
entity Cities : sap.common.CodeList {
|
||||
key code : String(11);
|
||||
region : Association to Regions;
|
||||
districts : Composition of many Districts on districts.city = $self;
|
||||
}
|
||||
entity Districts : sap.common.CodeList {
|
||||
key code : String(11);
|
||||
city : Association to Cities;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,22 +0,0 @@
|
||||
using { sap.common } from '@sap/cds/common';
|
||||
namespace sap.common.countries;
|
||||
|
||||
extend common.Countries {
|
||||
regions : Composition of many Regions on regions._parent = $self.code;
|
||||
}
|
||||
|
||||
entity Regions : common.CodeList {
|
||||
key code : String(5); // ISO 3166-2 alpha5 codes, e.g. DE-BW
|
||||
children : Composition of many Regions on children._parent = $self.code;
|
||||
cities : Composition of many Cities on cities.region = $self;
|
||||
_parent : String(11);
|
||||
}
|
||||
entity Cities : common.CodeList {
|
||||
key code : String(11);
|
||||
region : Association to Regions;
|
||||
districts : Composition of many Districts on districts.city = $self;
|
||||
}
|
||||
entity Districts : common.CodeList {
|
||||
key code : String(11);
|
||||
city : Association to Cities;
|
||||
}
|
||||
@@ -34,8 +34,7 @@ class DataService extends cds.ApplicationService { init(){
|
||||
query.SELECT.limit = req.query.SELECT.limit // forward $skip / $top
|
||||
|
||||
const dataSource = findDataSource(dataSourceName, entityName)
|
||||
let res = await dataSource.run(query)
|
||||
if (!Array.isArray(res)) res = [res] // singleton result
|
||||
const res = await dataSource.run(query)
|
||||
return res.map((line) => {
|
||||
const record = Object.entries(line).map(([column, data]) => ({ column, data }))
|
||||
return {
|
||||
@@ -1,4 +0,0 @@
|
||||
const cds = require("@sap/cds")
|
||||
cds.on ('served', ()=> {
|
||||
cds.app.serve ('/data') .from ('@capire/data-viewer','app/viewer')
|
||||
})
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 273 KiB |
@@ -1,29 +0,0 @@
|
||||
{
|
||||
"version": "1.1.0",
|
||||
"options": {
|
||||
"management": true,
|
||||
"messagingrest": true,
|
||||
"messaging": true
|
||||
},
|
||||
"rules": {
|
||||
"topicRules": {
|
||||
"publishFilter": [
|
||||
"*"
|
||||
],
|
||||
"subscribeFilter": [
|
||||
"*"
|
||||
]
|
||||
},
|
||||
"queueRules": {
|
||||
"publishFilter": [
|
||||
"*"
|
||||
],
|
||||
"subscribeFilter": [
|
||||
"*"
|
||||
]
|
||||
}
|
||||
},
|
||||
"authorities": [
|
||||
"$ACCEPT_GRANTED_AUTHORITIES"
|
||||
]
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
cds.requires.[hybrid].messaging.kind=file-based-messaging
|
||||
@@ -1,7 +1,7 @@
|
||||
Age = Age
|
||||
Lifetime = Lifetime
|
||||
|
||||
SubGenres = Subgenre
|
||||
SubGenres = Sub Genres
|
||||
|
||||
NumCode = Numeric Code
|
||||
MinorUnit = Minor Unit
|
||||
|
||||
@@ -1,11 +1,6 @@
|
||||
using { AdminService } from '@capire/bookstore';
|
||||
using from '../common'; // to help UI linter get the complete annotations
|
||||
|
||||
annotate sap.capire.bookshop.Genres with @fiori.draft.enabled;
|
||||
annotate AdminService.Genres with @odata.draft.enabled;
|
||||
annotate AdminService.Genres with @odata.draft.bypass;
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Books Object Page
|
||||
@@ -14,11 +9,10 @@ annotate AdminService.Genres with @odata.draft.bypass;
|
||||
annotate AdminService.Books with @(
|
||||
UI: {
|
||||
Facets: [
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'},
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Translations}', Target: 'texts/@UI.LineItem'},
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'},
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Contents}', Target: 'contents/@UI.PresentationVariant'}
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'},
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Translations}', Target: 'texts/@UI.LineItem'},
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
|
||||
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'},
|
||||
],
|
||||
FieldGroup#General: {
|
||||
Data: [
|
||||
@@ -46,34 +40,6 @@ annotate AdminService.Books with @(
|
||||
}
|
||||
);
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Value Help for Tree Table
|
||||
//
|
||||
annotate AdminService.Books with {
|
||||
genre @(Common: {
|
||||
Label : 'Genre',
|
||||
ValueList: {
|
||||
CollectionPath : 'Genres',
|
||||
Parameters : [
|
||||
{
|
||||
$Type : 'Common.ValueListParameterDisplayOnly',
|
||||
ValueListProperty: 'name',
|
||||
},
|
||||
{
|
||||
$Type : 'Common.ValueListParameterInOut',
|
||||
LocalDataProperty: genre_ID,
|
||||
ValueListProperty: 'ID',
|
||||
}
|
||||
],
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// Hide ID because of the ValueHelp
|
||||
annotate AdminService.Genres with {
|
||||
ID @UI.Hidden;
|
||||
};
|
||||
|
||||
|
||||
////////////////////////////////////////////////////////////
|
||||
@@ -116,4 +82,5 @@ extend service AdminService {
|
||||
// Workaround for Fiori popup for asking user to enter a new UUID on Create
|
||||
annotate AdminService.Books with { ID @Core.Computed; }
|
||||
|
||||
using from './tree-view';
|
||||
// Show Genre as drop down, not a dialog
|
||||
annotate AdminService.Books with { genre @Common.ValueListWithFixedValues; }
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
using { AdminService } from '@capire/bookstore';
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Contents Tree Table Annotations
|
||||
//
|
||||
|
||||
// Tell Fiori about the structure of the hierarchy
|
||||
annotate AdminService.Contents with @Aggregation.RecursiveHierarchy #ContentsHierarchy : {
|
||||
ParentNavigationProperty : parent, // navigates to a node's parent
|
||||
NodeProperty : ID, // identifies a node, usually the key
|
||||
};
|
||||
|
||||
// Fiori expects the following to be defined explicitly, even though they're always the same
|
||||
extend AdminService.Contents with @(
|
||||
// The columns expected by Fiori to be present in hierarchy entities
|
||||
Hierarchy.RecursiveHierarchy #ContentsHierarchy : {
|
||||
LimitedDescendantCount : LimitedDescendantCount,
|
||||
DistanceFromRoot : DistanceFromRoot,
|
||||
DrillState : DrillState,
|
||||
LimitedRank : LimitedRank
|
||||
},
|
||||
// Disallow filtering on these properties from Fiori UIs
|
||||
Capabilities.FilterRestrictions.NonFilterableProperties: [
|
||||
'LimitedDescendantCount',
|
||||
'DistanceFromRoot',
|
||||
'DrillState',
|
||||
'LimitedRank'
|
||||
],
|
||||
// Disallow sorting on these properties from Fiori UIs
|
||||
Capabilities.SortRestrictions.NonSortableProperties : [
|
||||
'LimitedDescendantCount',
|
||||
'DistanceFromRoot',
|
||||
'DrillState',
|
||||
'LimitedRank'
|
||||
],
|
||||
) columns { // Ensure we can query these fields from database
|
||||
null as LimitedDescendantCount : Int16,
|
||||
null as DistanceFromRoot : Int16,
|
||||
null as DrillState : String,
|
||||
null as LimitedRank : Int16,
|
||||
};
|
||||
@@ -79,11 +79,6 @@
|
||||
"pattern": "Books({key}/author({key2}):?query:",
|
||||
"name": "AuthorsDetails",
|
||||
"target": "AuthorsDetails"
|
||||
},
|
||||
{
|
||||
"pattern": "Books({key})/contents({key2}):?query:",
|
||||
"name": "ContentsDetails",
|
||||
"target": "ContentsDetails"
|
||||
}
|
||||
],
|
||||
"targets": {
|
||||
@@ -117,34 +112,11 @@
|
||||
"detail" : {
|
||||
"route" : "AuthorsDetails"
|
||||
}
|
||||
},
|
||||
"contents": {
|
||||
"detail": {
|
||||
"route": "ContentsDetails"
|
||||
}
|
||||
}
|
||||
},
|
||||
"controlConfiguration": {
|
||||
"contents/@com.sap.vocabularies.UI.v1.LineItem": {
|
||||
"tableSettings": {
|
||||
"hierarchyQualifier": "ContentsHierarchy",
|
||||
"type": "TreeTable"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"ContentsDetails": {
|
||||
"type": "Component",
|
||||
"id": "ContentsDetails",
|
||||
"name": "sap.fe.templates.ObjectPage",
|
||||
"options": {
|
||||
"settings": {
|
||||
"contextPath": "/Books/contents"
|
||||
}
|
||||
}
|
||||
},
|
||||
"AuthorsDetails": {
|
||||
"type": "Component",
|
||||
"id": "AuthorsDetailsList",
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
"LaunchPage": {
|
||||
"adapter": {
|
||||
"config": {
|
||||
"catalogs": [],
|
||||
"groups": [
|
||||
{
|
||||
"id": "Bookshop",
|
||||
@@ -18,6 +19,14 @@
|
||||
"title": "Browse Books",
|
||||
"targetURL": "#Books-display"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "BrowseGenres",
|
||||
"tileType": "sap.ushell.ui.tile.StaticTile",
|
||||
"properties": {
|
||||
"title": "Browse Genres (OData v2)",
|
||||
"targetURL": "#Genres-display"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
@@ -35,14 +44,6 @@
|
||||
"title": "Manage Books",
|
||||
"targetURL": "#Books-manage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ManageGenres",
|
||||
"tileType": "sap.ushell.ui.tile.StaticTile",
|
||||
"properties": {
|
||||
"title": "Manage Genres",
|
||||
"targetURL": "#Genres-manage"
|
||||
}
|
||||
},
|
||||
{
|
||||
"id": "ManageAuthors",
|
||||
@@ -114,10 +115,10 @@
|
||||
"url": "/admin-authors/webapp"
|
||||
}
|
||||
},
|
||||
"ManageGenres": {
|
||||
"BrowseGenres": {
|
||||
"semanticObject": "Genres",
|
||||
"action": "manage",
|
||||
"title": "Manage Genres",
|
||||
"action": "display",
|
||||
"title": "Browse Genres",
|
||||
"signature": {
|
||||
"parameters": {
|
||||
"Genre.ID": {
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
using { sap.capire.bookshop as my } from '@capire/bookstore';
|
||||
using { sap.common } from '@capire/common';
|
||||
using { sap.common.Currencies } from '@sap/cds/common';
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
@@ -37,7 +38,7 @@ annotate my.Books with @(
|
||||
author @ValueList.entity : 'Authors';
|
||||
};
|
||||
|
||||
annotate common.Currencies with {
|
||||
annotate Currencies with {
|
||||
symbol @Common.Label : '{i18n>Currency}';
|
||||
}
|
||||
|
||||
@@ -68,53 +69,55 @@ annotate my.Books with {
|
||||
image @title: '{i18n>Image}';
|
||||
}
|
||||
|
||||
annotate my.Contents with @(
|
||||
cds.search: {name}
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Genres List
|
||||
//
|
||||
annotate my.Genres with @(
|
||||
Common.SemanticKey : [name],
|
||||
UI : {
|
||||
SelectionFields : [name],
|
||||
LineItem : [
|
||||
{ Value: name },
|
||||
{
|
||||
Value : parent.name,
|
||||
Label: 'Main Genre'
|
||||
},
|
||||
],
|
||||
}
|
||||
);
|
||||
|
||||
annotate my.Genres with {
|
||||
ID @Common.Text : name @Common.TextArrangement : #TextOnly;
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Contents List
|
||||
// Genre Details
|
||||
//
|
||||
annotate my.Contents with @UI: {
|
||||
PresentationVariant : {
|
||||
$Type : 'UI.PresentationVariantType',
|
||||
RequestAtLeast: [name],
|
||||
Visualizations: ['@UI.LineItem'],
|
||||
},
|
||||
LineItem : [{
|
||||
$Type: 'UI.DataField',
|
||||
Value: name,
|
||||
Label : '{i18n>Name}'
|
||||
},
|
||||
{
|
||||
$Type: 'UI.DataField',
|
||||
Value: page,
|
||||
Label : '{i18n>Page}'
|
||||
}],
|
||||
HeaderInfo : {
|
||||
$Type : 'UI.HeaderInfoType',
|
||||
TypeName : '{i18n>ContentsLevel}',
|
||||
TypeNamePlural: '{i18n>ContentsLevels}',
|
||||
Title : {
|
||||
$Type: 'UI.DataField',
|
||||
Value: name,
|
||||
}
|
||||
},
|
||||
FieldGroup : {
|
||||
$Type: 'UI.FieldGroupType',
|
||||
Data : [{
|
||||
$Type: 'UI.DataField',
|
||||
Value: page,
|
||||
Label : '{i18n>PageNumber}'
|
||||
}],
|
||||
},
|
||||
Facets : [{
|
||||
$Type : 'UI.ReferenceFacet',
|
||||
Target: '@UI.FieldGroup',
|
||||
Label : '{i18n>Informations}',
|
||||
}],
|
||||
};
|
||||
annotate my.Genres with @(UI : {
|
||||
Identification : [{ Value: name}],
|
||||
HeaderInfo : {
|
||||
TypeName : '{i18n>Genre}',
|
||||
TypeNamePlural : '{i18n>Genres}',
|
||||
Title : { Value: name },
|
||||
Description : { Value: ID }
|
||||
},
|
||||
Facets : [{
|
||||
$Type : 'UI.ReferenceFacet',
|
||||
Label : '{i18n>SubGenres}',
|
||||
Target : 'children/@UI.LineItem'
|
||||
}, ],
|
||||
});
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Genres Elements
|
||||
//
|
||||
annotate my.Genres with {
|
||||
ID @title: '{i18n>ID}';
|
||||
name @title: '{i18n>Genre}';
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
|
||||
@@ -1,32 +1,8 @@
|
||||
using { sap.capire.bookshop.Genres } from '@capire/bookstore';
|
||||
using { sap.capire.bookshop } from '../../db/common';
|
||||
|
||||
annotate Genres with @cds.search: {name};
|
||||
annotate Genres with {
|
||||
name @title: '{i18n>Genre}';
|
||||
annotate bookshop.GenreHierarchy {
|
||||
ID @sap.hierarchy.node.for;
|
||||
parent @sap.hierarchy.parent.node.for;
|
||||
hierarchyLevel @sap.hierarchy.level.for;
|
||||
drillState @sap.hierarchy.drill.state.for;
|
||||
}
|
||||
|
||||
// Lists
|
||||
annotate Genres with @(
|
||||
Common.SemanticKey : [name],
|
||||
UI.SelectionFields : [name],
|
||||
UI.LineItem : [
|
||||
{ Value: name, Label: '{i18n>Name}' },
|
||||
],
|
||||
);
|
||||
|
||||
// Details
|
||||
annotate Genres with @(UI : {
|
||||
Identification : [{ Value: name }],
|
||||
HeaderInfo : {
|
||||
TypeName : '{i18n>Genre}',
|
||||
TypeNamePlural : '{i18n>Genres}',
|
||||
Title : { Value: name },
|
||||
Description : { Value: ID }
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
// Tree Views
|
||||
// annotate AdminService.Genres with @hierarchy; // upcomming simplification
|
||||
using from './tree-view';
|
||||
using from './value-help';
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
using { AdminService } from '@capire/bookstore';
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Genres Tree View
|
||||
//
|
||||
|
||||
// Tell Fiori about the structure of the hierarchy
|
||||
annotate AdminService.Genres with @Aggregation.RecursiveHierarchy #GenresHierarchy : {
|
||||
ParentNavigationProperty : parent, // navigates to a node's parent
|
||||
NodeProperty : ID, // identifies a node, usually the key
|
||||
};
|
||||
|
||||
// Fiori expects the following to be defined explicitly, even though they're always the same
|
||||
extend AdminService.Genres with @(
|
||||
// The columns expected by Fiori to be present in hierarchy entities
|
||||
Hierarchy.RecursiveHierarchy #GenresHierarchy : {
|
||||
LimitedDescendantCount : LimitedDescendantCount,
|
||||
DistanceFromRoot : DistanceFromRoot,
|
||||
DrillState : DrillState,
|
||||
LimitedRank : LimitedRank
|
||||
},
|
||||
// Disallow filtering on these properties from Fiori UIs
|
||||
Capabilities.FilterRestrictions.NonFilterableProperties: [
|
||||
'LimitedDescendantCount',
|
||||
'DistanceFromRoot',
|
||||
'DrillState',
|
||||
'LimitedRank'
|
||||
],
|
||||
// Disallow sorting on these properties from Fiori UIs
|
||||
Capabilities.SortRestrictions.NonSortableProperties : [
|
||||
'LimitedDescendantCount',
|
||||
'DistanceFromRoot',
|
||||
'DrillState',
|
||||
'LimitedRank'
|
||||
],
|
||||
) columns { // Ensure we can query these fields from database
|
||||
null as LimitedDescendantCount : Int16,
|
||||
null as DistanceFromRoot : Int16,
|
||||
null as DrillState : String,
|
||||
null as LimitedRank : Int16,
|
||||
};
|
||||
@@ -1,6 +0,0 @@
|
||||
// Value help with Tree View
|
||||
using from '../admin-books/fiori-service';
|
||||
annotate AdminService.Books:genre with @Common.ValueList.PresentationVariantQualifier: 'VH';
|
||||
annotate AdminService.Genres with @UI.PresentationVariant #VH: {
|
||||
RecursiveHierarchyQualifier : 'GenresHierarchy',
|
||||
};
|
||||
@@ -1,3 +1,7 @@
|
||||
sap.ui.define(["sap/fe/core/AppComponent"], ac => ac.extend("genres.Component", {
|
||||
metadata:{ manifest:'json' }
|
||||
}))
|
||||
sap.ui.define(["sap/suite/ui/generic/template/lib/AppComponent"], (AppComponent) =>
|
||||
AppComponent.extend("genres.Component", {
|
||||
metadata: {
|
||||
manifest: "json",
|
||||
},
|
||||
})
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#XTIT
|
||||
appTitle=Manage Genres
|
||||
appTitle=Genres
|
||||
#XTXT
|
||||
appDescription=Genres as Tree View
|
||||
appDescription=Browse Genres
|
||||
|
||||
@@ -1,2 +0,0 @@
|
||||
appTitle=Zeige Genres
|
||||
appDescription=Genres als Baumansicht
|
||||
@@ -1,124 +1,155 @@
|
||||
{
|
||||
"_version": "1.8.0",
|
||||
"sap.app": {
|
||||
"id": "genres",
|
||||
"type": "application",
|
||||
"title": "{{appTitle}}",
|
||||
"description": "{{appDescription}}",
|
||||
"applicationVersion": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"dataSources": {
|
||||
"AdminService": {
|
||||
"uri": "admin/",
|
||||
"type": "OData",
|
||||
"settings": {
|
||||
"odataVersion": "4.0"
|
||||
}
|
||||
}
|
||||
},
|
||||
"crossNavigation": {
|
||||
"inbounds": {
|
||||
"Genres-manage": {
|
||||
"signature": {
|
||||
"parameters": {},
|
||||
"additionalParameters": "allowed"
|
||||
},
|
||||
"semanticObject": "Genres",
|
||||
"action": "manage"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sap.ui5": {
|
||||
"dependencies": {
|
||||
"minUI5Version": "1.122.0",
|
||||
"libs": {
|
||||
"sap.fe.templates": {}
|
||||
}
|
||||
},
|
||||
"models": {
|
||||
"i18n": {
|
||||
"type": "sap.ui.model.resource.ResourceModel",
|
||||
"uri": "i18n/i18n.properties"
|
||||
},
|
||||
"": {
|
||||
"dataSource": "AdminService",
|
||||
"settings": {
|
||||
"synchronizationMode": "None",
|
||||
"operationMode": "Server",
|
||||
"autoExpandSelect": true,
|
||||
"earlyRequests": true,
|
||||
"groupProperties": {
|
||||
"default": {
|
||||
"submit": "Auto"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"routing": {
|
||||
"routes": [
|
||||
{
|
||||
"pattern": ":?query:",
|
||||
"name": "GenresList",
|
||||
"target": "GenresList"
|
||||
},
|
||||
{
|
||||
"pattern": "Genres({key}):?query:",
|
||||
"name": "GenresDetails",
|
||||
"target": "GenresDetails"
|
||||
}
|
||||
],
|
||||
"targets": {
|
||||
"GenresList": {
|
||||
"type": "Component",
|
||||
"id": "GenresList",
|
||||
"name": "sap.fe.templates.ListReport",
|
||||
"options": {
|
||||
"settings": {
|
||||
"contextPath": "/Genres",
|
||||
"navigation": {
|
||||
"Genres": {
|
||||
"detail": {
|
||||
"route": "GenresDetails"
|
||||
}
|
||||
}
|
||||
},
|
||||
"controlConfiguration": {
|
||||
"@com.sap.vocabularies.UI.v1.LineItem": {
|
||||
"tableSettings": {
|
||||
"hierarchyQualifier": "GenresHierarchy",
|
||||
"type": "TreeTable"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"GenresDetails": {
|
||||
"type": "Component",
|
||||
"id": "GenresDetails",
|
||||
"name": "sap.fe.templates.ObjectPage",
|
||||
"options": {
|
||||
"settings": {
|
||||
"contextPath": "/Genres"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contentDensities": {
|
||||
"compact": true,
|
||||
"cozy": true
|
||||
}
|
||||
},
|
||||
"sap.ui": {
|
||||
"technology": "UI5",
|
||||
"fullWidth": false
|
||||
},
|
||||
"sap.fiori": {
|
||||
"registrationIds": [],
|
||||
"archeType": "transactional"
|
||||
}
|
||||
}
|
||||
"_version": "1.8.0",
|
||||
"sap.app": {
|
||||
"id": "genres",
|
||||
"type": "application",
|
||||
"i18n": "i18n/i18n.properties",
|
||||
"applicationVersion": {
|
||||
"version": "1.0.0"
|
||||
},
|
||||
"title": "Browse Genres Hierarchy (OData v2)",
|
||||
"description": "{{appDescription}}",
|
||||
"tags": {
|
||||
"keywords": []
|
||||
},
|
||||
"crossNavigation": {
|
||||
"inbounds": {
|
||||
"appShow": {
|
||||
"title": "{{appTitle}}",
|
||||
"semanticObject": "GenreHierarchy",
|
||||
"action": "display",
|
||||
"deviceTypes": {
|
||||
"desktop": true,
|
||||
"tablet": true,
|
||||
"phone": true
|
||||
},
|
||||
"icon": "sap-icon://settings",
|
||||
"size": "1x1"
|
||||
}
|
||||
},
|
||||
"outbounds": {}
|
||||
},
|
||||
"ach": "",
|
||||
"resources": "resources.json",
|
||||
"dataSources": {
|
||||
"main": {
|
||||
"uri": "/odata/v2/browse",
|
||||
"type": "OData",
|
||||
"settings": {
|
||||
"annotations": ["localAnnotations"],
|
||||
"localUri": "localService/metadata.xml"
|
||||
}
|
||||
},
|
||||
"localAnnotations": {
|
||||
"type": "ODataAnnotation",
|
||||
"uri": "annotations/localAnnotations.xml",
|
||||
"settings": {
|
||||
"localUri": "annotations/localAnnotations.xml"
|
||||
}
|
||||
}
|
||||
},
|
||||
"offline": false,
|
||||
"sourceTemplate": {
|
||||
"id": "ui5template.smartTemplate",
|
||||
"version": "1.40.12"
|
||||
}
|
||||
},
|
||||
"sap.ui": {
|
||||
"technology": "UI5",
|
||||
"icons": {
|
||||
"icon": "",
|
||||
"favIcon": "",
|
||||
"phone": "",
|
||||
"phone@2": "",
|
||||
"tablet": "",
|
||||
"tablet@2": ""
|
||||
},
|
||||
"deviceTypes": {
|
||||
"desktop": true,
|
||||
"tablet": true,
|
||||
"phone": true
|
||||
},
|
||||
"supportedThemes": ["sap_hcb", "sap_belize", "sap_belize_deep", "sap_fiori_3"]
|
||||
},
|
||||
"sap.ui5": {
|
||||
"resources": {
|
||||
"js": [],
|
||||
"css": []
|
||||
},
|
||||
"dependencies": {
|
||||
"minUI5Version": "1.65.6",
|
||||
"libs": {},
|
||||
"components": {}
|
||||
},
|
||||
"models": {
|
||||
"i18n": {
|
||||
"type": "sap.ui.model.resource.ResourceModel",
|
||||
"uri": "i18n/i18n.properties"
|
||||
},
|
||||
"@i18n": {
|
||||
"type": "sap.ui.model.resource.ResourceModel",
|
||||
"uri": "i18n/i18n.properties"
|
||||
},
|
||||
"json": {
|
||||
"type": "sap.ui.model.json.JSONModel"
|
||||
},
|
||||
"i18n|sap.suite.ui.generic.template.ListReport|Genres": {
|
||||
"type": "sap.ui.model.resource.ResourceModel",
|
||||
"uri": "i18n/ListReport/Genres/i18n.properties"
|
||||
},
|
||||
"": {
|
||||
"dataSource": "main",
|
||||
"preload": true,
|
||||
"settings": {
|
||||
"useBatch": true,
|
||||
"defaultBindingMode": "TwoWay",
|
||||
"defaultCountMode": "Inline",
|
||||
"refreshAfterChange": true,
|
||||
"metadataUrlParams": {
|
||||
"sap-value-list": "none"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"contentDensities": {
|
||||
"compact": true,
|
||||
"cozy": true
|
||||
}
|
||||
},
|
||||
"sap.ui.generic.app": {
|
||||
"_version": "1.3.0",
|
||||
"settings": {
|
||||
"forceGlobalRefresh": false,
|
||||
"useColumnLayoutForSmartForm": false,
|
||||
"showBasicSearch": false
|
||||
},
|
||||
"pages": {
|
||||
"ListReport|Genres": {
|
||||
"entitySet": "GenreHierarchy",
|
||||
"component": {
|
||||
"name": "sap.suite.ui.generic.template.ListReport",
|
||||
"list": true,
|
||||
"settings": {
|
||||
"condensedTableLayout": true,
|
||||
"smartVariantManagement": true,
|
||||
"tableType": "TreeTable",
|
||||
"enableTableFilterInPageVariant": true,
|
||||
"dataLoadSettings": {
|
||||
"loadDataOnAppLaunch": "always"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"sap.fiori": {
|
||||
"registrationIds": [],
|
||||
"archeType": "transactional"
|
||||
},
|
||||
"sap.platform.hcp": {
|
||||
"uri": ""
|
||||
},
|
||||
"sap.platform.cf": {
|
||||
"oAuthScopes": []
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,6 +6,5 @@ using from './admin-authors/fiori-service';
|
||||
using from './admin-books/fiori-service';
|
||||
using from './browse/fiori-service';
|
||||
using from './genres/fiori-service';
|
||||
|
||||
using from './common';
|
||||
using from '@capire/bookstore/srv/mashup';
|
||||
|
||||
14
fiori/db/common.cds
Normal file
14
fiori/db/common.cds
Normal file
@@ -0,0 +1,14 @@
|
||||
namespace sap.capire.bookshop;
|
||||
|
||||
using { sap.capire.bookshop } from '@capire/bookstore/srv/mashup';
|
||||
|
||||
entity GenreHierarchy : bookshop.Genres {
|
||||
hierarchyLevel : Integer default 0;
|
||||
drillState : String default 'leaf';
|
||||
parent : Association to GenreHierarchy;
|
||||
children : Composition of many GenreHierarchy on children.parent = $self;
|
||||
}
|
||||
|
||||
extend service CatalogService with {
|
||||
@readonly entity GenreHierarchy as projection on bookshop.GenreHierarchy;
|
||||
}
|
||||
16
fiori/db/data/sap.capire.bookshop-GenreHierarchy.csv
Normal file
16
fiori/db/data/sap.capire.bookshop-GenreHierarchy.csv
Normal file
@@ -0,0 +1,16 @@
|
||||
ID;parent_ID;name;hierarchyLevel;drillState
|
||||
10;;Fiction;0;expanded
|
||||
11;10;Drama;1;leaf
|
||||
12;10;Poetry;1;leaf
|
||||
13;10;Fantasy;1;leaf
|
||||
14;10;Science Fiction;1;leaf
|
||||
15;10;Romance;1;leaf
|
||||
16;10;Mystery;1;leaf
|
||||
17;10;Thriller;1;leaf
|
||||
18;10;Dystopia;1;leaf
|
||||
20;;Non-Fiction;0;expanded
|
||||
19;10;Fairy Tale;1;leaf
|
||||
21;20;Biography;1;expanded
|
||||
22;21;Autobiography;2;leaf
|
||||
23;20;Essay;1;leaf
|
||||
24;20;Speech;1;leaf
|
||||
|
@@ -4,10 +4,11 @@
|
||||
"dependencies": {
|
||||
"@capire/bookstore": "*",
|
||||
"@sap/cds": ">=5",
|
||||
"@cap-js-community/odata-v2-adapter": "^1",
|
||||
"express": "^4.17.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@cap-js/sqlite": ">=1"
|
||||
"@cap-js/sqlite": "^1"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cds-serve",
|
||||
@@ -23,8 +24,20 @@
|
||||
"kind": "odata",
|
||||
"model": "@capire/orders"
|
||||
},
|
||||
"messaging": true,
|
||||
"db": true,
|
||||
"messaging": {
|
||||
"[production]": {
|
||||
"kind": "enterprise-messaging"
|
||||
},
|
||||
"[development]": {
|
||||
"kind": "file-based-messaging"
|
||||
},
|
||||
"[hybrid]": {
|
||||
"kind": "enterprise-messaging-shared"
|
||||
}
|
||||
},
|
||||
"db": {
|
||||
"kind": "sql"
|
||||
},
|
||||
"db-ext": {
|
||||
"[development]": {
|
||||
"model": "db/sqlite"
|
||||
@@ -33,6 +46,9 @@
|
||||
"model": "db/hana"
|
||||
}
|
||||
}
|
||||
},
|
||||
"hana": {
|
||||
"deploy-format": "hdbtable"
|
||||
}
|
||||
},
|
||||
"sapux": [
|
||||
|
||||
@@ -1,50 +0,0 @@
|
||||
const cds = require('@sap/cds/lib')
|
||||
|
||||
// PoC for simplified Fiori Tree Views
|
||||
cds.on('compile.for.runtime', csn => {
|
||||
for (let each of cds.linked(csn).definitions) {
|
||||
if (each.is_entity && each._service && each['@hierarchy']) _hierarchy (each)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
const _hierarchy = entity => {
|
||||
|
||||
// Add annotations explaining the hierarchy structure to Fiori
|
||||
const Qualifier = entity.name.slice (entity._service.name.length+1) + 'Hierarchy'
|
||||
const parent = _parent4(entity)
|
||||
entity[`@Aggregation.RecursiveHierarchy#${Qualifier}.ParentNavigationProperty`] ??= {'=': parent.name }
|
||||
entity[`@Aggregation.RecursiveHierarchy#${Qualifier}.NodeProperty`] ??= {'=': parent.keys[0].ref[0] }
|
||||
|
||||
// Add expected hierarchy elements to the entity
|
||||
const columns = entity.projection.columns ??= ['*']
|
||||
const elements = entity.elements
|
||||
for (let e of Hierarchy.elements) {
|
||||
entity[`@Hierarchy.RecursiveHierarchy#${Qualifier}.${e.name}`] = {'=': e.name }
|
||||
if (e.name in elements) continue
|
||||
const { name, value, ...rest } = e
|
||||
elements[e.name] = Object.defineProperty ({ __proto__:e, ...rest }, 'parent', { value: entity })
|
||||
columns.push ({ ...value, as: name, cast: { type: e.type } })
|
||||
}
|
||||
|
||||
// Disable filter and sort for hierarchy elements
|
||||
entity['@Capabilities.FilterRestrictions.NonFilterableProperties'] =
|
||||
entity['@Capabilities.SortRestrictions.NonSortableProperties'] =
|
||||
Object.keys (Hierarchy.elements)
|
||||
}
|
||||
|
||||
|
||||
const _parent4 = entity => {
|
||||
const parent = entity['@hierarchy.parent'] || entity['@hierarchy.via']
|
||||
if (parent) return entity.elements [parent['=']||parent]
|
||||
else for (let e of entity.elements) // use first recursive uplink association
|
||||
if (e.is2one && e._target === entity) return e
|
||||
}
|
||||
|
||||
|
||||
const { Hierarchy } = cds.linked `aspect Hierarchy {
|
||||
LimitedDescendantCount : Int16 = null;
|
||||
DistanceFromRoot : Int16 = null;
|
||||
DrillState : String = null;
|
||||
LimitedRank : Int16 = null;
|
||||
}`.definitions
|
||||
15
hello/README.md
Normal file
15
hello/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# Hello World Getting Started Sample
|
||||
|
||||
## Next Steps
|
||||
|
||||
- To run the JavaScript implementation, open a new terminal and run `cds watch`.
|
||||
- To run the TypeScript implementation, open a new terminal and run `cds-ts watch`.
|
||||
|
||||
Then call the service at: http://localhost:4004/say/hello(to='world')
|
||||
|
||||
## Learn More
|
||||
|
||||
Learn more about:
|
||||
|
||||
- [Hello World!](https://cap.cloud.sap/docs/get-started/hello-world)
|
||||
- [Using TypeScript](https://cap.cloud.sap/docs/node.js/typescript)
|
||||
12
hello/package.json
Normal file
12
hello/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@capire/hello-world",
|
||||
"version": "1.0.0",
|
||||
"scripts": {
|
||||
"test": "npx jest --silent",
|
||||
"start": "cds-serve srv/world.cds",
|
||||
"start:ts": "cds-ts serve srv/world.cds"
|
||||
},
|
||||
"dependencies": {
|
||||
"@sap/cds": ">=5.0.4"
|
||||
}
|
||||
}
|
||||
3
hello/srv/world.cds
Normal file
3
hello/srv/world.cds
Normal file
@@ -0,0 +1,3 @@
|
||||
service say {
|
||||
function hello (to:String) returns String;
|
||||
}
|
||||
7
hello/srv/world.js
Normal file
7
hello/srv/world.js
Normal file
@@ -0,0 +1,7 @@
|
||||
module.exports = class say {
|
||||
hello(req) {
|
||||
let {to} = req.data
|
||||
if (to === 'me') to = require('os').userInfo().username
|
||||
return `Hello ${to}!`
|
||||
}
|
||||
}
|
||||
7
hello/srv/world.ts
Normal file
7
hello/srv/world.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import { Request } from "@sap/cds"
|
||||
|
||||
module.exports = class say {
|
||||
hello(req: Request) {
|
||||
return `Hello ${req.data.to} from a TypeScript file!`
|
||||
}
|
||||
}
|
||||
5
hello/test/test.http
Normal file
5
hello/test/test.http
Normal file
@@ -0,0 +1,5 @@
|
||||
GET http://localhost:4004/odata/v4/say/hello
|
||||
###
|
||||
|
||||
GET http://localhost:4004/odata/v4/say/hello(to='me')
|
||||
###
|
||||
@@ -1,3 +0,0 @@
|
||||
export default {
|
||||
silent: true
|
||||
}
|
||||
13
media/db/data-model.cds
Normal file
13
media/db/data-model.cds
Normal file
@@ -0,0 +1,13 @@
|
||||
namespace sap.capire.media;
|
||||
|
||||
entity Media {
|
||||
|
||||
key id:Integer;
|
||||
@Core.MediaType: mediaType
|
||||
content : LargeBinary ;
|
||||
|
||||
@Core.IsMediaType: true
|
||||
mediaType : String;
|
||||
fileName : String;
|
||||
applicationName:String;
|
||||
}
|
||||
2
media/index.cds
Normal file
2
media/index.cds
Normal file
@@ -0,0 +1,2 @@
|
||||
using from './db/data-model';
|
||||
using from './srv/media-service';
|
||||
19
media/package.json
Normal file
19
media/package.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "@capire/media",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"lokijs": "^1.5.6"
|
||||
},
|
||||
"files": [
|
||||
"db",
|
||||
"srv",
|
||||
"index.cds"
|
||||
],
|
||||
"cds": {
|
||||
"requires": {
|
||||
"db": {
|
||||
"kind": "sql"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
9
media/srv/media-service.cds
Normal file
9
media/srv/media-service.cds
Normal file
@@ -0,0 +1,9 @@
|
||||
using { sap.capire.media as db } from '../db/data-model';
|
||||
namespace sap.capire.media;
|
||||
|
||||
@path: '/media-server'
|
||||
service MediaServer {
|
||||
entity Media as projection on db.Media ;
|
||||
}
|
||||
|
||||
|
||||
68
media/srv/media-service.js
Normal file
68
media/srv/media-service.js
Normal file
@@ -0,0 +1,68 @@
|
||||
const loki = require('lokijs')
|
||||
const db = new loki('DB')
|
||||
const mediaDB = db.addCollection('Media')
|
||||
const { Readable, PassThrough } = require('stream')
|
||||
|
||||
module.exports = srv => {
|
||||
srv.before('CREATE', 'Media', req => {
|
||||
const obj = mediaDB.insert({ media: '' })
|
||||
req.data.id = obj.$loki
|
||||
})
|
||||
|
||||
srv.on('UPDATE', 'Media', (req, next) => {
|
||||
const url = req.path
|
||||
if (url.includes('content')) {
|
||||
const id = req.data.id
|
||||
const obj = mediaDB.get(id)
|
||||
if (!obj) {
|
||||
req.reject(404, 'No record found for the ID')
|
||||
return
|
||||
}
|
||||
const stream = new PassThrough()
|
||||
const chunks = []
|
||||
stream.on('data', chunk => {
|
||||
chunks.push(chunk)
|
||||
})
|
||||
stream.on('end', () => {
|
||||
obj.media = Buffer.concat(chunks).toString('base64')
|
||||
mediaDB.update(obj)
|
||||
})
|
||||
req.data.content.pipe(stream)
|
||||
} else return next()
|
||||
})
|
||||
|
||||
srv.on('READ', 'Media', (req, next) => {
|
||||
const url = req.path
|
||||
if (url.includes('content')) {
|
||||
const id = req.data.id
|
||||
const mediaObj = mediaDB.get(id)
|
||||
if (!mediaObj) {
|
||||
req.reject(404, 'Media not found for the ID')
|
||||
return
|
||||
}
|
||||
const decodedMedia = Buffer.from(
|
||||
mediaObj.media.split(';base64,').pop(),
|
||||
'base64'
|
||||
)
|
||||
return _formatResult(decodedMedia)
|
||||
} else return next() //> delegate to next/default handlers
|
||||
})
|
||||
|
||||
srv.on('DELETE', 'Media', (req, next) => {
|
||||
const id = req.data.id
|
||||
mediaDB
|
||||
.chain()
|
||||
.find({ $loki: id })
|
||||
.remove()
|
||||
return next() //> delegate to next/default handlers
|
||||
})
|
||||
|
||||
function _formatResult (decodedMedia) {
|
||||
const readable = new Readable()
|
||||
const result = new Array()
|
||||
readable.push(decodedMedia)
|
||||
readable.push(null)
|
||||
result.push({ value: readable })
|
||||
return result
|
||||
}
|
||||
}
|
||||
BIN
media/test/Test.png
Normal file
BIN
media/test/Test.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.0 KiB |
35
media/test/media.http
Normal file
35
media/test/media.http
Normal file
@@ -0,0 +1,35 @@
|
||||
### Requires REST Client for VS Code
|
||||
### https://marketplace.visualstudio.com/items?itemName=humao.rest-client
|
||||
###
|
||||
@protocol = http
|
||||
@host = localhost
|
||||
@port = 4004
|
||||
### Read Pictures
|
||||
GET {{protocol}}://{{host}}:{{port}}/media-server/Media
|
||||
Authorization: Basic admin:
|
||||
|
||||
### Create Picture with mediatype
|
||||
POST {{protocol}}://{{host}}:{{port}}/media-server/Media
|
||||
Authorization: Basic admin:
|
||||
Accept: application/json
|
||||
Content-Type: application/json
|
||||
|
||||
{
|
||||
"id": 1,
|
||||
"mediaType": "image/png"
|
||||
}
|
||||
|
||||
### Upload Binary PNG
|
||||
PUT {{protocol}}://{{host}}:{{port}}/media-server/Media(1)/content
|
||||
Authorization: Basic admin:
|
||||
Content-Type: image/png
|
||||
|
||||
< ./Test.png
|
||||
|
||||
### Read Binary
|
||||
GET {{protocol}}://{{host}}:{{port}}/media-server/Media(1)/content
|
||||
Authorization: Basic admin:
|
||||
|
||||
### Delete Image
|
||||
DELETE {{protocol}}://{{host}}:{{port}}/media-server/Media(1)
|
||||
Authorization: Basic admin:
|
||||
178
mta.yaml
178
mta.yaml
@@ -1,178 +0,0 @@
|
||||
_schema-version: 3.3.0
|
||||
ID: capire.samples
|
||||
version: 2.1.0
|
||||
description: "A monorepo with several samples for CAP."
|
||||
parameters:
|
||||
enable-parallel-deployments: true
|
||||
build-parameters:
|
||||
before-all:
|
||||
- builder: custom
|
||||
commands:
|
||||
- npm ci
|
||||
- npx cds build shared-db --for hana --production
|
||||
- npx cds build orders --for nodejs --production --ws-pack
|
||||
- npx cds build reviews --for nodejs --production
|
||||
- npx cds build bookstore --for nodejs --production --ws-pack
|
||||
modules:
|
||||
- name: orders-srv
|
||||
type: nodejs
|
||||
path: orders/gen/srv
|
||||
parameters:
|
||||
buildpack: nodejs_buildpack
|
||||
readiness-health-check-type: http
|
||||
readiness-health-check-http-endpoint: /health
|
||||
disk-quota: 256M
|
||||
memory: 256M
|
||||
build-parameters:
|
||||
builder: npm
|
||||
provides:
|
||||
- name: orders-api
|
||||
properties:
|
||||
srv-url: ${default-url}
|
||||
requires:
|
||||
- name: samples-messaging
|
||||
- name: samples-db
|
||||
- name: samples-auth
|
||||
|
||||
- name: reviews-srv
|
||||
type: nodejs
|
||||
path: reviews/gen/srv
|
||||
parameters:
|
||||
buildpack: nodejs_buildpack
|
||||
readiness-health-check-type: http
|
||||
readiness-health-check-http-endpoint: /health
|
||||
disk-quota: 256M
|
||||
memory: 256M
|
||||
build-parameters:
|
||||
builder: npm
|
||||
provides:
|
||||
- name: reviews-api
|
||||
properties:
|
||||
srv-url: ${default-url}
|
||||
requires:
|
||||
- name: samples-messaging
|
||||
- name: samples-db
|
||||
- name: samples-auth
|
||||
|
||||
- name: bookstore-srv
|
||||
type: nodejs
|
||||
path: bookstore/gen/srv
|
||||
parameters:
|
||||
buildpack: nodejs_buildpack
|
||||
readiness-health-check-type: http
|
||||
readiness-health-check-http-endpoint: /health
|
||||
disk-quota: 256M
|
||||
memory: 256M
|
||||
properties:
|
||||
cds_requires_ReviewsService_credentials: {"destination": "reviews-dest","path": "/reviews"}
|
||||
cds_requires_OrdersService_credentials: {"destination": "orders-dest","path": "/odata/v4/orders"}
|
||||
build-parameters:
|
||||
builder: npm
|
||||
provides:
|
||||
- name: bookstore-api
|
||||
properties:
|
||||
srv-url: ${default-url}
|
||||
requires:
|
||||
- name: samples-messaging
|
||||
- name: samples-db
|
||||
- name: samples-auth
|
||||
- name: samples-destination
|
||||
|
||||
- name: samples-db-deployer
|
||||
type: hdb
|
||||
path: shared-db/gen/db
|
||||
parameters:
|
||||
buildpack: nodejs_buildpack
|
||||
requires:
|
||||
- name: samples-db
|
||||
|
||||
- name: samples
|
||||
type: approuter.nodejs
|
||||
path: .deploy/app-router
|
||||
parameters:
|
||||
keep-existing-routes: true
|
||||
disk-quota: 256M
|
||||
memory: 256M
|
||||
requires:
|
||||
- name: orders-api
|
||||
group: destinations
|
||||
properties:
|
||||
name: orders-api # must be used in xs-app.json as well
|
||||
url: ~{srv-url}
|
||||
forwardAuthToken: true
|
||||
- name: reviews-api
|
||||
group: destinations
|
||||
properties:
|
||||
name: reviews-api # must be used in xs-app.json as well
|
||||
url: ~{srv-url}
|
||||
forwardAuthToken: true
|
||||
- name: bookstore-api
|
||||
group: destinations
|
||||
properties:
|
||||
name: bookstore-api # must be used in xs-app.json as well
|
||||
url: ~{srv-url}
|
||||
forwardAuthToken: true
|
||||
- name: samples-auth
|
||||
|
||||
- name: destination-content
|
||||
type: com.sap.application.content
|
||||
requires:
|
||||
- name: orders-api
|
||||
- name: reviews-api
|
||||
- name: bookstore-api
|
||||
- name: samples-auth
|
||||
parameters:
|
||||
service-key:
|
||||
name: xsuaa-service-key
|
||||
- name: samples-destination
|
||||
parameters:
|
||||
content-target: true
|
||||
build-parameters:
|
||||
no-source: true
|
||||
parameters:
|
||||
content:
|
||||
instance:
|
||||
existing_destinations_policy: update
|
||||
destinations:
|
||||
- Name: orders-dest
|
||||
URL: ~{orders-api/srv-url}
|
||||
Authentication: OAuth2ClientCredentials
|
||||
TokenServiceInstanceName: samples-auth
|
||||
TokenServiceKeyName: xsuaa-service-key
|
||||
- Name: reviews-dest
|
||||
URL: ~{reviews-api/srv-url}
|
||||
Authentication: OAuth2ClientCredentials
|
||||
TokenServiceInstanceName: samples-auth
|
||||
TokenServiceKeyName: xsuaa-service-key
|
||||
|
||||
resources:
|
||||
- name: samples-messaging
|
||||
type: org.cloudfoundry.managed-service
|
||||
parameters:
|
||||
service: enterprise-messaging
|
||||
service-plan: default
|
||||
path: ./event-mesh.json
|
||||
config:
|
||||
emname: bookstore-${org}-${space}
|
||||
namespace: cap/samples/${space}
|
||||
- name: samples-db
|
||||
type: com.sap.xs.hdi-container
|
||||
parameters:
|
||||
service: hana
|
||||
service-plan: hdi-shared
|
||||
- name: samples-auth
|
||||
type: org.cloudfoundry.managed-service
|
||||
processed-after:
|
||||
- samples-messaging
|
||||
parameters:
|
||||
service: xsuaa
|
||||
service-plan: application
|
||||
path: ./xs-security.json
|
||||
config:
|
||||
xsappname: samples-${org}-${space}
|
||||
tenant-mode: dedicated
|
||||
- name: samples-destination
|
||||
type: org.cloudfoundry.managed-service
|
||||
parameters:
|
||||
service: destination
|
||||
service-plan: lite
|
||||
1
ord/cds-plugin.js
Normal file
1
ord/cds-plugin.js
Normal file
@@ -0,0 +1 @@
|
||||
// just a dummy tag file to be identified as a plugin
|
||||
12
ord/package.json
Normal file
12
ord/package.json
Normal file
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"name": "@cap-js/ord",
|
||||
"version": "2.0.0",
|
||||
"cds": {
|
||||
"requires": {
|
||||
"SAP ORD Service": {
|
||||
"model": "@cap-js/ord/srv/ord-service",
|
||||
"service": "OrdService"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
12
ord/srv/ord-service.cds
Normal file
12
ord/srv/ord-service.cds
Normal file
@@ -0,0 +1,12 @@
|
||||
// @requires: 'ORDconsumer'
|
||||
@rest @path:'/ord/v1'
|
||||
service OrdService {
|
||||
@readonly entity documents {
|
||||
key id: String;
|
||||
}
|
||||
@readonly entity csn {
|
||||
key id: String;
|
||||
}
|
||||
|
||||
function api (service: String, format: String) returns {};
|
||||
}
|
||||
73
ord/srv/ord-service.mjs
Normal file
73
ord/srv/ord-service.mjs
Normal file
@@ -0,0 +1,73 @@
|
||||
import cds from '@sap/cds'
|
||||
export class OrdService extends cds.ApplicationService {
|
||||
init(){
|
||||
|
||||
this.on('READ','documents', req => {
|
||||
let csn = cds.context?.model || cds.model
|
||||
return { ord: csn }
|
||||
})
|
||||
|
||||
/**
|
||||
* Just an example to do something with id, if given.
|
||||
* Try it out with URLs like that:
|
||||
*
|
||||
* - http://localhost:4004/ord/v1/documents
|
||||
* - http://localhost:4004/ord/v1/documents/CatalogService
|
||||
* - http://localhost:4004/ord/v1/documents/CatalogService.Books
|
||||
* - http://localhost:4004/ord/v1/documents/CatalogService.Authors
|
||||
*/
|
||||
this.on('READ','csn', req => {
|
||||
let csn = cds.context?.model || cds.model
|
||||
let { id } = req.data
|
||||
if (id) csn = csn.definitions[id] || 'not in model!'
|
||||
return { id, csn }
|
||||
})
|
||||
|
||||
/**
|
||||
* Just an example to serve arbitrary content with a function.
|
||||
* Try it out with URLs like that:
|
||||
*
|
||||
* - http://localhost:4004/ord/v1/api?service=CatalogService
|
||||
* - http://localhost:4004/ord/v1/api?service=CatalogService&format=edmx
|
||||
* - http://localhost:4004/ord/v1/api?service=CatalogService&format=edmx-v2
|
||||
* - http://localhost:4004/ord/v1/api?service=CatalogService&format=openapi
|
||||
*/
|
||||
this.on('api', req => {
|
||||
let csn = cds.context?.model || cds.model
|
||||
let { service, format = 'csn' } = req.data
|
||||
let { res } = req.http
|
||||
if (format === 'csn') {
|
||||
if (!service) return res.send(csn)
|
||||
service = csn.services[service]
|
||||
return res.send({ definitions: [ service, ...service.entities ] .reduce ((all,e) => {
|
||||
let d = all[e.name] = {...e}
|
||||
delete d.projection // not part of the API
|
||||
delete d.query // not part of the API
|
||||
return all
|
||||
},{})})
|
||||
}
|
||||
let api = cds.compile(csn).to[format]({service})
|
||||
return res.send(api)
|
||||
})
|
||||
|
||||
/**
|
||||
* Example how to register arbitrary express routes,
|
||||
* and map them to our service's interface.
|
||||
* Try it out with URLs like that:
|
||||
*
|
||||
* - http://localhost:4004/ord/v1/csn/CatalogService
|
||||
* - http://localhost:4004/ord/v1/edmx/CatalogService
|
||||
* - http://localhost:4004/ord/v1/openapi/CatalogService
|
||||
* - http://localhost:4004/ord/v1/asyncapi/CatalogService
|
||||
*
|
||||
* NOTE: we add cds.middlewares.before to the route, which gives us all
|
||||
* the context and auth handling, which is also available to CAP services.
|
||||
*/
|
||||
cds.app.get (`${this.path}/:api?/:service?`, cds.middlewares.before, req => {
|
||||
const { api, service } = req.params
|
||||
return this.api (service, api)
|
||||
})
|
||||
|
||||
return super.init()
|
||||
}
|
||||
}
|
||||
@@ -1 +1,2 @@
|
||||
cds.requires.messaging.kind = file-based-messaging
|
||||
PORT = 4006
|
||||
@@ -16,15 +16,15 @@
|
||||
description: "CAP Sample App",
|
||||
additionalInformation: "SAPUI5.Component=orders",
|
||||
applicationType : "URL",
|
||||
url: "webapp",
|
||||
url: "/orders/webapp",
|
||||
navigationMode: "embedded"
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<script id="sap-ushell-bootstrap" src="https://ui5.sap.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
||||
<script id="sap-ui-bootstrap" src="https://ui5.sap.com/resources/sap-ui-core.js"
|
||||
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
|
||||
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
|
||||
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
|
||||
data-sap-ui-compatVersion="edge"
|
||||
data-sap-ui-theme="sap_horizon"
|
||||
|
||||
@@ -2,18 +2,7 @@
|
||||
"name": "@capire/orders",
|
||||
"version": "1.0.0",
|
||||
"dependencies": {
|
||||
"@cap-js/hana": ">=1",
|
||||
"@capire/common": "*",
|
||||
"@sap/cds": ">=5",
|
||||
"@sap/xssec": "^4"
|
||||
},
|
||||
"cds": {
|
||||
"requires": {
|
||||
"messaging": true,
|
||||
"db": true
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cds-serve"
|
||||
"@sap/cds": ">=5"
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,14 +2,4 @@ using { sap.capire.orders as my } from '../db/schema';
|
||||
|
||||
service OrdersService {
|
||||
entity Orders as projection on my.Orders;
|
||||
|
||||
@odata.draft.bypass
|
||||
@(requires: 'system-user')
|
||||
entity OrdersNoDraft as projection on my.Orders;
|
||||
|
||||
event OrderChanged {
|
||||
product: String;
|
||||
deltaQuantity: Integer;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -8,14 +8,18 @@ class OrdersService extends cds.ApplicationService {
|
||||
this.before ('UPDATE', 'Orders', async function(req) {
|
||||
const { ID, Items } = req.data
|
||||
if (Items) for (let { product_ID, quantity } of Items) {
|
||||
const { quantity:before } = await SELECT.one.from (OrderItems, oi => oi.quantity) .where ({up__ID:ID, product_ID})
|
||||
const { quantity:before } = await cds.tx(req).run (
|
||||
SELECT.one.from (OrderItems, oi => oi.quantity) .where ({up__ID:ID, product_ID})
|
||||
)
|
||||
if (quantity != before) await this.orderChanged (product_ID, quantity-before)
|
||||
}
|
||||
})
|
||||
|
||||
this.before ('DELETE', 'Orders', async function(req) {
|
||||
const { ID } = req.data
|
||||
const Items = await SELECT.from (OrderItems, oi => { oi.product_ID, oi.quantity }) .where ({up__ID:ID})
|
||||
const Items = await cds.tx(req).run (
|
||||
SELECT.from (OrderItems, oi => { oi.product_ID, oi.quantity }) .where ({up__ID:ID})
|
||||
)
|
||||
if (Items) await Promise.all (Items.map(it => this.orderChanged (it.product_ID, -it.quantity)))
|
||||
})
|
||||
|
||||
|
||||
5595
package-lock.json
generated
5595
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
54
package.json
54
package.json
@@ -1,38 +1,46 @@
|
||||
{
|
||||
"name": "@capire/samples",
|
||||
"version": "3.0.0",
|
||||
"version": "2.1.0",
|
||||
"description": "A monorepo with several samples for CAP.",
|
||||
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
|
||||
"author": "daniel.hutzel@sap.com",
|
||||
"dependencies": {
|
||||
"@sap/cds": ">=8"
|
||||
},
|
||||
"workspaces": [
|
||||
"bookshop",
|
||||
"bookstore",
|
||||
"common",
|
||||
"fiori",
|
||||
"orders",
|
||||
"reviews",
|
||||
"shared-db",
|
||||
"etc/data-viewer",
|
||||
"etc/loggers"
|
||||
"./bookshop",
|
||||
"./bookstore",
|
||||
"./common",
|
||||
"./data-viewer",
|
||||
"./fiori",
|
||||
"./hello",
|
||||
"./media",
|
||||
"./ord",
|
||||
"./orders",
|
||||
"./loggers",
|
||||
"./reviews"
|
||||
],
|
||||
"devDependencies": {
|
||||
"@cap-js/cds-test": "^0"
|
||||
"@cap-js/cds-types": "^0",
|
||||
"@cap-js/sqlite": "^1",
|
||||
"axios": "^1",
|
||||
"chai": "^4.3.4",
|
||||
"chai-as-promised": "^7.1.1",
|
||||
"chai-subset": "^1.6.0",
|
||||
"eslint": "^9",
|
||||
"semver": "^7"
|
||||
},
|
||||
"scripts": {
|
||||
"start": "cds watch bookshop --open http://localhost:4004",
|
||||
"bookstore": "cds watch bookstore",
|
||||
"bookshop": "cds watch bookshop",
|
||||
"start": "cds watch fiori",
|
||||
"fiori": "cds watch fiori",
|
||||
"orders": "cds watch orders",
|
||||
"reviews": "cds watch reviews",
|
||||
"lint": "npx eslint",
|
||||
"test": "chest test",
|
||||
"jest": "npx jest",
|
||||
"mocha": "npx mocha",
|
||||
"node:test": "node --test",
|
||||
"build": "mbt build -t gen --mtar mta.tar",
|
||||
"deploy": "cf deploy gen/mta.tar",
|
||||
"undeploy": "cf undeploy capire.samples --delete-services --delete-service-keys"
|
||||
"hello": "cds watch hello",
|
||||
"media": "cds watch media",
|
||||
"lint": "eslint",
|
||||
"test": "npx jest --silent",
|
||||
"jest": "npx jest --silent",
|
||||
"mocha": "CDS_TEST_SILENT=y npx mocha",
|
||||
"test:hello": "cd hello && npm test"
|
||||
},
|
||||
"mocha": {
|
||||
"recursive": true,
|
||||
|
||||
57
readme.md
57
readme.md
@@ -1,57 +0,0 @@
|
||||
|
||||
|
||||
# Welcome to cap/samples
|
||||
|
||||
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo).
|
||||
|
||||

|
||||
|
||||
## Get Started
|
||||
|
||||
Assumed you did your [initial setup of CAP Node.js](https://cap.cloud.sap/docs/get-started/#setup), simply copy & paste these lines to a terminal for a jumpstart:
|
||||
|
||||
```sh
|
||||
git clone -q https://github.com/sap-samples/cloud-cap-samples cap/samples
|
||||
cd cap/samples
|
||||
npm install
|
||||
npm test
|
||||
npm start
|
||||
```
|
||||
|
||||
After download and setup this starts the bookshop server and opens a browser window on _http://localhost:4004_ looking like that:
|
||||
|
||||
<p align="center">
|
||||
<img width=480 src="etc/index-html.png" alt="bookshop showing up in browser" />
|
||||
</p>
|
||||
|
||||
Click on the *[/vue](http:/localhost:4004/vue)* link at the top to display the bookshop app (when asked to log in, type `alice` as user and leave the password field blank).
|
||||
|
||||
## Grow as you go...
|
||||
|
||||
After the jumpstart, have a look into the enclosed sub folders/projects, which are:
|
||||
|
||||
- [bookshop](bookshop) – a simplistic [primer app](https://cap.cloud.sap/docs/get-started/in-a-nutshell)
|
||||
- [reviews](reviews) - a generic reuse service
|
||||
- [orders](orders) - a generic reuse service
|
||||
- [common](common) - a reuse content package
|
||||
- [bookstore](bookstore) - a composite app of the above
|
||||
- [fiori](fiori) - Fiori elements UIs for the bookstore
|
||||
- [etc/*](etc) - Plugins adding cross-cutting concerns
|
||||
- [test](test) - Tests for all the above
|
||||
|
||||
> _see also [samples.md](samples.md)_
|
||||
|
||||
<p align="center">
|
||||
<img width=480 src="etc/samples.drawio.svg">
|
||||
</p>
|
||||
|
||||
## Get Help
|
||||
|
||||
- Visit the [*capire* docs](https://cap.cloud.sap) to learn about CAP, ...
|
||||
- especially [*Getting Started in a Nutshell*](https://cap.cloud.sap/docs/get-started/in-a-nutshell).
|
||||
- Visit our [*SAP Community*](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce) to ask questions.
|
||||
|
||||
|
||||
## License
|
||||
|
||||
Copyright (c) 2022 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](LICENSE)_ file.
|
||||
@@ -69,25 +69,3 @@ const reviews = Vue.createApp ({
|
||||
|
||||
// initially fill list of my reviews
|
||||
reviews.fetch()
|
||||
|
||||
axios.interceptors.request.use(csrfToken)
|
||||
function csrfToken (request) {
|
||||
if (request.method === 'head' || request.method === 'get') return request
|
||||
if ('csrfToken' in document) {
|
||||
request.headers['x-csrf-token'] = document.csrfToken
|
||||
return request
|
||||
}
|
||||
return fetchToken().then(token => {
|
||||
document.csrfToken = token
|
||||
request.headers['x-csrf-token'] = document.csrfToken
|
||||
return request
|
||||
}).catch(() => {
|
||||
document.csrfToken = null // set mark to not try again
|
||||
return request
|
||||
})
|
||||
|
||||
function fetchToken() {
|
||||
return axios.get('/', { headers: { 'x-csrf-token': 'fetch' } })
|
||||
.then(res => res.headers['x-csrf-token'])
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
<head>
|
||||
<title> Capire Reviews </title>
|
||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/primitive-ui/dist/css/main.css">
|
||||
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
|
||||
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
|
||||
<script src="https://cdn.jsdelivr.net/npm/vue@3/dist/vue.global.prod.js"></script>
|
||||
<style>
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
ID;subject;rating;reviewer;title;text
|
||||
1689144d-3b10-4849-bcbe-2408a13e161d;201;5;bob;Intriguing;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
539ab728-3068-450f-a617-ed5af9e9dbb7;201;4;bob;Fascinating;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Id diam maecenas ultricies mi eget mauris pharetra et. Risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Pulvinar mattis nunc sed blandit libero. Facilisis magna etiam tempor orci eu. Nec sagittis aliquam malesuada bibendum arcu. Eu consequat ac felis donec. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Tortor posuere ac ut consequat semper viverra nam libero. Amet nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Scelerisque purus semper eget duis at tellus. Elementum tempus egestas sed sed risus pretium. Arcu dictum varius duis at. Amet luctus venenatis lectus magna fringilla urna. Eget velit aliquet sagittis id consectetur purus ut faucibus. Vitae auctor eu augue ut lectus. Fermentum iaculis eu non diam phasellus vestibulum.
|
||||
cd9bfd8d-eab4-40ee-9b46-770302533009;207;2;bob;What is this?;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero justo laoreet sit amet cursus sit amet dictum. Nunc faucibus a pellentesque sit. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Enim nunc faucibus a pellentesque. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Cras ornare arcu dui vivamus. Facilisi etiam dignissim diam quis enim lobortis. Et molestie ac feugiat sed. Urna neque viverra justo nec ultrices dui. Ullamcorper a lacus vestibulum sed arcu non. Volutpat ac tincidunt vitae semper quis. Dignissim sodales ut eu sem. Feugiat in fermentum posuere urna nec. At augue eget arcu dictum varius.
|
||||
f2896a44-637f-4198-a428-c0966d10b7ce;251;3;bob;It's dark...;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit tellus mauris a diam. Velit aliquet sagittis id consectetur purus ut. Viverra adipiscing at in tellus integer. Vitae elementum curabitur vitae nunc. Mattis ullamcorper velit sed ullamcorper morbi. Diam quis enim lobortis scelerisque. Auctor neque vitae tempus quam pellentesque nec nam aliquam. Semper auctor neque vitae tempus. Quis eleifend quam adipiscing vitae proin. Neque convallis a cras semper auctor neque vitae. Imperdiet massa tincidunt nunc pulvinar sapien et ligula. Sit amet consectetur adipiscing elit ut aliquam purus. Pretium quam vulputate dignissim suspendisse.
|
||||
subject;rating;reviewer;title;text
|
||||
201;5;bob;Intriguing;Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est Lorem ipsum dolor sit amet.
|
||||
201;4;bob;Fascinating;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Id diam maecenas ultricies mi eget mauris pharetra et. Risus at ultrices mi tempus imperdiet nulla malesuada pellentesque. Pulvinar mattis nunc sed blandit libero. Facilisis magna etiam tempor orci eu. Nec sagittis aliquam malesuada bibendum arcu. Eu consequat ac felis donec. Ultricies tristique nulla aliquet enim tortor at auctor urna nunc. Tortor posuere ac ut consequat semper viverra nam libero. Amet nisl suscipit adipiscing bibendum est ultricies integer quis auctor. Scelerisque purus semper eget duis at tellus. Elementum tempus egestas sed sed risus pretium. Arcu dictum varius duis at. Amet luctus venenatis lectus magna fringilla urna. Eget velit aliquet sagittis id consectetur purus ut faucibus. Vitae auctor eu augue ut lectus. Fermentum iaculis eu non diam phasellus vestibulum.
|
||||
207;2;bob;What is this?;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Libero justo laoreet sit amet cursus sit amet dictum. Nunc faucibus a pellentesque sit. Dis parturient montes nascetur ridiculus mus mauris vitae ultricies. Enim nunc faucibus a pellentesque. Commodo quis imperdiet massa tincidunt nunc pulvinar sapien. Cras ornare arcu dui vivamus. Facilisi etiam dignissim diam quis enim lobortis. Et molestie ac feugiat sed. Urna neque viverra justo nec ultrices dui. Ullamcorper a lacus vestibulum sed arcu non. Volutpat ac tincidunt vitae semper quis. Dignissim sodales ut eu sem. Feugiat in fermentum posuere urna nec. At augue eget arcu dictum varius.
|
||||
251;3;bob;It's dark...;Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Suscipit tellus mauris a diam. Velit aliquet sagittis id consectetur purus ut. Viverra adipiscing at in tellus integer. Vitae elementum curabitur vitae nunc. Mattis ullamcorper velit sed ullamcorper morbi. Diam quis enim lobortis scelerisque. Auctor neque vitae tempus quam pellentesque nec nam aliquam. Semper auctor neque vitae tempus. Quis eleifend quam adipiscing vitae proin. Neque convallis a cras semper auctor neque vitae. Imperdiet massa tincidunt nunc pulvinar sapien et ligula. Sit amet consectetur adipiscing elit ut aliquam purus. Pretium quam vulputate dignissim suspendisse.
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user