Compare commits

..

15 Commits

Author SHA1 Message Date
Daniel Kurzynski
7333001683 Use version v28 of the pipeline 2020-05-07 16:45:21 +02:00
Daniel Kurzynski
d6e051fcb8 Update README.md 2020-04-06 18:00:46 +02:00
Daniel Kurzynski
059df03cc1 Update README.md 2020-04-06 17:49:19 +02:00
Daniel Kurzynski
669c5249ff Update pipeline_config.yml 2020-04-06 17:45:33 +02:00
Christian Georgi
1a3fb3ffab Add package-lock.json, fix readme 2020-04-02 16:21:34 +02:00
Daniel Kurzynski
f2c458db7a Update README.md 2020-03-27 16:03:45 +01:00
Daniel Kurzynski
5c7a3e6860 Update README.md 2020-03-27 16:03:04 +01:00
Daniel Kurzynski
ece590be9c Create package-lock.json 2020-03-27 15:45:23 +01:00
Daniel Kurzynski
19ba5c2d9c Update README.md 2020-03-27 15:42:22 +01:00
Daniel Kurzynski
3e6a23e43d Delete package-lock.json 2020-03-27 15:40:05 +01:00
Christian Georgi
2857ab7fa0 Update readme 2020-03-23 17:12:08 +01:00
Daniel Kurzynski
ecd2152be5 Update project 2020-02-07 15:29:53 +01:00
Daniel Kurzynski
ce3094a30d Adapt mta file 2020-01-14 11:12:00 +01:00
Daniel Kurzynski
8f0f032e6c Add mta and pipeline 2020-01-14 11:00:53 +01:00
Daniel Kurzynski
7c52a3b4f2 Adopt repository structure 2020-01-14 10:58:42 +01:00
132 changed files with 2878 additions and 3575 deletions

View File

@@ -1,11 +1,9 @@
{
"extends": "eslint:recommended",
"env": {
"browser": true,
"node": true,
"es6": true,
"jest": true,
"mocha": true
"jest": true
},
"parserOptions": {
"ecmaVersion": 2018
@@ -21,7 +19,6 @@
},
"rules": {
"no-console": "off",
"require-atomic-updates": "off",
"require-await":"warn"
"require-atomic-updates": "off"
}
}

View File

@@ -1,10 +0,0 @@
---
name: This channel is CLOSED.
about: Use our community at https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce
title: ''
labels: ''
assignees: ''
---
Please use our community on https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce

View File

@@ -1,28 +0,0 @@
# This workflow will do a clean install of node dependencies, build the source code and run tests across different versions of node
# For more information see: https://help.github.com/actions/language-and-framework-guides/using-nodejs-with-github-actions
name: CI
on:
push:
branches: [ master ]
pull_request:
branches: [ master ]
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [12.x, 14.x]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test

2
.gitignore vendored
View File

@@ -13,5 +13,3 @@ target/
connection.properties
default-env.json
packages/messageBox
reviews/msg-box
reviews/db/test.db

1
.npmrc Normal file
View File

@@ -0,0 +1 @@
@sap:registry=https://npm.sap.com

View File

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

View File

@@ -1,71 +0,0 @@
const { exec } = require ('child_process')
const express = require ('express')
const fs = require ('fs')
const app = express()
const { PORT=4444 } = process.env
const [,,port=PORT] = process.argv
process.chdir(__dirname)
app.use('/-/:tarball', (req,res,next) => {
console.debug ('GET', req.params)
try {
const { tarball } = req.params
const [, pkg ] = /^capire-(\w+)/.exec(tarball)
fs.lstat(tarball,(err => {
if (err) exec(`npm pack ../${pkg}`,next)
else next()
}))
} catch (e) {
console.error(e)
res.sendStatus(500)
}
})
app.use('/-', express.static(__dirname))
app.get('/*', (req,res)=>{
const url = decodeURIComponent(req.url)
console.debug ('GET',url)
try {
const [, capire, pkg ] = /^\/(@capire)\/(\w+)/.exec(url)
const package = require (`${capire}/${pkg}/package.json`)
const tarball = `capire-${pkg}-${package.version}.tgz`
// https://github.com/npm/registry/blob/master/docs/responses/package-metadata.md
res.json({
"name": package.name,
"dist-tags": {
"latest": package.version
},
"versions": {
[package.version]: {
"name": package.name,
"version": package.version,
"dist": {
"tarball": `http://localhost:${port}/-/${tarball}`
},
}
},
})
} catch (e) {
console.error(e)
res.sendStatus(404)
}
})
app.listen(port, ()=>{
console.log (`npm set @capire:registry=http://localhost:${port}`)
console.log (`@capire registry listening on http://localhost:${port}`)
exec(`npm set @capire:registry=http://localhost:${port}`)
})
const _exit = ()=>{
console.log ('\nnpm conf rm @capire:registry')
exec('npm conf rm @capire:registry')
exec('rm *.tgz')
process.exit()
}
process.on ('SIGTERM',_exit)
process.on ('SIGHUP',_exit)
process.on ('SIGINT',_exit)
process.on ('SIGUSR2',_exit)

View File

@@ -1,29 +0,0 @@
Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/
Upstream-Name: cloud-cap-samples
Upstream-Contact: <Christian Georgi (christian.georgi@sap.com)>
Source: https://github.com/SAP-samples/cloud-cap-samples
Disclaimer: The code in this project may include calls to APIs (“API Calls”) of
SAP or third-party products or services developed outside of this project
(“External Products”).
“APIs” means application programming interfaces, as well as their respective
specifications and implementing code that allows software to communicate with
other software.
API Calls to External Products are not licensed under the open source license
that governs this project. The use of such API Calls and related External
Products are subject to applicable additional agreements with the relevant
provider of the External Products. In no event shall the open source license
that governs this project grant any rights in or to any External Products,or
alter, expand or supersede any terms of the applicable additional agreements.
If you have a valid license agreement with SAP for the use of a particular SAP
External Product, then you may make use of any API Calls included in this
projects code for that SAP External Product, subject to the terms of such
license agreement. If you do not have a valid license agreement for the use of
a particular SAP External Product, then you may only make use of any API Calls
in this project for that SAP External Product for your internal, non-productive
and non-commercial test and evaluation of such API Calls. Nothing herein grants
you any rights to use or access any SAP External Product, or provide any third
parties the right to use of access any SAP External Product, through API Calls.
Files: *
Copyright: 2019-2020 SAP SE or an SAP affiliate company and cap-cloud-samples
License: Apache-2.0

View File

@@ -1,136 +0,0 @@
{
"$schema": "https://aka.ms/codetour-schema",
"title": "CAP Samples",
"steps": [
{
"title": "Welcome",
"file": "README.md",
"description": "### Welcome to CAP Samples!\n\nThis tour leads you through a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap)\nYou will learn which features of the programming models are demonstrated in which sample.\n\nLet's start!",
"line": 2,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 3,
"character": 108
}
}
},
{
"file": "hello/world.cds",
"description": "### Hello World!\n\nThis is a simplistic [Hello World](https://cap.cloud.sap/docs/get-started/hello-world) service using [CDS](https://cap.cloud.sap/docs/cds/) and [cds.services](https://cap.cloud.sap/docs/node.js/api#services-api).",
"line": 2,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 4,
"character": 1
}
},
"title": "Hello World!"
},
{
"file": "bookshop/db/schema.cds",
"description": "### A Bookshop!\n\nIntroduces:\n- [Project Setup](https://cap.cloud.sap/docs/get-started/) and [Layouts](https://cap.cloud.sap/docs/get-started/projects)\n- [Domain Modeling](https://cap.cloud.sap/docs/guides/domain-models)\n- [Defining Services](https://cap.cloud.sap/docs/guides/providing-services)\n- [Generic Providers](https://cap.cloud.sap/docs/guides/generic-providers)\n- [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl)\n- [Using Databases](https://cap.cloud.sap/docs/guides/databases)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 32,
"character": 1
}
},
"title": "Bookshop"
},
{
"file": "common/index.cds",
"description": "### Extend and Reuse\n\nShowcases how to extend [@sap/cds/common](https://cap.cloud.sap/docs/cds/common) thereby covering:\n- Building [extension packages](https://cap.cloud.sap/docs/guides/domain-models#aspects-extensibility)\n- Providing [reuse packages](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content)\n- [Verticalization](https://cap.cloud.sap/docs/cds/common#adapting-to-your-needs)\n- Using [Aspects](https://cap.cloud.sap/docs/cds/cdl#aspects)\n- Used in the [fiori app sample](#fiori)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 46,
"character": 1
}
},
"title": "Common"
},
{
"file": "orders/db/schema.cds",
"description": "### Compositions and Serving Documents\n\nA standalone orders management service, demonstrating:\n- Using [Compositions](https://cap.cloud.sap/docs/cds/cdl#compositions) in [Domain Models](https://cap.cloud.sap/docs/guides/domain-models), along with\n- [Serving deeply nested documents](https://cap.cloud.sap/docs/guides/generic-providers#serving-structured-data)\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 27,
"character": 1
}
},
"title": "Orders"
},
{
"file": "reviews/db/schema.cds",
"description": "### More Modularity\n\nShows how to implement a modular service to manage product reviews, including:\n- Consuming other services synchronously and asynchronously\n- Serving requests synchronously\n- Emitting events asynchronously\n- Grow as you go, with:\n- Mocking app services\n- Running service meshes\n- Late-cut Micro Services\n- As well as managed data, input validations, and authorization\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 39,
"character": 1
}
},
"title": "Reviews"
},
{
"file": "fiori/app/index.cds",
"description": "### Annotations for SAP Fiori Elements\n\nA [composite app, reusing and combining](https://cap.cloud.sap/docs/guides/verticalize) these packages:\n - [@capire/bookshop](bookshop)\n - [@capire/reviews](reviews)\n - [@capire/orders](orders)\n - [@capire/common](common)\n\n[Adds a SAP Fiori elements application](https://cap.cloud.sap/docs/guides/fiori/) to bookshop, thereby introducing to:\n - [OData Annotations](https://cap.cloud.sap/docs/guides/fiori#adding-odata-annotations) in `.cds` files\n - Support for [Fiori Draft](https://cap.cloud.sap/docs/guides/fiori#draft)\n - Support for [Value Helps](https://cap.cloud.sap/docs/guides/fiori#value-help)\n - Serving SAP Fiori apps locally\n\n[The Vue.js app](bookshop/app/vue) imported from bookshop is served as well.\n",
"line": 1,
"selection": {
"start": {
"line": 1,
"character": 1
},
"end": {
"line": 13,
"character": 1
}
},
"title": "Fiori"
},
{
"file": "package.json",
"description": "### All-in-one Monorepo\n\nEach sample sub directory essentially is a standard npm package, some with standard npm dependencies to other samples. The root folder's [package.json](package.json) has local links to the sub folders, such that an `npm install` populates a local `node_modules` folder acts like a local npm registry to the individual sample packages.\n",
"line": 8,
"selection": {
"start": {
"line": 8,
"character": 1
},
"end": {
"line": 15,
"character": 1
}
},
"title": "Packages"
}
],
"isPrimary": true,
"description": "Overview of CAP Samples for Node.js"
}

View File

@@ -1,21 +0,0 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
// Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
// List of extensions which should be recommended for users of this workspace.
"recommendations": [
"sapse.vscode-cds",
"dbaeumer.vscode-eslint",
"esbenp.prettier-vscode",
"mechatroner.rainbow-csv",
"humao.rest-client",
"alexcvzz.vscode-sqlite",
"hbenl.vscode-mocha-test-adapter",
"sdras.night-owl",
"vsls-contrib.codetour"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}

37
.vscode/launch.json vendored
View File

@@ -4,36 +4,33 @@
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach by Process ID",
"processId": "${command:PickProcess}",
"request": "attach",
"skipFiles": [
"<node_internals>/**"
],
"type": "pwa-node"
},
{
"name": "bookshop",
"command": "cds watch bookshop",
"request": "launch",
"type": "node-terminal",
"name": "bookshop", "request": "launch", "type": "node", "runtimeExecutable": "npx", "runtimeArgs": [ "-n" ],
"args": [ "--", "cds", "run", "--in-memory" ],
"cwd": "${workspaceFolder}/packages/bookshop",
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
},
{
"name": "Fiori app",
"command": "cds watch fiori",
"request": "launch",
"type": "node-terminal",
"name": "cds run ...", "request": "launch", "type": "node", "runtimeExecutable": "npx", "runtimeArgs": [ "-n" ],
"args": [ "--", "cds", "run", "--with-mocks", "--in-memory?" ],
"cwd": "${workspaceFolder}/packages/${input:service}",
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
}
],
"inputs": [
{
"type": "pickString",
"id": "sample",
"description": "Which sample do you want to start?",
"options": ["bookshop", "fiori", "reviews", "reviews/test/bookshop"],
"id": "service",
"description": "Which service do you want to start?",
"options": [
"bookshop",
"bookstore",
"media-server",
"office-supplies",
"reviews-service"
],
"default": "bookshop"
}
]

28
.vscode/tasks.json vendored
View File

@@ -1,15 +1,17 @@
{
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "npm",
"script": "jest",
"group": {
"kind": "test",
"isDefault": true
}
}
]
// See https://go.microsoft.com/fwlink/?LinkId=733558
// for the documentation about the tasks.json format
"version": "2.0.0",
"tasks": [
{
"type": "npm", "script": "watch", "path": "packages/bookshop/",
"options": { "env": { "PORT": "4004" }},
"presentation": { "group": "A" }
},
{
"type": "npm", "script": "watch", "path": "packages/reviews-service/",
"options": { "env": { "PORT": "5005" }},
"presentation": { "group": "A" }
}
]
}

24
Jenkinsfile vendored Normal file
View File

@@ -0,0 +1,24 @@
#!/usr/bin/env groovy
/*
* This file bootstraps the codified Continuous Delivery pipeline for extensions of SAP solutions, such as SAP S/4HANA.
* The pipeline helps you to deliver software changes quickly and in a reliable manner.
* A suitable Jenkins instance is required to run the pipeline.
* The Jenkins can easily be bootstraped using the life-cycle script located inside the 'cx-server' directory.
*
* More information on getting started with Continuous Delivery can be found in the following places:
* - GitHub repository: https://github.com/SAP/cloud-s4-sdk-pipeline
* - Blog Post: https://blogs.sap.com/2017/09/20/continuous-integration-and-delivery
*/
/*
* Set pipelineVersion to a fixed released version (e.g. "v15") when running in a productive environment.
* To find out about available versions and release notes, visit: https://github.com/SAP/cloud-s4-sdk-pipeline/releases
*/
String pipelineVersion = "v28"
node {
deleteDir()
sh "git clone --depth 1 https://github.com/SAP/cloud-s4-sdk-pipeline.git -b ${pipelineVersion} pipelines"
load './pipelines/s4sdk-pipeline.groovy'
}

370
LICENSE
View File

@@ -1,208 +1,190 @@
Apache License
SAP SAMPLE CODE LICENSE AGREEMENT
Version 2.0, January 2004
Please scroll down and read the following SAP Sample Code License Agreement
carefully ("Agreement"). By downloading, installing, or otherwise using the
SAP sample code or any materials that accompany the sample code documentation
(collectively, the "Sample Code"), You agree that this Agreement forms a legally
binding agreement between You ("You" or "Your") and SAP SE, for and on behalf
of itself and its subsidiaries and affiliates (as defined in Section 15 of the
German Stock Corporation Act), and You agree to be bound by all of the terms
and conditions stated in this Agreement. If You are trying to access or download
the Sample Code on behalf of Your employer or as a consultant or agent of a
third party (either "Your Company"), You represent and warrant that You have
the authority to act on behalf of and bind Your Company to the terms of this
Agreement and everywhere in this Agreement that refers to 'You' or 'Your' shall
also include Your Company. If You do not agree to these terms, do not attempt
to access or use the Sample Code.
http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION,
AND DISTRIBUTION
1. LICENSE: Subject to the terms of this Agreement, SAP grants You a nonexclusive,
non-transferable, non-sublicensable, revocable, royalty-free,
limited license to use, copy, and modify the Sample Code solely for Your internal
business purposes.
1. Definitions.
2. RESTRICTIONS: You must not use the Sample Code to: (a) impair, degrade or
reduce the performance or security of any SAP products, services or related
technology (collectively, "SAP Products"); (b) enable the bypassing or
circumventing of SAP's license restrictions and/or provide users with access to
the SAP Products to which such users are not licensed; or (c) permit mass data
extraction from an SAP Product to a non-SAP Product, including use,
modification, saving or other processing of such data in the non-SAP Product.
Further, You must not: (i) provide or make the Sample Code available to any
third party other than your authorized employees, contractors and agents
(collectively, “Representatives”) and solely to be used by Your Representatives
for Your own internal business purposes; ii) remove or modify any marks or
proprietary notices from the Sample Code; iii) assign this Agreement, or any
interest therein, to any third party; (iv) use any SAP name, trademark or logo
without the prior written authorization of SAP; or (v) use the Sample Code to
modify an SAP Product or decompile, disassemble or reverse engineer an SAP
Product (except to the extent permitted by applicable law). You are responsible
for any breach of the terms of this Agreement by You or Your Representatives.
3. INTELLECTUAL PROPERTY: SAP or its licensors retain all ownership and
intellectual property rights in and to the Sample Code and SAP Products. In
exchange for the right to use, copy and modify the Sample Code provided under
this Agreement, You covenant not to assert any intellectual property rights in
or to any of Your products, services, or related technology that are based on
or incorporate the Sample Code against any individual or entity in respect of
any current or future SAP Products.
4. SAP AND THIRD PARTY APIS: The Sample Code may include API (application
programming interface) calls to SAP and third-party products or services. The
access or use of the third-party products and services to which the API calls
are directed may be subject to additional terms and conditions between you and
SAP or such third parties. You (and not SAP) are solely responsible for
understanding and complying with any additional terms and conditions that apply
to the access or use of those APIs and/or third-party products and services.
SAP does not grant You any rights in or to these APIs, products or services
under this Agreement.
"License" shall mean the terms and conditions for use, reproduction, and distribution
as defined by Sections 1 through 9 of this document.
5. FREE AND OPEN SOURCE COMPONENTS: The Sample Code may include third party
free or open source components ("FOSS Components"). You may have additional
rights in such FOSS Components that are provided by the third party licensors
of those components.
6. THIRD PARTY DEPENDENCIES: The Sample Code may require third party software
dependencies ("Dependencies") for the use or operation of the Sample Code. These
Dependencies may be identified by SAP in Maven POM files, documentation or by
other means. SAP does not grant You any rights in or to such Dependencies under
this Agreement. You are solely responsible for the acquisition, installation
and use of such Dependencies.
7. WARRANTY:
a) If You are located outside the US or Canada: AS THE SAMPLE CODE IS PROVIDED
TO YOU FREE OF CHARGE, SAP DOES NOT GUARANTEE OR WARRANT ANY FEATURES OR
QUALITIES OF THE SAMPLE CODE OR GIVE ANY UNDERTAKING WITH REGARD TO ANY OTHER
QUALITY. NO SUCH WARRANTY OR UNDERTAKING SHALL BE IMPLIED BY YOU FROM ANY
DESCRIPTION IN THE SAMPLE CODE OR ANY OTHER MATERIALS, COMMUNICATION OR
ADVERTISEMENT. IN PARTICULAR, SAP DOES NOT WARRANT THAT THE SAMPLE CODE WILL BE
AVAILABLE UNINTERRUPTED, ERROR FREE, OR PERMANENTLY AVAILABLE. ALL WARRANTY
CLAIMS RESPECTING THE SAMPLE CODE ARE SUBJECT TO THE LIMITATION OF LIABILITY
STIPULATED IN SECTION 8 BELOW.
b) If You are located in the US or Canada: THE SAMPLE CODE IS LICENSED TO YOU
"AS IS", WITHOUT ANY WARRANTY, ESCROW, TRAINING, MAINTENANCE, OR SERVICE
OBLIGATIONS WHATSOEVER ON THE PART OF SAP. SAP MAKES NO EXPRESS OR IMPLIED
WARRANTIES OR CONDITIONS OF SALE OF ANY TYPE WHATSOEVER, INCLUDING BUT NOT
LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY AND OF FITNESS FOR A PARTICULAR
PURPOSE. IN PARTICULAR, SAP DOES NOT WARRANT THAT THE SAMPLE CODE WILL BE
AVAILABLE UNINTERRUPTED, ERROR FREE, OR PERMANENTLY AVAILABLE. YOU ASSUME ALL
RISKS ASSOCIATED WITH THE USE OF THE SAMPLE CODE, INCLUDING WITHOUT LIMITATION
RISKS RELATING TO QUALITY, AVAILABILITY, PERFORMANCE, DATA LOSS, AND UTILITY IN
A PRODUCTION ENVIRONMENT.
c) For all locations: SAP DOES NOT MAKE ANY REPRESENTATIONS OR WARRANTIES IN
RESPECT OF THIRD PARTY DEPENDENCIES, APIS, PRODUCTS AND SERVICES, INCLUDING BUT
NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY AND OF FITNESS FOR A
PARTICULAR PURPOSE. IN PARTICULAR, SAP DOES NOT WARRANT THAT THIRDPARTY
DEPENDENCIES, APIS, PRODUCTS AND SERVICES WILL BE AVAILABLE, ERROR FREE,
INTEROPERABLE WITH THE SAMPLE CODE, SUITABLE FOR ANY PARTICULAR PURPOSE OR NONINFRINGING.
YOU ASSUME ALL RISKS ASSOCIATED WITH THE USE OF THIRD
PARTY DEPENDENCIES, APIS, PRODUCTS AND SERVICES, INCLUDING WITHOUT LIMITATION
RISKS RELATING TO QUALITY, AVAILABILITY, PERFORMANCE, DATA LOSS, UTILITY IN A
PRODUCTION ENVIRONMENT, AND NON-INFRINGEMENT. IN NO EVENT WILL SAP BE LIABLE
DIRECTLY OR INDIRECTLY IN RESPECT OF ANY USE OF THIRD PARTY DEPENDENCIES, APIS,
PRODUCTS AND SERVICES BY YOU.
8. LIMITATION OF LIABILITY:
a) If You are located outside the US or Canada: IRRESPECTIVE OF THE LEGAL
REASONS, SAP SHALL ONLY BE LIABLE FOR DAMAGES UNDER THIS AGREEMENT IF SUCH
DAMAGE (I) CAN BE CLAIMED UNDER THE GERMAN PRODUCT LIABILITY ACT OR (II) IS
CAUSED BY INTENTIONAL MISCONDUCT OF SAP OR (III) CONSISTS OF PERSONAL INJURY.
IN ALL OTHER CASES, NEITHER SAP NOR ITS EMPLOYEES, AGENTS AND SUBCONTRACTORS
SHALL BE LIABLE FOR ANY KIND OF DAMAGE OR CLAIMS HEREUNDER.
b) If You are located in the US or Canada: IN NO EVENT SHALL SAP BE LIABLE TO
YOU, YOUR COMPANY OR TO ANY THIRD PARTY FOR ANY DAMAGES IN AN AMOUNT IN EXCESS
OF $100 ARISING IN CONNECTION WITH YOUR USE OF OR INABILITY TO USE THE SAMPLE
CODE OR IN CONNECTION WITH SAP'S PROVISION OF OR FAILURE TO PROVIDE SERVICES
PERTAINING TO THE SAMPLE CODE, OR AS A RESULT OF ANY DEFECT IN THE SAMPLE COED.
THIS DISCLAIMER OF LIABILITY SHALL APPLY REGARDLESS OF THE FORM OF ACTION THAT
MAY BE BROUGHT AGAINST SAP, WHETHER IN CONTRACT OR TORT, INCLUDING WITHOUT
LIMITATION ANY ACTION FOR NEGLIGENCE. YOUR SOLE REMEDY IN THE EVENT OF BREACH
OF THIS AGREEMENT BY SAP OR FOR ANY OTHER CLAIM RELATED TO THE SAMPLE CODE SHALL
BE TERMINATION OF THIS AGREEMENT. NOTWITHSTANDING ANYTHING TO THE CONTRARY
HEREIN, UNDER NO CIRCUMSTANCES SHALL SAP OR ITS LICENSORS BE LIABLE TO YOU OR
ANY OTHER PERSON OR ENTITY FOR ANY SPECIAL, INCIDENTAL, CONSEQUENTIAL, OR
INDIRECT DAMAGES, LOSS OF GOOD WILL OR BUSINESS PROFITS, WORK STOPPAGE, DATA
LOSS, COMPUTER FAILURE OR MALFUNCTION, ANY AND ALL OTHER COMMERCIAL DAMAGES OR
LOSS, OR EXEMPLARY OR PUNITIVE DAMAGES.
9. INDEMNITY: You will fully indemnify, hold harmless and defend SAP against
law suits based on any claim: (a) that any of Your products, services or related
technology that are based on or incorporate the Sample Code infringes or
misappropriates any patent, copyright, trademark, trade secrets, or other
proprietary rights of a third party, or (b) related to Your alleged violation
of the terms of this Agreement.
"Licensor" shall mean the copyright owner or entity authorized by the copyright
owner that is granting the License.
10. EXPORT: The Sample Code is subject to German, EU and US export control
regulations. You confirm that: a) You will not use the Sample Code for, and
will not allow the Sample Code to be used for, any purposes prohibited by
German, EU and US law, including, without limitation, for the development,
design, manufacture or production of nuclear, chemical or biological weapons of
mass destruction; b) You are not located in Cuba, Iran, Sudan, Iraq, North
Korea, Syria, nor any other country to which the United States has prohibited
export or that has been designated by the U.S. Government as a "terrorist
supporting" country (any, an "US Embargoed Country"); c) You are not a citizen,
national or resident of, and are not under the control of, a US Embargoed
Country; d) You will not download or otherwise export or re-export the Sample
Code, directly or indirectly, to a US Embargoed Country nor to citizens,
nationals or residents of a US Embargoed Country; e) You are not listed on the
United States Department of Treasury lists of Specially Designated Nationals,
Specially Designated Terrorists, and Specially Designated Narcotic Traffickers,
nor listed on the United States Department of Commerce Table of Denial Orders
or any other U.S. government list of prohibited or restricted parties and f)
You will not download or otherwise export or re-export the Sample Code, directly
or indirectly, to persons on the above-mentioned lists.
11. SUPPORT: SAP does not offer support for the Sample Code.
12. TERM AND TERMINATION: You may terminate this Agreement by destroying all
copies of the Sample Code in Your possession or control. SAP may terminate Your
license to use the Sample Code immediately if You fail to comply with any of
the terms of this Agreement, or, for SAP's convenience by providing you with
ten (10) days written notice of termination. In case of termination or
expiration of this Agreement, You must immediately destroy all copies of the
Sample Code in your possession or control. In the event Your Company is acquired
(by merger, purchase of stock, assets or intellectual property or exclusive
license), or You become employed, by a direct competitor of SAP, then this
Agreement and all licenses granted to You in this Agreement shall immediately
terminate upon the date of such acquisition or change of employment.
"Legal Entity" shall mean the union of the acting entity and all other entities
that control, are controlled by, or are under common control with that entity.
For the purposes of this definition, "control" means (i) the power, direct
or indirect, to cause the direction or management of such entity, whether
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more
of the outstanding shares, or (iii) beneficial ownership of such entity.
13. LAW/VENUE:
a) If You are located outside the US or Canada: This Agreement is governed by
and construed in accordance with the laws of Germany without reference to its
conflicts of law principles. You and SAP agree to submit to the exclusive
jurisdiction of, and venue in, the courts located in Karlsruhe, Germany in any
dispute arising out of or relating to this Agreement or the Sample Code. The
United Nations Convention on Contracts for the International Sale of Goods shall
not apply to this Agreement.
b) If You are located in the US or Canada: This Agreement shall be governed by
and construed in accordance with the laws of the State of New York, USA without
reference to its conflicts of law principles. You and SAP agree to submit to
the exclusive jurisdiction of, and venue in, the courts located in New York,
New York, USA in any dispute arising out of or relating to this Agreement or
the Sample Code. The United Nations Convention on Contracts for the
International Sale of Goods shall not apply to this Agreement.
14. MISCELLANEOUS: This Agreement is the complete agreement between the parties
respecting the Sample Code. This Agreement supersedes all prior or
contemporaneous agreements or representations with regards to the Sample Code.
If any term of this Agreement is found to be invalid or unenforceable, the
surviving provisions shall remain effective. SAP's failure to enforce any right
or provisions stipulated in this Agreement will not constitute a waiver of such
provision, or any other provision of this Agreement.
"You" (or "Your") shall mean an individual or Legal Entity exercising permissions
granted by this License.
"Source" form shall mean the preferred form for making modifications, including
but not limited to software source code, documentation source, and configuration
files.
"Object" form shall mean any form resulting from mechanical transformation
or translation of a Source form, including but not limited to compiled object
code, generated documentation, and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or Object form,
made available under the License, as indicated by a copyright notice that
is included in or attached to the work (an example is provided in the Appendix
below).
"Derivative Works" shall mean any work, whether in Source or Object form,
that is based on (or derived from) the Work and for which the editorial revisions,
annotations, elaborations, or other modifications represent, as a whole, an
original work of authorship. For the purposes of this License, Derivative
Works shall not include works that remain separable from, or merely link (or
bind by name) to the interfaces of, the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including the original version
of the Work and any modifications or additions to that Work or Derivative
Works thereof, that is intentionally submitted to Licensor for inclusion in
the Work by the copyright owner or by an individual or Legal Entity authorized
to submit on behalf of the copyright owner. For the purposes of this definition,
"submitted" means any form of electronic, verbal, or written communication
sent to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems, and
issue tracking systems that are managed by, or on behalf of, the Licensor
for the purpose of discussing and improving the Work, but excluding communication
that is conspicuously marked or otherwise designated in writing by the copyright
owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity on behalf
of whom a Contribution has been received by Licensor and subsequently incorporated
within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of this
License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable copyright license to reproduce, prepare
Derivative Works of, publicly display, publicly perform, sublicense, and distribute
the Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of this License,
each Contributor hereby grants to You a perpetual, worldwide, non-exclusive,
no-charge, royalty-free, irrevocable (except as stated in this section) patent
license to make, have made, use, offer to sell, sell, import, and otherwise
transfer the Work, where such license applies only to those patent claims
licensable by such Contributor that are necessarily infringed by their Contribution(s)
alone or by combination of their Contribution(s) with the Work to which such
Contribution(s) was submitted. If You institute patent litigation against
any entity (including a cross-claim or counterclaim in a lawsuit) alleging
that the Work or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses granted to You
under this License for that Work shall terminate as of the date such litigation
is filed.
4. Redistribution. You may reproduce and distribute copies of the Work or
Derivative Works thereof in any medium, with or without modifications, and
in Source or Object form, provided that You meet the following conditions:
(a) You must give any other recipients of the Work or Derivative Works a copy
of this License; and
(b) You must cause any modified files to carry prominent notices stating that
You changed the files; and
(c) You must retain, in the Source form of any Derivative Works that You distribute,
all copyright, patent, trademark, and attribution notices from the Source
form of the Work, excluding those notices that do not pertain to any part
of the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its distribution,
then any Derivative Works that You distribute must include a readable copy
of the attribution notices contained within such NOTICE file, excluding those
notices that do not pertain to any part of the Derivative Works, in at least
one of the following places: within a NOTICE text file distributed as part
of the Derivative Works; within the Source form or documentation, if provided
along with the Derivative Works; or, within a display generated by the Derivative
Works, if and wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and do not modify the
License. You may add Your own attribution notices within Derivative Works
that You distribute, alongside or as an addendum to the NOTICE text from the
Work, provided that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and may provide
additional or different license terms and conditions for use, reproduction,
or distribution of Your modifications, or for any such Derivative Works as
a whole, provided Your use, reproduction, and distribution of the Work otherwise
complies with the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise, any
Contribution intentionally submitted for inclusion in the Work by You to the
Licensor shall be under the terms and conditions of this License, without
any additional terms or conditions. Notwithstanding the above, nothing herein
shall supersede or modify the terms of any separate license agreement you
may have executed with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade names,
trademarks, service marks, or product names of the Licensor, except as required
for reasonable and customary use in describing the origin of the Work and
reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or agreed to
in writing, Licensor provides the Work (and each Contributor provides its
Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
KIND, either express or implied, including, without limitation, any warranties
or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR
A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness
of using or redistributing the Work and assume any risks associated with Your
exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory, whether
in tort (including negligence), contract, or otherwise, unless required by
applicable law (such as deliberate and grossly negligent acts) or agreed to
in writing, shall any Contributor be liable to You for damages, including
any direct, indirect, special, incidental, or consequential damages of any
character arising as a result of this License or out of the use or inability
to use the Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all other commercial
damages or losses), even if such Contributor has been advised of the possibility
of such damages.
9. Accepting Warranty or Additional Liability. While redistributing the Work
or Derivative Works thereof, You may choose to offer, and charge a fee for,
acceptance of support, warranty, indemnity, or other liability obligations
and/or rights consistent with this License. However, in accepting such obligations,
You may act only on Your own behalf and on Your sole responsibility, not on
behalf of any other Contributor, and only if You agree to indemnify, defend,
and hold each Contributor harmless for any liability incurred by, or claims
asserted against, such Contributor by reason of your accepting any such warranty
or additional liability. END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following boilerplate
notice, with the fields enclosed by brackets "[]" replaced with your own identifying
information. (Don't include the brackets!) The text should be enclosed in
the appropriate comment syntax for the file format. We also recommend that
a file or class name and description of purpose be included on the same "printed
page" as the copyright notice for easier identification within third-party
archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
v1.0-071618

1
NOTICE Normal file
View File

@@ -0,0 +1 @@
Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved.

View File

@@ -1,83 +1,45 @@
# Welcome to cap/samples
# Welcome to SAP Cloud Application Programming model samples
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo). &rarr; See [**Overview** of contained samples](samples.md)
Find here the samples for the openSAP course [Building Applications with the SAP Cloud Application Programming Model](https://open.sap.com/courses/cp7).
![](https://github.com/SAP-samples/cloud-cap-samples/workflows/CI/badge.svg)
<!--[![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-cap-samples)](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-samples)-->
### Preliminaries
1. Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/) globally:
```sh
npm i -g @sap/cds-dk
```
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/tools#vscode)
### Download
If you have [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/master.zip).
## Notes on the Demo in Week 4 Unit 4
To add all pipeline specific file to your project, run the following command:
```sh
git clone https://github.com/sap-samples/cloud-cap-samples samples
cd samples
cds add pipeline
```
### Setup
Details on how to start your Jenkins in your own environment can be found in the [Operations Guide](https://github.com/SAP/devops-docker-cx-server/blob/master/docs/operations/cx-server-operations-guide.md).
In the samples folder run:
Please note that other than shown in the video Jenkins now is secured by default with an admin user and password.
After you have started Jenkins with the command `cx-server start`, you can get the initial password by running `./cx-server initial-credentials`.
The pipeline documentation can be found [here](https://sap.github.io/jenkins-library/pipelines/cloud-sdk/introduction/).
## Get Access to SAP Business Application Studio
The recommended environment for the course is SAP Business Application Studio. Watch [unit 2 of week 1](https://open.sap.com/courses/cp7/items/51pzQUzbXHr2kdbOmVs6jI) for how to get access.
## Setup
In SAP Business Application Studio, open a terminal.
Then clone the repo with this specific branch:
```sh
git clone https://github.com/sap-samples/cloud-cap-samples -b openSAP-week4-unit4
cd cloud-cap-samples
```
In the `cloud-cap-samples` folder run:
```sh
npm install
```
### 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.
### Serve `npm`
We've included a simple npm registry mock, which allows you to do an `npm install @capire/<package>` locally. Use it as follows:
1. Start the @capire registry:
```sh
npm run registry
```
> While running this will have `@capire:registry=http://localhost:4444` set with npmrc.
2. Install one of the @capire packages wherever you like, for example:
```sh
npm add @capire/common @capire/bookshop
```
## Get Support
Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br>
In case you have a question, find a bug, or otherwise need support, please use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce).
Check out the cap docs at https://cap.cloud.sap. <br>
In case you find a bug or need support, please [open an issue in here](https://github.com/SAP-samples/cloud-cap-samples/issues/new).
## License
Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt) file.
Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under SAP Sample Code License Agreement, except as noted otherwise in the [LICENSE](/LICENSE) file.

13
app/_i18n/i18n.properties Normal file
View File

@@ -0,0 +1,13 @@
Books = Books
Book = Book
ID = ID
Title = Title
Author = Author
AuthorID = Author ID
Stock = Stock
Name = Name
AuthorName = Author's Name
Authors = Authors
Order = Order
Orders = Orders
Price = Price

View File

@@ -0,0 +1,37 @@
using AdminService from '../../srv/admin-service';
////////////////////////////////////////////////////////////////////////////
//
// Books Object Page
//
annotate AdminService.Books with @(
UI: {
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'},
],
FieldGroup#General: {
Data: [
{Value: title},
{Value: author_ID},
{Value: descr},
]
},
FieldGroup#Details: {
Data: [
{Value: stock},
{Value: price},
{Value: currency_code, Label: '{i18n>Currency}'},
]
},
FieldGroup#Admin: {
Data: [
{Value: createdBy},
{Value: createdAt},
{Value: modifiedBy},
{Value: modifiedAt}
]
}
}
);

View File

@@ -0,0 +1,22 @@
sap.ui.define(["sap/fe/AppComponent"], ac => ac.extend("admin.Component", {
metadata:{ manifest:'json' }
}))
// sap.ui.define (["sap/ui/core/UIComponent"], ui5 => ui5.extend("bookshop.Component", {
// metadata: { manifest: "json" }
// }))
// sap.ui.define (["sap/ui/generic/app/AppComponent"], ui5 => ui5.extend("bookshop.Component", {
// metadata: { manifest: "json" }
// }))
// jQuery.sap.declare("bookshop.Component");
// sap.ui.getCore().loadLibrary("sap.ui.generic.app");
// jQuery.sap.require("sap.ui.generic.app.AppComponent");
// sap.ui.generic.app.AppComponent.extend("bookshop.Component", {
// metadata: {
// manifest: "json"
// }
// });
/* eslint no-undef:0 */

View File

@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
"sap.fe": {}
}
},
"models": {

View File

@@ -1,4 +1,4 @@
using CatalogService from '@capire/bookshop';
using CatalogService from '../../srv/cat-service';
////////////////////////////////////////////////////////////////////////////
//
@@ -7,8 +7,6 @@ using CatalogService from '@capire/bookshop';
annotate CatalogService.Books with @(
UI: {
HeaderInfo: {
TypeName: 'Book',
TypeNamePlural: 'Books',
Description: {Value: author}
},
HeaderFacets: [
@@ -42,7 +40,6 @@ annotate CatalogService.Books with @(
LineItem: [
{Value: title},
{Value: author, Label:'{i18n>Author}'},
{Value: genre.name},
{Value: price},
{Value: currency.symbol, Label:' '},
]

View File

@@ -0,0 +1,22 @@
sap.ui.define(["sap/fe/AppComponent"], ac => ac.extend("bookshop.Component", {
metadata:{ manifest:'json' }
}))
// sap.ui.define (["sap/ui/core/UIComponent"], ui5 => ui5.extend("bookshop.Component", {
// metadata: { manifest: "json" }
// }))
// sap.ui.define (["sap/ui/generic/app/AppComponent"], ui5 => ui5.extend("bookshop.Component", {
// metadata: { manifest: "json" }
// }))
// jQuery.sap.declare("bookshop.Component");
// sap.ui.getCore().loadLibrary("sap.ui.generic.app");
// jQuery.sap.require("sap.ui.generic.app.AppComponent");
// sap.ui.generic.app.AppComponent.extend("bookshop.Component", {
// metadata: {
// manifest: "json"
// }
// });
/* eslint no-undef:0 */

View File

@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
"sap.fe": {}
}
},
"models": {

74
app/common.cds Normal file
View File

@@ -0,0 +1,74 @@
/*
Common Annotations shared by all apps
*/
using { sap.capire.bookshop as my } from '../db/schema';
////////////////////////////////////////////////////////////////////////////
//
// Books Lists
//
annotate my.Books with @(
UI: {
Identification: [{Value:title}],
SelectionFields: [ ID, author_ID, price, currency_code ],
LineItem: [
{Value: ID},
{Value: title},
{Value: author.name, Label:'{i18n>Author}'},
{Value: stock},
{Value: price},
{Value: currency.symbol, Label:' '},
]
}
) {
author @ValueList.entity:'Authors';
};
annotate my.Authors with @(
UI: {
Identification: [{Value:name}],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books Details
//
annotate my.Books with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Book}',
TypeNamePlural: '{i18n>Books}',
Title: {Value: title},
Description: {Value: author.name}
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books Elements
//
annotate my.Books with {
ID @title:'{i18n>ID}' @UI.HiddenFilter;
title @title:'{i18n>Title}';
author @title:'{i18n>AuthorID}';
price @title:'{i18n>Price}';
stock @title:'{i18n>Stock}';
descr @UI.MultiLineText;
}
////////////////////////////////////////////////////////////////////////////
//
// Authors Elements
//
annotate my.Authors with {
ID @title:'{i18n>ID}' @UI.HiddenFilter;
name @title:'{i18n>AuthorName}';
}

View File

@@ -13,7 +13,7 @@
applications: {
"browse-books": {
title: "Browse Books",
description: "w/ SAP Fiori Elements",
description: "... testing FE v42",
additionalInformation: "SAPUI5.Component=bookshop",
applicationType : "URL",
url: "/browse/webapp",
@@ -21,7 +21,7 @@
},
"manage-books": {
title: "Manage Books",
description: "w/ SAP Fiori Elements",
description: "... testing FE v42",
additionalInformation: "SAPUI5.Component=admin",
applicationType : "URL",
url: "/admin/webapp",
@@ -29,7 +29,7 @@
},
"manage-orders": {
title: "Manage Orders",
description: "w/ SAP Fiori Elements",
description: "... testing FE v42",
additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL",
url: "/orders/webapp",
@@ -39,8 +39,8 @@
};
</script>
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
<script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"

View File

@@ -4,9 +4,5 @@
using from './admin/fiori-service';
using from './browse/fiori-service';
using from './orders/fiori-service';
using from './common';
using from '@capire/common';
// only works in case of embedded orders service
using from '@capire/orders/app/orders/fiori-service';

View File

@@ -1,27 +1,41 @@
using AdminService from '../../srv/admin-service';
annotate AdminService.Books with {
price @Common.FieldControl: #ReadOnly;
}
////////////////////////////////////////////////////////////////////////////
//
// Note: this is designed for the OrdersService being co-located with
// bookshop. It does not work if OrdersService is run as a separate
// process, and is not intended to do so.
// Common
//
////////////////////////////////////////////////////////////////////////////
annotate AdminService.OrderItems with {
book @(
Common: {
Text: book.title,
FieldControl: #Mandatory
},
ValueList.entity:'Books',
);
amount @(
Common.FieldControl: #Mandatory
);
}
using { OrdersService } from '../../srv/orders-service';
@odata.draft.enabled
annotate OrdersService.Orders with @(
annotate AdminService.Orders with @(
UI: {
////////////////////////////////////////////////////////////////////////////
//
// Lists of Orders
//
SelectionFields: [ createdAt, createdBy ],
LineItem: [
{Value: OrderNo, Label:'OrderNo'},
{Value: buyer, Label:'Customer'},
{Value: createdBy, Label:'Customer'},
{Value: createdAt, Label:'Date'}
],
////////////////////////////////////////////////////////////////////////////
//
// Order Details
//
HeaderInfo: {
TypeName: 'Order', TypeNamePlural: 'Orders',
Title: {
@@ -45,7 +59,7 @@ annotate OrdersService.Orders with @(
],
FieldGroup#Details: {
Data: [
{Value: currency.code, Label:'Currency'}
{Value: currency_code, Label:'Currency'}
]
},
FieldGroup#Created: {
@@ -68,25 +82,36 @@ annotate OrdersService.Orders with @(
annotate OrdersService.Orders_Items with @(
//The enity types name is AdminService.my_bookshop_OrderItems
//The annotations below are not generated in edmx WHY?
annotate AdminService.OrderItems with @(
UI: {
HeaderInfo: {
TypeName: 'Order Item', TypeNamePlural: ' ',
Title: {
Value: book.title
},
Description: {Value: book.descr}
},
// There is no filterbar for items so the selctionfileds is not needed
SelectionFields: [ book_ID ],
////////////////////////////////////////////////////////////////////////////
//
// Lists of OrderItems
//
LineItem: [
{Value: product_ID, Label:'Product ID'},
{Value: title, Label:'Product Title'},
{Value: price, Label:'Unit Price'},
{Value: book_ID, Label:'Book'},
//The following entry is only used to have the assoication followed in the read event
{Value: book.price, Label:'Book Price'},
{Value: amount, Label:'Quantity'},
],
Identification: [ //Is the main field group
//{Value: ID, Label:'ID'}, //A guid shouldn't be on the UI
{Value: book_ID, Label:'Book'},
{Value: amount, Label:'Amount'},
{Value: title, Label:'Product'},
{Value: price, Label:'Unit Price'},
],
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: '@UI.Identification'},
],
},
) {
amount @(
Common.FieldControl: #Mandatory
);
};
);

View File

@@ -0,0 +1,22 @@
sap.ui.define(["sap/fe/AppComponent"], ac => ac.extend("orders.Component", {
metadata:{ manifest:'json' }
}))
// sap.ui.define (["sap/ui/core/UIComponent"], ui5 => ui5.extend("bookshop.Component", {
// metadata: { manifest: "json" }
// }))
// sap.ui.define (["sap/ui/generic/app/AppComponent"], ui5 => ui5.extend("bookshop.Component", {
// metadata: { manifest: "json" }
// }))
// jQuery.sap.declare("bookshop.Component");
// sap.ui.getCore().loadLibrary("sap.ui.generic.app");
// jQuery.sap.require("sap.ui.generic.app.AppComponent");
// sap.ui.generic.app.AppComponent.extend("bookshop.Component", {
// metadata: {
// manifest: "json"
// }
// });
/* eslint no-undef:0 */

View File

@@ -3,12 +3,12 @@
"sap.app": {
"id": "orders",
"type": "application",
"title": "Order Books",
"title": "Manage Orders",
"description": "Sample Application",
"i18n": "i18n/i18n.properties",
"dataSources": {
"OrdersService": {
"uri": "/orders/",
"AdminService": {
"uri": "/admin/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
@@ -24,7 +24,7 @@
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
"sap.fe": {}
}
},
"models": {
@@ -33,7 +33,7 @@
"uri": "i18n/i18n.properties"
},
"": {
"dataSource": "OrdersService",
"dataSource": "AdminService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
@@ -121,7 +121,7 @@
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet": "Orders_Items"
"entitySet": "OrderItems"
}
}
},

View File

@@ -1,25 +0,0 @@
{
// Use IntelliSense to learn about possible attributes.
// Hover to view descriptions of existing attributes.
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
"version": "0.2.0",
"configurations": [
{
"name": "Attach to...",
"type": "node",
"request": "attach",
"processId": "${command:PickProcess}",
"skipFiles": ["<node_internals>/**"]
},
{
"name": "cds run",
"type": "node",
"request": "launch",
"runtimeExecutable": "npx",
"runtimeArgs": ["-n"],
"args": ["--", "cds", "run", "--with-mocks", "--in-memory?"],
"console": "integratedTerminal",
"skipFiles": ["<node_internals>/**"]
}
]
}

View File

@@ -1,2 +0,0 @@
// Incorporate pre-build extensions from...
using from '@capire/common';

View File

@@ -1,48 +0,0 @@
/* global Vue axios */ //> from vue.html
const $ = sel => document.querySelector(sel)
const GET = (url) => axios.get('/browse'+url)
const POST = (cmd,data) => axios.post('/browse'+cmd,data)
const books = new Vue ({
el:'#app',
data: {
list: [],
book: undefined,
order: { amount:1, succeeded:'', failed:'' }
},
methods: {
search: ({target:{value:v}}) => books.fetch(v && '&$search='+v),
async fetch (etc='') {
const {data} = await GET(`/ListOfBooks?$expand=genre,currency${etc}`)
books.list = data.value
},
async inspect (eve) {
const book = books.book = books.list [eve.currentTarget.rowIndex-1]
const res = await GET(`/Books/${book.ID}?$select=descr,stock,image`)
Object.assign (book, res.data)
books.order = { amount:1 }
setTimeout (()=> $('form > input').focus(), 111)
},
async submitOrder () {
const {book,order} = books, amount = parseInt (order.amount) || 1 // REVISIT: Okra should be less strict
try {
const res = await POST(`/submitOrder`, { amount, book: book.ID })
book.stock = res.data.stock
books.order = { amount, succeeded: `Successfully orderd ${amount} item(s).` }
} catch (e) {
books.order = { amount, failed: e.response.data.error.message }
}
}
}
})
// initially fill list of books
books.fetch()

View File

@@ -1,65 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<title> Capire Books </title>
<link rel="stylesheet" href="https://unpkg.com/primitive-ui/dist/css/main.css">
<script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/vue"></script>
<style>
.hovering tr:hover td { color:cyan; background: #123; cursor: pointer; }
.rating-stars { color:teal }
.succeeded { color:teal }
.failed { color:red }
</style>
</head>
<body class="small-container", style="margin-top: 70px;">
<div id='app'>
<h1> {{ document.title }} </h1>
<input type="text" placeholder="Search..." @input="search">
<table id='books' class="hovering">
<thead>
<th> Book </th>
<th> Author </th>
<th> Genre </th>
<th> Rating </th>
<th> Price </th>
</thead>
<tr v-for="book in list" v-bind:id="book.ID" v-on:click="inspect">
<td>{{ book.title }}</td>
<td>{{ book.author }}</td>
<td>{{ book.genre.name }}</td>
<td class="rating-stars">
{{ ('★'.repeat(Math.round(book.rating))+'☆☆☆☆☆').slice(0,5) }}
</td>
<td>{{ book.currency && book.currency.symbol }} {{ book.price }}</td>
</tr>
</table>
<div v-if="book">
<img v-bind:src="book.image" alt=""/>
<label style="text-align:right">
<span class="succeeded"> {{ order.succeeded }} </span>
<span class="failed"> {{ order.failed }} </span>
&nbsp;&nbsp; {{ book.stock }} in stock
</label>
<form @submit.prevent="submitOrder" style="float:right; display:flex; flex-direction:row-reverse">
<input type="number" v-model="order.amount" v-bind:class="{ failed: order.failed }" style="width:5em">
<input type="submit" value="Order:" class="muted-button">
</form>
<h4> {{ book.title }} </h4>
<p> {{ book.descr }} </p>
</div>
<div v-else>
( click on a row to see details... )
</div>
</div>
</body>
<script src="app.js"></script>
</html>

View File

@@ -1,16 +0,0 @@
ID;parent_ID;name
10;;Fiction
11;10;Drama
12;10;Poetry
13;10;Fantasy
14;10;Science Fiction
15;10;Romance
16;10;Mystery
17;10;Thriller
18;10;Dystopia
19;10;Fairy Tale
20;;Non-Fiction
21;20;Biography
22;21;Autobiography
23;20;Essay
24;20;Speech
1 ID parent_ID name
2 10 Fiction
3 11 10 Drama
4 12 10 Poetry
5 13 10 Fantasy
6 14 10 Science Fiction
7 15 10 Romance
8 16 10 Mystery
9 17 10 Thriller
10 18 10 Dystopia
11 19 10 Fairy Tale
12 20 Non-Fiction
13 21 20 Biography
14 22 21 Autobiography
15 23 20 Essay
16 24 20 Speech

View File

@@ -1,4 +0,0 @@
namespace sap.capire.bookshop; //> important for reflection
using from './db/schema';
using from './srv/cat-service';
using from './srv/admin-service';

View File

@@ -1 +0,0 @@
exports.CatalogService = require('./srv/cat-service')

View File

@@ -1,23 +0,0 @@
{
"name": "@capire/bookshop",
"version": "1.0.0",
"description": "A simple self-contained bookshop service.",
"dependencies": {
"@capire/common": "*",
"@sap/cds": "^4",
"express": "^4.17.1",
"passport": "0.4.1"
},
"scripts": {
"genres": "cds serve test/genres.cds",
"start": "cds run",
"watch": "cds watch"
},
"cds": {
"requires": {
"db": {
"kind": "sql"
}
}
}
}

View File

@@ -1,31 +0,0 @@
# Bookshop Getting Started Sample
This stand-alone sample introduces the essential tasks in the development of CAP-based services as also covered in the [Getting Started guide in capire](https://cap.cloud.sap/docs/get-started/in-a-nutshell).
## Hypothetical Use Cases
1. Build a service that allows to browse _Books_ and _Authors_.
2. Books have assigned _Genres_, which are organized hierarchically.
3. All users may browse books without login.
4. All entries are maintained by Administrators.
5. End users may order books (the actual order mgmt being out of scope).
## Running the Sample
```sh
npm run watch
```
## Content & Best Practices
| Links to capire | Sample files / folders |
| --------------------------------------------------------------------------------------------------------- | ------------------------------------ |
| [Project Setup & Layouts](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./`](./) |
| [Domain Modeling with CDS](https://cap.cloud.sap/docs/guides/domain-models) | [`./db/schema.cds`](./db/schema.cds) |
| [Defining Services](https://cap.cloud.sap/docs/guides/services#defining-services) | [`./srv/*.cds`](./srv) |
| [Single-purposed Services](https://cap.cloud.sap/docs/guides/services#single-purposed-services) | [`./srv/*.cds`](./srv) |
| [Providing & Consuming Providers](https://cap.cloud.sap/docs/guides/providing-services) | http://localhost:4004 |
| [Using Databases](https://cap.cloud.sap/docs/guides/databases) | [`./db/data/*.csv`](./db/data) |
| [Adding Custom Logic](https://cap.cloud.sap/docs/guides/service-impl) | [`./srv/*.js`](./srv) |
| Adding Tests | [`./test`](./test) |
| [Sharing for Reuse](https://cap.cloud.sap/docs/guides/reuse-and-compose) | [`./index.cds`](./index.cds) |

View File

@@ -1,5 +0,0 @@
using { sap.capire.bookshop as my } from '../db/schema';
service AdminService @(requires:'admin') {
entity Books as projection on my.Books;
entity Authors as projection on my.Authors;
}

View File

@@ -1,12 +0,0 @@
const cds = require('@sap/cds')
module.exports = cds.service.impl (function(){
this.before ('NEW','Authors', genid)
this.before ('NEW','Books', genid)
})
/** Generate primary keys for target entity in request */
async function genid (req) {
const {ID} = await cds.tx(req).run (SELECT.one.from(req.target).columns('max(ID) as ID'))
req.data.ID = ID - ID % 100 + 100 + 1
}

View File

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

View File

@@ -1,28 +0,0 @@
const cds = require('@sap/cds')
const { Books } = cds.entities ('sap.capire.bookshop')
class CatalogService extends cds.ApplicationService { init(){
// Reduce stock of ordered books if available stock suffices
this.on ('submitOrder', async req => {
const {book,amount} = req.data, tx = cds.tx(req)
let {stock} = await tx.read('stock').from(Books,book)
if (stock >= amount) {
await tx.update (Books,book).with ({ stock: stock -= amount })
await this.emit ('OrderedBook', { book, amount, buyer:req.user.id })
return { stock }
}
else return req.error (409,`${amount} exceeds stock for book #${book}`)
})
// Add some discount for overstocked books
this.after ('READ','Books', each => {
if (each.stock > 111) {
each.title += ` -- 11% discount!`
}
})
return super.init()
}}
module.exports = { CatalogService }

View File

@@ -1,4 +0,0 @@
using { sap.capire.bookshop as my } from '../db/schema';
service TestService {
entity Genres as projection on my.Genres;
}

View File

@@ -1,38 +0,0 @@
#################################################
#
# Genres
#
GET http://localhost:4004/test/Genres?
###
GET http://localhost:4004/test/Genres?
&$filter=parent_ID eq null&$select=name
&$expand=children($select=name)
###
POST http://localhost:4004/test/Genres?
Content-Type: application/json
{ "ID":100, "name":"Some Sample Genres...", "children":[
{ "ID":101, "name":"Cat", "children":[
{ "ID":102, "name":"Kitty", "children":[
{ "ID":103, "name":"Kitty Cat", "children":[
{ "ID":104, "name":"Aristocat" } ]},
{ "ID":105, "name":"Kitty Bat" } ]},
{ "ID":106, "name":"Catwoman", "children":[
{ "ID":107, "name":"Catalina" } ]} ]},
{ "ID":108, "name":"Catweazle" }
]}
###
GET http://localhost:4004/test/Genres(100)?
# &$expand=children
# &$expand=children($expand=children($expand=children($expand=children)))
###
DELETE http://localhost:4004/test/Genres(103)
###
DELETE http://localhost:4004/test/Genres(100)
###

View File

@@ -1,82 +0,0 @@
@server = http://localhost:4004
@me = Authorization: Basic {{$processEnv USER}}:
### ------------------------------------------------------------------------
# Get service info
GET {{server}}/browse
{{me}}
### ------------------------------------------------------------------------
# Get $metadata document
GET {{server}}/browse/$metadata
{{me}}
### ------------------------------------------------------------------------
# Browse Books as any user
GET {{server}}/browse/Books?
# &$select=title,stock
# &$expand=currency
# &sap-language=de
{{me}}
### ------------------------------------------------------------------------
# Fetch Authors as admin
GET {{server}}/admin/Authors?
# &$select=name,dateOfBirth,placeOfBirth
# &$expand=books($select=title;$expand=currency)
# &$filter=ID eq 101
# &sap-language=de
Authorization: Basic alice:
### ------------------------------------------------------------------------
# Create book
POST {{server}}/admin/Books
Content-Type: application/json;IEEE754Compatible=true
Authorization: Basic alice:
{
"ID": 2,
"title": "Poems : Pocket Poets",
"descr": "The Everyman's Library Pocket Poets hardcover series is popular for its compact size and reasonable price which does not compromise content. Poems: Bronte contains poems that demonstrate a sensibility elemental in its force with an imaginative discipline and flexibility of the highest order. Also included are an Editor's Note and an index of first lines.",
"author": { "ID": 101 },
"genre": { "ID": 12 },
"stock": 5,
"price": "12.05",
"currency": { "code": "USD" }
}
### ------------------------------------------------------------------------
# Put image to books
PUT {{server}}/admin/Books(2)/image
Content-Type: image/png
Authorization: Basic alice:
data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAANwAAADcCAYAAAAbWs+BAAAGwElEQVR4Ae3cwZFbNxBFUY5rkrDTmKAUk5QT03Aa44U22KC7NHptw+DRikVAXf8fzC3u8Hj4R4AAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAgZzAW26USQT+e4HPx+Mz+RRvj0e0kT+SD2cWAQK1gOBqH6sEogKCi3IaRqAWEFztY5VAVEBwUU7DCNQCgqt9rBKICgguymkYgVpAcLWPVQJRAcFFOQ0jUAsIrvaxSiAqILgop2EEagHB1T5WCUQFBBflNIxALSC42scqgaiA4KKchhGoBQRX+1glEBUQXJTTMAK1gOBqH6sEogKCi3IaRqAWeK+Xb1z9iN558fHxcSPS9p2ezx/ROz4e4TtIHt+3j/61hW9f+2+7/+UXbifjewIDAoIbQDWSwE5AcDsZ3xMYEBDcAKqRBHYCgtvJ+J7AgIDgBlCNJLATENxOxvcEBgQEN4BqJIGdgOB2Mr4nMCAguAFUIwnsBAS3k/E9gQEBwQ2gGklgJyC4nYzvCQwICG4A1UgCOwHB7WR8T2BAQHADqEYS2AkIbifjewIDAoIbQDWSwE5AcDsZ3xMYEEjfTzHwiK91B8npd6Q8n8/oGQ/ckRJ9vvQwv3BpUfMIFAKCK3AsEUgLCC4tah6BQkBwBY4lAmkBwaVFzSNQCAiuwLFEIC0guLSoeQQKAcEVOJYIpAUElxY1j0AhILgCxxKBtIDg0qLmESgEBFfgWCKQFhBcWtQ8AoWA4AocSwTSAoJLi5pHoBAQXIFjiUBaQHBpUfMIFAKCK3AsEUgLCC4tah6BQmDgTpPsHSTFs39p6fQ7Q770UsV/Ov19X+2OFL9wxR+rJQJpAcGlRc0jUAgIrsCxRCAtILi0qHkECgHBFTiWCKQFBJcWNY9AISC4AscSgbSA4NKi5hEoBARX4FgikBYQXFrUPAKFgOAKHEsE0gKCS4uaR6AQEFyBY4lAWkBwaVHzCBQCgitwLBFICwguLWoegUJAcAWOJQJpAcGlRc0jUAgIrsCxRCAt8J4eePq89B0ar3ZnyOnve/rfn1+400/I810lILirjtPLnC4guNNPyPNdJSC4q47Ty5wuILjTT8jzXSUguKuO08ucLiC400/I810lILirjtPLnC4guNNPyPNdJSC4q47Ty5wuILjTT8jzXSUguKuO08ucLiC400/I810lILirjtPLnC4guNNPyPNdJSC4q47Ty5wuILjTT8jzXSUguKuO08ucLiC400/I810l8JZ/m78+szP/zI47fJo7Q37vgJ7PHwN/07/3TOv/9gu3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhAcMPAxhNYBQS3avhMYFhg4P6H9J0maYHXuiMlrXf+vOfA33Turf3C5SxNItAKCK4lsoFATkBwOUuTCLQCgmuJbCCQExBcztIkAq2A4FoiGwjkBASXszSJQCsguJbIBgI5AcHlLE0i0AoIriWygUBOQHA5S5MItAKCa4lsIJATEFzO0iQCrYDgWiIbCOQEBJezNIlAKyC4lsgGAjkBweUsTSLQCgiuJbKBQE5AcDlLkwi0Akff//Dz6U+/I6U1/sUNr3bnytl3kPzi4bXb/cK1RDYQyAkILmdpEoFWQHAtkQ0EcgKCy1maRKAVEFxLZAOBnIDgcpYmEWgFBNcS2UAgJyC4nKVJBFoBwbVENhDICQguZ2kSgVZAcC2RDQRyAoLLWZpEoBUQXEtkA4GcgOByliYRaAUE1xLZQCAnILicpUkEWgHBtUQ2EMgJCC5naRKBVkBwLZENBHIC/4M7TXIv+3PS22d24qvdQfL3C/7N5P5i/MLlLE0i0AoIriWygUBOQHA5S5MItAKCa4lsIJATEFzO0iQCrYDgWiIbCOQEBJezNIlAKyC4lsgGAjkBweUsTSLQCgiuJbKBQE5AcDlLkwi0AoJriWwgkBMQXM7SJAKtgOBaIhsI5AQEl7M0iUArILiWyAYCOQHB5SxNItAKCK4lsoFATkBwOUuTCBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIAAAQIECBAgQIDAvyrwDySEJ2VQgUSoAAAAAElFTkSuQmCC
### ------------------------------------------------------------------------
# Reading image from from the server directly
GET {{server}}/browse/Books(2)/image
### ------------------------------------------------------------------------
# Submit Order as authenticated user
# (send that three times to get out-of-stock message)
POST {{server}}/browse/submitOrder
Content-Type: application/json
{{me}}
{ "book":201, "amount":5 }
### ------------------------------------------------------------------------
# Browse Genres
GET {{server}}/browse/Genres?
# &$filter=parent_ID eq null&$select=name
# &$expand=children($select=name)
{{me}}

View File

@@ -1,12 +0,0 @@
code;name;descr
AU;Australia;Commonwealth of Australia
CA;Canada;Canada
CN;China;People's Republic of China (PRC)
FR;France;French Republic
DE;Germany;Federal Republic of Germany
IN;India;Republic of India
IL;Israel;State of Israel
MM;Myanmar;Republic of the Union of Myanmar
GB;United Kingdom;United Kingdom of Great Britain and Northern Ireland
US;United States;United States of America (USA)
EU;European Union;European Union
1 code name descr
2 AU Australia Commonwealth of Australia
3 CA Canada Canada
4 CN China People's Republic of China (PRC)
5 FR France French Republic
6 DE Germany Federal Republic of Germany
7 IN India Republic of India
8 IL Israel State of Israel
9 MM Myanmar Republic of the Union of Myanmar
10 GB United Kingdom United Kingdom of Great Britain and Northern Ireland
11 US United States United States of America (USA)
12 EU European Union European Union

View File

@@ -1,12 +0,0 @@
code;locale;name;descr
AU;de;Australien;Commonwealth Australien
CA;de;Kanada;Canada
CN;de;China;Volksrepublik China
FR;de;Frankreich;Republik Frankreich
DE;de;Deutschland;Bundesrepublik Deutschland
IN;de;Indien;Republik Indien
IL;de;Israel;Staat Israel
MM;de;Myanmar;Republik der Union Myanmar
GB;de;Vereinigtes Königreich;Vereinigtes Königreich Großbritannien und Nordirland
US;de;Vereinigte Staaten;Vereinigte Staaten von Amerika
EU;de;Europäische Union;Europäische Union
1 code locale name descr
2 AU de Australien Commonwealth Australien
3 CA de Kanada Canada
4 CN de China Volksrepublik China
5 FR de Frankreich Republik Frankreich
6 DE de Deutschland Bundesrepublik Deutschland
7 IN de Indien Republik Indien
8 IL de Israel Staat Israel
9 MM de Myanmar Republik der Union Myanmar
10 GB de Vereinigtes Königreich Vereinigtes Königreich Großbritannien und Nordirland
11 US de Vereinigte Staaten Vereinigte Staaten von Amerika
12 EU de Europäische Union Europäische Union

View File

@@ -1,12 +0,0 @@
code;symbol;name;descr;numcode;minor;exponent
EUR;€;Euro;European Euro;978;Cent;2
USD;$;US Dollar;United States Dollar;840;Cent;2
CAD;$;Canadian Dollar;Canadian Dollar;124;Cent;2
AUD;$;Australian Dollar;Australian Dollar;036;Cent;2
GBP;£;British Pound;Great Britain Pound;826;Penny;2
ILS;₪;Shekel;Israeli New Shekel;376;Agorat;2
INR;₹;Rupee;Indian Rupee;356;Paise;2
QAR;﷼;Riyal;Katar Riyal;356;Dirham;2
SAR;﷼;Riyal;Saudi Riyal;682;Halala;2
JPY;¥;Yen;Japanese Yen;392;Sen;2
CNY;¥;Yuan;Chinese Yuan Renminbi;156;Jiao;1
1 code symbol name descr numcode minor exponent
2 EUR Euro European Euro 978 Cent 2
3 USD $ US Dollar United States Dollar 840 Cent 2
4 CAD $ Canadian Dollar Canadian Dollar 124 Cent 2
5 AUD $ Australian Dollar Australian Dollar 036 Cent 2
6 GBP £ British Pound Great Britain Pound 826 Penny 2
7 ILS Shekel Israeli New Shekel 376 Agorat 2
8 INR Rupee Indian Rupee 356 Paise 2
9 QAR Riyal Katar Riyal 356 Dirham 2
10 SAR Riyal Saudi Riyal 682 Halala 2
11 JPY ¥ Yen Japanese Yen 392 Sen 2
12 CNY ¥ Yuan Chinese Yuan Renminbi 156 Jiao 1

View File

@@ -1,5 +0,0 @@
code;name
de;German
fr;French
en;English
en_GB;British English
1 code name
2 de German
3 fr French
4 en English
5 en_GB British English

View File

@@ -1,10 +0,0 @@
code;locale;name
de;en;German
de;de;Deutsch
de;fr;Allemande
fr;en;French
fr;de;Französisch
fr;fr;Francais
en;en;English
en;de;Englisch
en;fr;Anglais
1 code locale name
2 de en German
3 de de Deutsch
4 de fr Allemande
5 fr en French
6 fr de Französisch
7 fr fr Francais
8 en en English
9 en de Englisch
10 en fr Anglais

View File

@@ -1,45 +0,0 @@
using { sap } from '@sap/cds/common';
extend sap.common.Currencies with {
// Currencies.code = ISO 4217 alphabetic three-letter code
// with the first two letters being equal to ISO 3166 alphabetic country codes
// See also:
// [1] https://www.iso.org/iso-4217-currency-codes.html
// [2] https://www.currency-iso.org/en/home/tables/table-a1.html
// [3] https://www.ibm.com/support/knowledgecenter/en/SSZLC2_7.0.0/com.ibm.commerce.payments.developer.doc/refs/rpylerl2mst97.htm
numcode : Integer;
exponent : Integer; //> e.g. 2 --> 1 Dollar = 10^2 Cent
minor : String; //> e.g. 'Cent'
}
/**
* The Code Lists below are designed as optional extensions to
* the base schema. Switch them on by adding an Association to
* one of the code list entities in your models or by:
* annotate sap.common.Countries with @cds.persistence.skip:false;
*/
context sap.common_countries {
extend sap.common.Countries {
regions : Composition of many Regions on regions._parent = $self.code;
}
entity Regions : sap.common.CodeList {
key code : String(5); // ISO 3166-2 alpha5 codes, e.g. DE-BW
children : Composition of many Regions on children._parent = $self.code;
cities : Composition of many Cities on cities.region = $self;
_parent : String(11);
}
entity Cities : sap.common.CodeList {
key code : String(11);
region : Association to Regions;
districts : Composition of many Districts on districts.city = $self;
}
entity Districts : sap.common.CodeList {
key code : String(11);
city : Association to Cities;
}
}

View File

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

View File

@@ -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;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;16
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;15;EUR;13
ID;title;descr;author_ID;stock;price;currency_code
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
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
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
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
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;15;EUR
1 ID title descr author_ID stock price currency_code genre_ID
2 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
3 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
4 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
5 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 16
6 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 15 EUR 13

View File

@@ -1,5 +1,4 @@
ID;locale;title;descr
201;de;Sturmhöhe;Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (18181848). 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 dEllis 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 ID locale title descr
2 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.
3 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
4 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.

View File

@@ -0,0 +1,4 @@
ID;amount;parent_ID;book_ID;netAmount
58040e66-1dcd-4ffb-ab10-fdce32028b79;1;7e2f2640-6866-4dcf-8f4d-3027aa831cad;201;11.11
64e718c9-ff99-47f1-8ca3-950c850777d4;1;7e2f2640-6866-4dcf-8f4d-3027aa831cad;271;15
e9641166-e050-4261-bfee-d1e797e6cb7f;2;64e718c9-ff99-47f1-8ca3-950c850777d4;252;28
1 ID amount parent_ID book_ID netAmount
2 58040e66-1dcd-4ffb-ab10-fdce32028b79 1 7e2f2640-6866-4dcf-8f4d-3027aa831cad 201 11.11
3 64e718c9-ff99-47f1-8ca3-950c850777d4 1 7e2f2640-6866-4dcf-8f4d-3027aa831cad 271 15
4 e9641166-e050-4261-bfee-d1e797e6cb7f 2 64e718c9-ff99-47f1-8ca3-950c850777d4 252 28

View File

@@ -0,0 +1,3 @@
ID;modifiedAt;createdAt;createdBy;modifiedBy;OrderNo;currency_code
7e2f2640-6866-4dcf-8f4d-3027aa831cad;;2019-01-31;john.doe@test.com;;1;EUR
64e718c9-ff99-47f1-8ca3-950c850777d4;;2019-01-30;jane.doe@test.com;;2;EUR
1 ID modifiedAt createdAt createdBy modifiedBy OrderNo currency_code
2 7e2f2640-6866-4dcf-8f4d-3027aa831cad 2019-01-31 john.doe@test.com 1 EUR
3 64e718c9-ff99-47f1-8ca3-950c850777d4 2019-01-30 jane.doe@test.com 2 EUR

View File

@@ -0,0 +1,7 @@
code;symbol;name;descr
EUR;€;Euro;European Euro
USD;$;US Dollar;United States Dollar
CAD;$;Canadian Dollar;Canadian Dollar
AUD;$;Australian Dollar;Australian Dollar
GBP;£;Pound;Great Britain Pound
ILS;₪;Shekel;Israeli New Shekel
1 code symbol name descr
2 EUR Euro European Euro
3 USD $ US Dollar United States Dollar
4 CAD $ Canadian Dollar Canadian Dollar
5 AUD $ Australian Dollar Australian Dollar
6 GBP £ Pound Great Britain Pound
7 ILS Shekel Israeli New Shekel

View File

@@ -1,16 +1,14 @@
using { Currency, managed, sap } from '@sap/cds/common';
namespace sap.capire.bookshop;
using { Currency, managed, cuid } from '@sap/cds/common';
entity Books : managed {
key ID : Integer;
title : localized String(111);
descr : localized String(1111);
author : Association to Authors;
genre : Association to Genres;
stock : Integer;
price : Decimal;
price : Decimal(9,2);
currency : Currency;
image : LargeBinary @Core.MediaType : 'image/png';
}
entity Authors : managed {
@@ -23,9 +21,15 @@ entity Authors : managed {
books : Association to many Books on books.author = $self;
}
/** Hierarchically organized Code List for Genres */
entity Genres : sap.common.CodeList {
key ID : Integer;
parent : Association to Genres;
children : Composition of many Genres on children.parent = $self;
entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many OrderItems on Items.parent = $self;
total : Decimal(9,2) @readonly;
currency : Currency;
}
entity OrderItems : cuid {
parent : Association to Orders;
book : Association to Books;
amount : Integer;
netAmount : Decimal(9,2);
}

View File

@@ -1,2 +0,0 @@
# cds.requires.messaging.kind = file-based-messaging
PORT = 4004

View File

@@ -1,25 +0,0 @@
Books = Books
Book = Book
ID = ID
Title = Title
Author = Author
AuthorID = Author ID
Stock = Stock
Name = Name
AuthorName = Author's Name
DateOfBirth = Date of Birth
DateOfDeath = Date of Death
PlaceOfBirth = Place of Birth
PlaceOfDeath = Place of Death
Authors = Authors
Order = Order
Orders = Orders
Price = Price
Genre = Genre
Genres = Genres
SubGenres = Sub Genres
NumCode = Numeric Code
MinorUnit = Minor Unit
Exponent = Exponent

View File

@@ -1,72 +0,0 @@
using AdminService from '@capire/bookshop';
////////////////////////////////////////////////////////////////////////////
//
// Books Object Page
//
annotate AdminService.Books with @(
UI: {
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>General}', Target: '@UI.FieldGroup#General'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Translations}', Target: 'texts/@UI.LineItem'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Admin}', Target: '@UI.FieldGroup#Admin'},
],
FieldGroup#General: {
Data: [
{Value: title},
{Value: author_ID},
{Value: genre_ID},
{Value: descr},
]
},
FieldGroup#Details: {
Data: [
{Value: stock},
{Value: price},
{Value: currency_code, Label: '{i18n>Currency}'},
]
},
FieldGroup#Admin: {
Data: [
{Value: createdBy},
{Value: createdAt},
{Value: modifiedBy},
{Value: modifiedAt}
]
}
}
);
////////////////////////////////////////////////////////////
//
// Draft for Localized Data
//
annotate sap.capire.bookshop.Books with @fiori.draft.enabled;
annotate AdminService.Books with @odata.draft.enabled;
annotate AdminService.Books_texts with @(
UI: {
Identification: [{Value:title}],
SelectionFields: [ locale, title ],
LineItem: [
{Value: locale, Label: 'Locale'},
{Value: title, Label: 'Title'},
{Value: descr, Label: 'Description'},
]
}
);
// Add Value Help for Locales
annotate AdminService.Books_texts {
locale @ValueList:{entity:'Languages',type:#fixed}
}
// In addition we need to expose Languages through AdminService
using { sap } from '@sap/cds/common';
extend service AdminService {
entity Languages as projection on sap.common.Languages;
}

View File

@@ -1,8 +0,0 @@
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("admin.Component", {
metadata: { manifest: "json" }
});
});
/* eslint no-undef:0 */

View File

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

View File

@@ -1,7 +0,0 @@
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("bookshop.Component", {
metadata: { manifest: "json" }
});
});
/* eslint no-undef:0 */

View File

@@ -1,257 +0,0 @@
/*
Common Annotations shared by all apps
*/
using { sap.capire.bookshop as my } from '@capire/bookshop';
using { sap.common } from '@capire/common';
////////////////////////////////////////////////////////////////////////////
//
// Books Lists
//
annotate my.Books with @(
Common.SemanticKey: [title],
UI: {
Identification: [{Value:title}],
SelectionFields: [ ID, author_ID, price, currency_code ],
LineItem: [
{Value: ID},
{Value: title},
{Value: author.name, Label:'{i18n>Author}'},
{Value: genre.name},
{Value: stock},
{Value: price},
{Value: currency.symbol, Label:' '},
]
}
) {
author @ValueList.entity:'Authors';
};
////////////////////////////////////////////////////////////////////////////
//
// Books Details
//
annotate my.Books with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Book}',
TypeNamePlural: '{i18n>Books}',
Title: {Value: title},
Description: {Value: author.name}
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books Elements
//
annotate my.Books with {
ID @title:'{i18n>ID}' @UI.HiddenFilter;
title @title:'{i18n>Title}';
genre @title:'{i18n>Genre}' @Common: { Text: genre.name, TextArrangement: #TextOnly };
author @title:'{i18n>Author}' @Common: { Text: author.name, TextArrangement: #TextOnly };
price @title:'{i18n>Price}';
stock @title:'{i18n>Stock}';
descr @UI.MultiLineText;
}
////////////////////////////////////////////////////////////////////////////
//
// Genres List
//
annotate my.Genres with @(
Common.SemanticKey: [name],
UI: {
SelectionFields: [ name ],
LineItem:[
{Value: name},
{Value: parent.name, Label: 'Main Genre'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Genre Details
//
annotate my.Genres with @(
UI: {
Identification: [{Value:name}],
HeaderInfo: {
TypeName: '{i18n>Genre}',
TypeNamePlural: '{i18n>Genres}',
Title: {Value: name},
Description: {Value: ID}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>SubGenres}', Target: 'children/@UI.LineItem'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Genres Elements
//
annotate my.Genres with {
ID @title: '{i18n>ID}';
name @title: '{i18n>Genre}';
}
////////////////////////////////////////////////////////////////////////////
//
// Authors List
//
annotate my.Authors with @(
Common.SemanticKey: [name],
UI: {
Identification: [{Value:name}],
SelectionFields: [ name ],
LineItem:[
{Value: ID},
{Value: name},
{Value: dateOfBirth},
{Value: dateOfDeath},
{Value: placeOfBirth},
{Value: placeOfDeath},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Author Details
//
annotate my.Authors with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Author}',
TypeNamePlural: '{i18n>Authors}',
Title: {Value: name},
Description: {Value: dateOfBirth}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Target: 'books/@UI.LineItem'},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Authors Elements
//
annotate my.Authors with {
ID @title:'{i18n>ID}' @UI.HiddenFilter;
name @title:'{i18n>Name}';
dateOfBirth @title:'{i18n>DateOfBirth}';
dateOfDeath @title:'{i18n>DateOfDeath}';
placeOfBirth @title:'{i18n>PlaceOfBirth}';
placeOfDeath @title:'{i18n>PlaceOfDeath}';
}
////////////////////////////////////////////////////////////////////////////
//
// Languages List
//
annotate common.Languages with @(
Common.SemanticKey: [code],
Identification: [{Value:code}],
UI: {
SelectionFields: [ name, descr ],
LineItem:[
{Value: code},
{Value: name},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Language Details
//
annotate common.Languages with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Language}',
TypeNamePlural: '{i18n>Languages}',
Title: {Value: name},
Description: {Value: descr}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
],
FieldGroup#Details: {
Data: [
{Value: code},
{Value: name},
{Value: descr}
]
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currencies List
//
annotate common.Currencies with @(
Common.SemanticKey: [code],
Identification: [{Value:code}],
UI: {
SelectionFields: [ name, descr ],
LineItem:[
{Value: descr},
{Value: symbol},
{Value: code},
],
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currency Details
//
annotate common.Currencies with @(
UI: {
HeaderInfo: {
TypeName: '{i18n>Currency}',
TypeNamePlural: '{i18n>Currencies}',
Title: {Value: descr},
Description: {Value: code}
},
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Extended}', Target: '@UI.FieldGroup#Extended'},
],
FieldGroup#Details: {
Data: [
{Value: name},
{Value: symbol},
{Value: code},
{Value: descr}
]
},
FieldGroup#Extended: {
Data: [
{Value: numcode},
{Value: minor},
{Value: exponent}
]
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Currencies Elements
//
annotate common.Currencies with {
numcode @title:'{i18n>NumCode}';
minor @title:'{i18n>MinorUnit}';
exponent @title:'{i18n>Exponent}';
}

View File

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

View File

@@ -1,30 +0,0 @@
{
"name": "@capire/fiori",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "*",
"@capire/reviews": "*",
"@capire/orders": "*",
"@capire/common": "*",
"@sap/cds": "^4",
"express": "^4.17.1",
"passport": "0.4.1"
},
"scripts": {
"start": "cds run --in-memory?",
"watch": "cds watch"
},
"cds": {
"requires": {
"ReviewsService": {
"kind": "odata", "model": "@capire/reviews"
},
"OrdersService": {
"kind": "odata", "model": "@capire/orders"
},
"db": {
"kind": "sql"
}
}
}
}

View File

@@ -1,18 +0,0 @@
const cds = require ('@sap/cds')
cds.once('bootstrap',(app)=>{
app.use ('/orders/webapp', _from('@capire/orders/app/orders/webapp/manifest.json'))
app.use ('/bookshop', _from('@capire/bookshop/app/vue/index.html'))
app.use ('/reviews', _from('@capire/reviews/app/vue/index.html'))
})
cds.once('served', require('./srv/mashup'))
module.exports = cds.server
// -----------------------------------------------------------------------
// Helper for serving static content from npm-installed packages
const {static} = require('express')
const {dirname} = require('path')
const _from = target => static (dirname (require.resolve(target)))

View File

@@ -1,25 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Mashing up imported models...
//
using { sap.capire.bookshop.Books } from '@capire/bookshop';
//
// Extend Books with access to Reviews and average ratings
//
using { ReviewsService.Reviews } from '@capire/reviews';
extend Books with {
reviews : Composition of many Reviews on reviews.subject = $self.ID;
rating : Decimal;
}
//
// Extend Orders with Books as Products
//
using { sap.capire.orders.Orders_Items } from '@capire/orders';
extend Orders_Items with {
book : Association to Books on product.ID = book.ID
}

View File

@@ -1,59 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// Mashing up provided and required services...
//
module.exports = async()=>{ // called by server.js
const cds = require('@sap/cds')
const CatalogService = await cds.connect.to ('CatalogService')
const ReviewsService = await cds.connect.to ('ReviewsService')
const OrdersService = await cds.connect.to ('OrdersService')
const db = await cds.connect.to ('db')
// reflect entity definitions used below...
const { Books } = db.entities ('sap.capire.bookshop')
//
// Delegate requests to read reviews to the ReviewsService
// Note: prepend is neccessary to intercept generic default handler
//
CatalogService.prepend (srv => srv.on ('READ', 'Books/reviews', (req) => {
console.debug ('> delegating request to ReviewsService')
const [id] = req.params, { columns, limit } = req.query.SELECT
return ReviewsService.tx(req).read ('Reviews',columns).limit(limit).where({subject:String(id)})
}))
//
// Create an order with the OrdersService when CatalogService signals a new order
//
CatalogService.on ('OrderedBook', async (msg) => {
const { book, amount, buyer } = msg.data
const { title, price } = await db.tx(msg).read (Books, book, b => { b.title, b.price })
return OrdersService.tx(msg).create ('Orders').entries({
OrderNo: 'Order at '+ (new Date).toLocaleString(),
Items: [{ product:{ID:`${book}`}, title, price, amount }],
buyer, createdBy: buyer
})
})
//
// Update Books' average ratings when ReviewsService signals updatd reviews
//
ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { subject, rating } = msg.data
return UPDATE(Books,subject).with({rating})
// ^ Note: the framework will execute this and take care for db.tx
})
//
// Reduce stock of ordered books for orders are created from Orders admin UI
//
OrdersService.on ('OrderChanged', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { product, deltaAmount } = msg.data
return UPDATE (Books) .where ('ID =', product)
.and ('stock >=', deltaAmount)
.set ('stock -=', deltaAmount)
})
}

View File

@@ -1,14 +0,0 @@
const cds = require ('./sap-cds')
module.exports = class extends cds.build.Task {
async build ({src='*'}) {
this.log (`Generating edmx output for '${src}'...`)
const csn = await this.model(src)
return Promise.all (csn.services.map (({name:service}) => {
const edmx = cds.compile(csn).to.edmx({service})
return this.write(edmx).to(`{srv}/src/main/resources/${service}.edmx`)
}))
}
}

View File

@@ -1,51 +0,0 @@
const cds = require ('@sap/cds/lib')
const path = require('path')
const cwd = process.cwd()
const _resolve = (root,file) => path.resolve (cwd, root, file.replace(/{(app|db|srv)}\/?/g, (_,folder) => cds.env.folders[folder]))
const _local = (file) => path.relative (cwd,file)
class BuildTask {
async build (options) {}
async clean (options) {}
async model(src='*') {
return cds.linked (await cds.load(src))
}
log(...args) { return console.log(...args) }
warn(...args) { return console.warn(...args) }
error(...args) { return console.error(...args) }
write(x) {
if (typeof x === 'object') x = JSON.stringify(x,null,' ')
return { to: async (dst)=>{
const file = _resolve (this.options.dest, dst)
await cds.utils.mkdirp (path.dirname (file))
await cds.utils.promises.writeFile (file,x)
console.log ('> wrote:', _local(file))
return file
}}
}
copy(x) {
return { to: async (dst) => {} }
}
}
module.exports = Object.assign (cds, {
build: {
run (tasks, _options) {
const options = { dest:'gen', ..._options }
return Promise.all(tasks.map (async each => {
const task = Object.assign (new each, {options})
await task.build (options)
}))
},
Task: BuildTask
}
})

View File

@@ -1,5 +0,0 @@
const cds = require ('./sap-cds')
const task = require('./build-task')
cds.build.run ([task], {src:process.argv[2]})
.catch(console.error)

View File

@@ -1,65 +0,0 @@
@bookshop = http://localhost:4004
@reviews-service = {{bookshop}}/reviews
# Uncomment this when running a separate reviews service
@reviews-service = http://localhost:4005/reviews
#################################################
#
# Reviews Service
#
GET {{reviews-service}}/Reviews
###
POST {{reviews-service}}/Reviews
Authorization: Basic {{$processEnv USER}}:
Content-Type: application/json
{"subject":"201", "title":"boo", "rating":3 }
#################################################
#
# Bookshop Services
#
GET {{bookshop}}/browse/Books/201/reviews?
&$select=rating,date,title
&$top=3
###
GET {{bookshop}}/browse/Books(201)?
&$select=ID,title,rating
&$expand=reviews
#################################################
#
# Orders Service, incl. draft choreography
#
@newOrderID = e939604c-ab83-4d4f-bdb6-95fe30b3773e
GET {{bookshop}}/orders/Orders
### Create order, still inactive
POST {{bookshop}}/orders/Orders
Content-Type: application/json
{"ID": "{{newOrderID}}"}
### Get inactive order. We have to specify `IsActiveEntity`.
GET {{bookshop}}/orders/Orders(ID={{newOrderID}},IsActiveEntity=false)
### Activate order using `.../<servicename>.draftActivate`
POST {{bookshop}}/orders/Orders(ID={{newOrderID}},IsActiveEntity=false)/OrdersService.draftActivate
Content-Type: application/json
### Get active order
GET {{bookshop}}/orders/Orders(ID={{newOrderID}},IsActiveEntity=true)

View File

@@ -1,7 +0,0 @@
{
"name": "@capire/hello-world",
"version": "1.0.0",
"scripts": {
"watch": "cds serve world.cds"
}
}

View File

@@ -1 +0,0 @@
GET http://localhost:4004/say/hello(to='world')

View File

@@ -1,3 +0,0 @@
service say {
function hello (to:String) returns String;
}

View File

@@ -1,3 +0,0 @@
module.exports = class say {
hello(req) { return `Hello ${req.data.to}!` }
}

View File

@@ -1,13 +0,0 @@
namespace sap.capire.media;
entity Media {
key id:Integer;
@Core.MediaType: mediaType
content : LargeBinary ;
@Core.IsMediaType: true
mediaType : String;
fileName : String;
applicationName:String;
}

View File

@@ -1,2 +0,0 @@
using from './db/data-model';
using from './srv/media-service';

View File

@@ -1,19 +0,0 @@
{
"name": "@capire/media",
"version": "1.0.0",
"dependencies": {
"lokijs": "^1.5.6"
},
"files": [
"db",
"srv",
"index.cds"
],
"cds": {
"requires": {
"db": {
"kind": "sql"
}
}
}
}

View File

@@ -1,8 +0,0 @@
using { sap.capire.media as db } from '../db/data-model';
namespace sap.capire.media;
service MediaServer {
entity Media as projection on db.Media ;
}

View File

@@ -1,68 +0,0 @@
const loki = require('lokijs')
const db = new loki('DB')
const mediaDB = db.addCollection('Media')
const { Readable, PassThrough } = require('stream')
module.exports = srv => {
srv.before('CREATE', 'Media', req => {
const obj = mediaDB.insert({ media: '' })
req.data.id = obj.$loki
})
srv.on('UPDATE', 'Media', (req, next) => {
const url = req._.req.path
if (url.includes('content')) {
const id = req.data.id
const obj = mediaDB.get(id)
if (!obj) {
req.reject(404, 'No record found for the ID')
return
}
const stream = new PassThrough()
const chunks = []
stream.on('data', chunk => {
chunks.push(chunk)
})
stream.on('end', () => {
obj.media = Buffer.concat(chunks).toString('base64')
mediaDB.update(obj)
})
req.data.content.pipe(stream)
} else return next()
})
srv.on('READ', 'Media', (req, next) => {
const url = req._.req.path
if (url.includes('content')) {
const id = req.data.id
const mediaObj = mediaDB.get(id)
if (!mediaObj) {
req.reject(404, 'Media not found for the ID')
return
}
const decodedMedia = new Buffer(
mediaObj.media.split(';base64,').pop(),
'base64'
)
return _formatResult(decodedMedia)
} else return next() //> delegate to next/default handlers
})
srv.on('DELETE', 'Media', (req, next) => {
const id = req.data.id
mediaDB
.chain()
.find({ $loki: id })
.remove()
return next() //> delegate to next/default handlers
})
function _formatResult (decodedMedia) {
const readable = new Readable()
const result = new Array()
readable.push(decodedMedia)
readable.push(null)
result.push({ value: readable })
return result
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.0 KiB

View File

@@ -1,35 +0,0 @@
### Requires REST Client for VS Code
### https://marketplace.visualstudio.com/items?itemName=humao.rest-client
###
@protocol = http
@host = localhost
@port = 4004
### Read Pictures
GET {{protocol}}://{{host}}:{{port}}/media-server/Media
Authorization: Basic admin:
### Create Picture with mediatype
POST {{protocol}}://{{host}}:{{port}}/media-server/Media
Authorization: Basic admin:
Accept: application/json
Content-Type: application/json
{
"id": 1,
"mediaType": "image/png"
}
### Upload Binary PNG
PUT {{protocol}}://{{host}}:{{port}}/media-server/Media(1)/content
Authorization: Basic admin:
Content-Type: image/png
< ./Test.png
### Read Binary
GET {{protocol}}://{{host}}:{{port}}/media-server/Media(1)/content
Authorization: Basic admin:
### Delete Image
DELETE {{protocol}}://{{host}}:{{port}}/media-server/Media(1)
Authorization: Basic admin:

64
mta.yaml Normal file
View File

@@ -0,0 +1,64 @@
####### Generated mta.yaml based on template version 0.2.0
####### appName = capire-bookshop
####### language=nodejs; multiTenant=
####### approuter=
_schema-version: '3.1'
ID: sap.capire-bookshop
version: 1.0.0
description: "A simple bookshop application, build in a self-contained all-in-one fashion, i.e. w/o reusing other packages."
build-parameters:
before-all:
- builder: custom
commands:
- npm install
- cds build/all
parameters:
enable-parallel-deployments: true
modules:
############## SERVER MODULE ##########################
- name: capire-bookshop-srv
type: nodejs
path: gen/srv
properties:
EXIT: 1 # required by deploy.js task to terminate
requires:
#### Resources extracted from CAP configuration ####
- name: capire-bookshop-db
provides:
- name: srv-binding # required by consumers of CAP services (e.g. approuter)
properties:
srv-url: ${default-url}
############################################################
############## SIDECAR MODULE #########################
- name: db
type: hdb
path: gen/db
parameters:
app-name: capire-bookshop-db
requires:
#### Hana and xsuaa resources extracted from CAP configuration ####
- name: capire-bookshop-db
############################################################
############## RESOURCES ##################################
resources:
##### Services extracted from CAP configuration ####
##### 'service-plan' can be configured via 'cds.requires.<name>.vcap.plan'
- name: capire-bookshop-db
type: com.sap.xs.hdi-container
parameters:
properties:
hdi-service-name: ${service-name} # required for Java case
############################################################

View File

@@ -1,2 +0,0 @@
cds.requires.messaging.kind = file-based-messaging
PORT = 4006

View File

@@ -1,39 +0,0 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="X-UA-Compatible" content="IE=edge" />
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Bookshop</title>
<script>
window["sap-ushell-config"] = {
defaultRenderer: "fiori2",
applications: {
"manage-orders": {
title: "Manage Orders",
description: "... testing FE v42",
additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL",
url: "/orders/webapp",
navigationMode: "embedded"
}
}
};
</script>
<script id="sap-ushell-bootstrap" src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<script id="sap-ui-bootstrap" src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"
data-sap-ui-frameOptions="allow"
></script>
<script>
sap.ui.getCore().attachInit(()=> sap.ushell.Container.createRenderer().placeAt("content"))
</script>
</head>
<body class="sapUiBody" id="content"></body>
</html>

View File

@@ -1,5 +0,0 @@
/*
This model controls what gets served to Fiori frontends...
*/
using from './orders/fiori-service';

View File

@@ -1,8 +0,0 @@
sap.ui.define(["sap/fe/core/AppComponent"], function(AppComponent) {
"use strict";
return AppComponent.extend("orders.Component", {
metadata: { manifest: "json" }
});
});
/* eslint no-undef:0 */

View File

@@ -1,3 +0,0 @@
ID;createdAt;createdBy;buyer;OrderNo;currency_code
7e2f2640-6866-4dcf-8f4d-3027aa831cad;2019-01-31;john.doe@test.com;john.doe@test.com;1;EUR
64e718c9-ff99-47f1-8ca3-950c850777d4;2019-01-30;jane.doe@test.com;jane.doe@test.com;2;EUR
1 ID createdAt createdBy buyer OrderNo currency_code
2 7e2f2640-6866-4dcf-8f4d-3027aa831cad 2019-01-31 john.doe@test.com john.doe@test.com 1 EUR
3 64e718c9-ff99-47f1-8ca3-950c850777d4 2019-01-30 jane.doe@test.com jane.doe@test.com 2 EUR

View File

@@ -1,4 +0,0 @@
ID;up__ID;amount;product_ID;title;price
58040e66-1dcd-4ffb-ab10-fdce32028b79;7e2f2640-6866-4dcf-8f4d-3027aa831cad;1;201;Wuthering Heights;11.11
64e718c9-ff99-47f1-8ca3-950c850777d4;7e2f2640-6866-4dcf-8f4d-3027aa831cad;1;271;Catweazle;15
e9641166-e050-4261-bfee-d1e797e6cb7f;64e718c9-ff99-47f1-8ca3-950c850777d4;2;252;Eleonora;28
1 ID up__ID amount product_ID title price
2 58040e66-1dcd-4ffb-ab10-fdce32028b79 7e2f2640-6866-4dcf-8f4d-3027aa831cad 1 201 Wuthering Heights 11.11
3 64e718c9-ff99-47f1-8ca3-950c850777d4 7e2f2640-6866-4dcf-8f4d-3027aa831cad 1 271 Catweazle 15
4 e9641166-e050-4261-bfee-d1e797e6cb7f 64e718c9-ff99-47f1-8ca3-950c850777d4 2 252 Eleonora 28

View File

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

View File

@@ -1,6 +0,0 @@
/*
This model controls what gets exposed
*/
namespace sap.capire.orders;
using from './srv/orders-service';
using from './db/schema';

Some files were not shown because too many files have changed in this diff Show More