Compare commits

..

5 Commits

Author SHA1 Message Date
Christian Georgi
810cac12d0 Better clone path 2020-04-20 17:09:07 +02:00
Christian Georgi
2cb2842dbe Add package-lock.json 2020-04-02 15:33:46 +02:00
Christian Georgi
e467635696 Update readme 2020-03-23 17:05:49 +01:00
Harini Gunabalan
a12cd7f4ec Update README.md 2020-03-21 23:32:56 +01:00
Harini Gunabalan
fa187be617 Adding code of OpenSAP week2 unit 1 demo 2020-03-21 23:31:43 +01:00
126 changed files with 2450 additions and 6732 deletions

View File

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

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: [10.x, 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 connection.properties
default-env.json default-env.json
packages/messageBox 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,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
app.use('/-/:tarball', (req,res,next) => {
const url = decodeURIComponent(req.url)
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,20 +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"
],
// 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

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

14
.vscode/tasks.json vendored
View File

@@ -4,12 +4,14 @@
"version": "2.0.0", "version": "2.0.0",
"tasks": [ "tasks": [
{ {
"type": "npm", "type": "npm", "script": "watch", "path": "packages/bookshop/",
"script": "jest", "options": { "env": { "PORT": "4004" }},
"group": { "presentation": { "group": "A" }
"kind": "test", },
"isDefault": true {
} "type": "npm", "script": "watch", "path": "packages/reviews-service/",
"options": { "env": { "PORT": "5005" }},
"presentation": { "group": "A" }
} }
] ]
} }

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, 1. LICENSE: Subject to the terms of this Agreement, SAP grants You a nonexclusive,
AND DISTRIBUTION 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 5. FREE AND OPEN SOURCE COMPONENTS: The Sample Code may include third party
as defined by Sections 1 through 9 of this document. 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 10. EXPORT: The Sample Code is subject to German, EU and US export control
owner that is granting the License. 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 13. LAW/VENUE:
that control, are controlled by, or are under common control with that entity. a) If You are located outside the US or Canada: This Agreement is governed by
For the purposes of this definition, "control" means (i) the power, direct and construed in accordance with the laws of Germany without reference to its
or indirect, to cause the direction or management of such entity, whether conflicts of law principles. You and SAP agree to submit to the exclusive
by contract or otherwise, or (ii) ownership of fifty percent (50%) or more jurisdiction of, and venue in, the courts located in Karlsruhe, Germany in any
of the outstanding shares, or (iii) beneficial ownership of such entity. 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.
v1.0-071618
"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.

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,42 @@
# Welcome to cap/samples # Welcome to SAP Cloud Application Programming model samples
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo). &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) ## Get Access to SAP Business Application Studio
[![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) The recommended environment for the course is SAP Business Application Studio. Watch [unit 2 of week 1](https://open.sap.com/courses/cp7/items/51pzQUzbXHr2kdbOmVs6jI) for how to get access.
## Setup
### Preliminaries In SAP Business Application Studio, open a terminal.
Then clone the repo with this specific branch:
1. Install [**@sap/cds-dk**](https://cap.cloud.sap/docs/get-started/) globally:
```sh
npm i -g @sap/cds-dk
```
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/tools#vscode)
### Download
If you have [Git](https://git-scm.com/downloads) installed, clone this repo as shown below, otherwise [download as ZIP file](archive/master.zip).
```sh ```sh
git clone https://github.com/sap-samples/cloud-cap-samples samples git clone https://github.com/sap-samples/cloud-cap-samples projects/cloud-cap-samples -b openSAP-week2-unit1
cd samples cd projects/cloud-cap-samples
``` ```
### Setup In the `cloud-cap-samples` folder run:
In the samples folder run:
```sh ```sh
npm install npm install
``` ```
### Run ## Run
With that you're ready to run the samples, for example:
Now you're ready to run the samples, for example:
```sh ```sh
cds watch bookshop cd packages/bookshop
cds watch
``` ```
After that open this link in your browser: [http://localhost:4004](http://localhost:4004) After that, watch out for the little popup in the lower right corner of SAP Business Application Studio that asks you to open the application in your browser.
When asked to log in, type `alice` as user and leave the password field blank, which is the [default user](https://cap.cloud.sap/docs/node.js/authentication#mocked).
### Testing
Run the provided tests with [_jest_](http://jestjs.io) or [_mocha_](http://mochajs.org), for example:
```sh
npx jest
```
> While mocha is a bit smaller and faster, jest runs tests in parallel and isolation, which allows to run all tests.
### Serve `npm`
We've included a simple npm registry mock, which allows you to do an `npm install @capire/<package>` locally. Use it as follows:
1. Start the @capire registry:
```sh
npm run registry
```
> While running this will have `@capire:registry=http://localhost:4444` set with npmrc.
2. Install one of the @capire packages wherever you like, for example:
```sh
npm add @capire/common @capire/bookshop
```
## Get Support ## Get Support
Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br> Check out the cap docs at https://cap.cloud.sap. <br>
In case you have a question, find a bug, or otherwise need support, please use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce). In case you find a bug or need support, please [open an issue in here](https://github.com/SAP-samples/cloud-cap-samples/issues/new).
## License ## License
Copyright (c) 2021 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](LICENSES/Apache-2.0.txt) file. Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under SAP Sample Code License Agreement, except as noted otherwise in the [LICENSE](/LICENSE) file.

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.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,5 +0,0 @@
ID;name;dateOfBirth;placeOfBirth;dateOfDeath;placeOfDeath
101;Emily Brontë;1818-07-30;Thornton, Yorkshire;1848-12-19;Haworth, Yorkshire
107;Charlotte Brontë;1818-04-21;Thornton, Yorkshire;1855-03-31;Haworth, Yorkshire
150;Edgar Allen Poe;1809-01-19;Boston, Massachusetts;1849-10-07;Baltimore, Maryland
170;Richard Carpenter;1929-08-14;Kings Lynn, Norfolk;2012-02-26;Hertfordshire, England
1 ID name dateOfBirth placeOfBirth dateOfDeath placeOfDeath
2 101 Emily Brontë 1818-07-30 Thornton, Yorkshire 1848-12-19 Haworth, Yorkshire
3 107 Charlotte Brontë 1818-04-21 Thornton, Yorkshire 1855-03-31 Haworth, Yorkshire
4 150 Edgar Allen Poe 1809-01-19 Boston, Massachusetts 1849-10-07 Baltimore, Maryland
5 170 Richard Carpenter 1929-08-14 King’s Lynn, Norfolk 2012-02-26 Hertfordshire, England

View File

@@ -1,6 +0,0 @@
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
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 +0,0 @@
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.
3 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.
4 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
5 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

@@ -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,31 +0,0 @@
using { Currency, managed, sap } from '@sap/cds/common';
namespace sap.capire.bookshop;
entity Books : managed {
key ID : Integer;
title : localized String(111);
descr : localized String(1111);
author : Association to Authors;
genre : Association to Genres;
stock : Integer;
price : Decimal;
currency : Currency;
image : LargeBinary @Core.MediaType : 'image/png';
}
entity Authors : managed {
key ID : Integer;
name : String(111);
dateOfBirth : Date;
dateOfDeath : Date;
placeOfBirth : String;
placeOfDeath : String;
books : Association to many Books on books.author = $self;
}
/** Hierarchically organized Code List for Genres */
entity Genres : sap.common.CodeList {
key ID : Integer;
parent : Association to Genres;
children : Composition of many Genres on children.parent = $self;
}

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,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,13 +0,0 @@
code;locale;name;descr
EUR;de;Euro;European Euro
USD;de;US-Dollar;United States Dollar
CAD;de;Kanadischer Dollar;Kanadischer Dollar
AUD;de;Australischer Dollar;Australischer Dollar
GBP;de;Pfund;Britische Pfund
ILS;de;Schekel;Israelische Schekel
EUR;fr;euro;de la Zone euro
USD;fr;dollar;dollar des États-Unis
CAD;fr;dollar canadien;dollar canadien
AUD;fr;dollar australien;dollar australien
GBP;fr;livre sterling;pound sterling
ILS;fr;Shekel;shekel israelien
1 code locale name descr
2 EUR de Euro European Euro
3 USD de US-Dollar United States Dollar
4 CAD de Kanadischer Dollar Kanadischer Dollar
5 AUD de Australischer Dollar Australischer Dollar
6 GBP de Pfund Britische Pfund
7 ILS de Schekel Israelische Schekel
8 EUR fr euro de la Zone euro
9 USD fr dollar dollar des États-Unis
10 CAD fr dollar canadien dollar canadien
11 AUD fr dollar australien dollar australien
12 GBP fr livre sterling pound sterling
13 ILS fr Shekel shekel israelien

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": "latest"
}
}

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,13 +0,0 @@
Books = Bücher
Book = Buch
ID = ID
Title = Titel
Authors = Autoren
Author = Autor
AuthorID = ID des Autors
AuthorName = Name des Autors
Name = Name
Stock = Bestand
Order = Bestellung
Orders = Bestellungen
Price = Preis

View File

@@ -1,55 +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: {
"browse-books": {
title: "Browse Books",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=bookshop",
applicationType : "URL",
url: "/browse/webapp",
navigationMode: "embedded"
},
"manage-books": {
title: "Manage Books",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=admin",
applicationType : "URL",
url: "/admin/webapp",
navigationMode: "embedded"
},
"manage-orders": {
title: "Manage Orders",
description: "w/ SAP Fiori Elements",
additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL",
url: "/orders/webapp",
navigationMode: "embedded"
}
}
};
</script>
<script 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,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,11 +0,0 @@
# This is the resource bundle of itelo
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
# JCI app descriptor contains lower case TITLE
appTitle=Bookshop Sample
# JCI app descriptor contains lower case DESCRIPTION
appSubTitle=CAP Sample Application
# JCI app descriptor contains lower case DESCRIPTION
appDescription=CDS Sample Service

View File

@@ -1,128 +0,0 @@
{
"_version": "1.8.0",
"sap.app": {
"id": "admin",
"type": "application",
"title": "Manage Books",
"description": "Sample Application",
"i18n": "i18n/i18n.properties",
"dataSources": {
"AdminService": {
"uri": "/admin/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
},
"-sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"-id": "ui5template.smartTemplate",
"-version": "1.40.12"
}
},
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
}
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"uri": "i18n/i18n.properties"
},
"": {
"dataSource": "AdminService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect" : true,
"earlyRequests": true,
"groupProperties": {
"default": {
"submit": "Auto"
}
}
}
}
},
"routing": {
"routes": [
{
"pattern": ":?query:",
"name": "BooksList",
"target": "BooksList"
},
{
"pattern": "Books({key}):?query:",
"name": "BooksDetails",
"target": "BooksDetails"
},
{
"pattern": "Books({key}/author({key2}):?query:",
"name": "AuthorsDetails",
"target": "AuthorsDetails"
}
],
"targets": {
"BooksList": {
"type": "Component",
"id": "BooksList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings" : {
"entitySet" : "Books",
"navigation" : {
"Books" : {
"detail" : {
"route" : "BooksDetails"
}
}
}
}
}
},
"BooksDetails": {
"type": "Component",
"id": "BooksDetailsList",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet" : "Books",
"navigation" : {
"Authors" : {
"detail" : {
"route" : "AuthorsDetails"
}
}
}
}
}
},
"AuthorsDetails": {
"type": "Component",
"id": "AuthorsDetailsList",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet" : "Authors"
}
}
}
}
},
"contentDensities": {
"compact": true,
"cozy": true
}
},
"sap.ui": {
"technology": "UI5",
"fullWidth": false
},
"sap.fiori": {
"registrationIds": [],
"archeType": "transactional"
}
}

View File

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

View File

@@ -1,50 +0,0 @@
using CatalogService from '@capire/bookshop';
////////////////////////////////////////////////////////////////////////////
//
// Books Object Page
//
annotate CatalogService.Books with @(
UI: {
HeaderInfo: {
TypeName: 'Book',
TypeNamePlural: 'Books',
Description: {Value: author}
},
HeaderFacets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Description}', Target: '@UI.FieldGroup#Descr'},
],
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Price'},
],
FieldGroup#Descr: {
Data: [
{Value: descr},
]
},
FieldGroup#Price: {
Data: [
{Value: price},
{Value: currency.symbol, Label: '{i18n>Currency}'},
]
},
}
);
////////////////////////////////////////////////////////////////////////////
//
// Books Object Page
//
annotate CatalogService.Books with @(
UI: {
SelectionFields: [ ID, price, currency_code ],
LineItem: [
{Value: title},
{Value: author, Label:'{i18n>Author}'},
{Value: genre.name},
{Value: price},
{Value: currency.symbol, Label:' '},
]
},
);

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,11 +0,0 @@
# This is the resource bundle of itelo
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
# JCI app descriptor contains lower case TITLE
appTitle=Bookshop Sample
# JCI app descriptor contains lower case DESCRIPTION
appSubTitle=CAP Sample Application
# JCI app descriptor contains lower case DESCRIPTION
appDescription=CDS Sample Service

View File

@@ -1,106 +0,0 @@
{
"_version": "1.8.0",
"sap.app": {
"id": "bookshop",
"type": "application",
"title": "Browse Books",
"description": "Sample Application",
"i18n": "i18n/i18n.properties",
"dataSources": {
"CatalogService": {
"uri": "/browse/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
},
"-sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"-id": "ui5template.smartTemplate",
"-version": "1.40.12"
}
},
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
}
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"uri": "i18n/i18n.properties"
},
"": {
"dataSource": "CatalogService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect": true,
"earlyRequests": true,
"groupProperties": {
"default": {
"submit": "Auto"
}
}
}
}
},
"routing": {
"routes": [
{
"pattern": ":?query:",
"name": "BooksList",
"target": "BooksList"
},
{
"pattern": "Books({key}):?query:",
"name": "BooksDetails",
"target": "BooksDetails"
}
],
"targets": {
"BooksList": {
"type": "Component",
"id": "BooksList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings": {
"entitySet": "Books",
"navigation": {
"Books": {
"detail": {
"route": "BooksDetails"
}
}
}
}
}
},
"BooksDetails": {
"type": "Component",
"id": "BooksDetailsList",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings": {
"entitySet": "Books"
}
}
}
}
},
"contentDensities": {
"compact": true,
"cozy": true
}
},
"sap.ui": {
"technology": "UI5",
"fullWidth": false
},
"sap.fiori": {
"registrationIds": [],
"archeType": "transactional"
}
}

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,12 +0,0 @@
/*
This model controls what gets served to Fiori frontends...
*/
using from './admin/fiori-service';
using from './browse/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,3 +0,0 @@
<head>
<meta http-equiv="refresh" content="0;url=vue/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,19 +0,0 @@
const express = require ('express')
const cds = require ('@sap/cds')
cds.once('bootstrap',(app)=>{
const {dirname} = require ('path')
// serving the orders app imported from @capire/orders
const orders_app = dirname (require.resolve('@capire/orders/app/orders/webapp/manifest.json'))
app.use ('/orders/webapp', express.static(orders_app))
// serving the vue.js app imported from @capire/bookshop
const bookshop_app = dirname (require.resolve('@capire/bookshop/app/vue/index.html'))
app.use ('/vue/bookshop', express.static(bookshop_app))
// serving the vue.js app imported from @capire/reviews
const reviews_app = dirname (require.resolve('@capire/reviews/app/vue/index.html'))
app.use ('/vue/reviews', express.static(reviews_app))
})
cds.once('served', require('./srv/mashup'))
module.exports = cds.server

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,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}!` }
}

1
lerna.json Normal file
View File

@@ -0,0 +1 @@
{"packages":["packages/*"],"version":"1.0.0"}

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:

View File

@@ -1 +0,0 @@
@sap:registry=http://nexus.wdf.sap.corp:8081/nexus/repository/build.milestones.npm/

View File

@@ -1,15 +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": [
"SirTobi.pegjs-language",
"tamuratak.vscode-pegjs",
"joeandaverde.vscode-pegjs-live"
],
// List of extensions recommended by VS Code that should not be recommended for users of this workspace.
"unwantedRecommendations": [
]
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -1,26 +0,0 @@
const fs = require("fs");
const path = require("path");
const peg = require("pegjs");
const pegGrammarPath = path.join(__dirname, "/odata2cqn.pegjs");
const odataPegGrammar = fs.readFileSync(pegGrammarPath, {
encoding: "utf8",
flag: "r",
});
const parser = peg.generate(odataPegGrammar);
module.exports = {
parse: {
url: parser.parse,
},
to: {
cqn: parser.parse,
url: (cqn) => pending(cqn)
},
serialize: (data) => pending(data),
deserialize: (body) => pending(body),
}
const pending = ()=>{
throw new Error ('Not yet implemented')
}

View File

@@ -1,200 +0,0 @@
/** ------------------------------------------
* This is a peg.js adaptation of the https://github.com/oasis-tcs/odata-abnf/blob/master/abnf/odata-abnf-construction-rules.txt
* which directly constructs CQN out of parsed sources.
*
* NOTE:
* In contrast to the OData ABNF source, which uses very detailedsemantic rules,
* this adaptation uses rather generic syntactic rules only, e.g. not distinguishing
* betwenn Collection Navigation or not knowing individual function names.
* This is to be open to future enhancements of the OData standard, as well as
* to improve error messages. For example a typo in a function name could be
* reported specifically instead of throwing a generic parser error.
*
* See also: https://docs.microsoft.com/en-us/odata/concepts/queryoptions-overview
* Future test cases http://docs.oasis-open.org/odata/odata/v4.0/errata03/os/complete/abnf/odata-abnf-testcases.xml
*
* Limitations: Type, Geo functions are not supported,
* maxdatetime, mindatetime, fractionalseconds,
* totaloffsetminutes, date, totalseconds,
* floor, ceiling also are not supported by CAP
*
* Examples:
* Books
* Books/201
* Books?$select=ID,title&$expand=author($select=name)&$filter=stock gt 1&$orderby=title
*/
// ---------- JavaScript Helpers -------------
{
const stack=[]
let SELECT
}
// ---------- Entity Paths ---------------
ODataRelativeURI // Note: case-sensitive!
= (p:path { SELECT = { from:p } })
( o"?"o QueryOption ( o'&'o QueryOption )* )? {
if (SELECT.expand) {
SELECT.columns = SELECT.expand
delete SELECT.expand
}
return { SELECT }
}
path
= crv:$("$count"/"$ref"/"$value") {return {ref:[crv]}}
/ head:identifier filter:(OPEN args CLOSE)? tail:( '/' p:path {return p} )? {
const ref = [ filter ? { id:head, where:filter[1] } : head ]
if (tail) ref.push (...tail.ref)
return {ref}
}
args
= val:( number / integer / string ) {return [{val}]}
/ ref:identifier o"="o val:( number / integer / string ) more:( COMMA args )? {
const args = [ {ref}, '=', {val} ]
if (more) args.push ('and', ...more[1])
return args
}
ref "a reference"
= head:identifier tail:( '/' identifier )* {
return { ref:[ head, ...tail ] }
}
//
// ---------- Query Options ------------
QueryOption = ExpandOption
ExpandOption =
"$select=" o select ( COMMA select )* /
"$expand=" o expand ( COMMA expand )* /
"$filter=" o filter /
"$orderby=" o orderby /
"$top=" o top /
"$skip=" o skip /
"$search=" o search /
"$count=" o count
select
= col:ref {
(SELECT.expand || (SELECT.expand = [])).push(col)
return col
}
expand =
( c:select {c.expand='*'} )
( // --- nested query options, if any
(OPEN {
stack.push (SELECT)
SELECT = SELECT.expand[SELECT.expand.length-1]
SELECT.expand = []
})
ExpandOption ( o";"o ExpandOption )*
(CLOSE {
SELECT = stack.pop()
})
)? // --- end of nested query options
( COMMA expand )?
top
= val:integer {
(SELECT.limit || (SELECT.limit={})).rows = {val}
}
skip
= val:integer {
(SELECT.limit || (SELECT.limit={})).offset = {val}
}
search
= p:search_clause {SELECT.search = p}
search_clause = p:( n:NOT? {return n?[n]:[]} )(
OPEN xpr:search_clause CLOSE {p.push({xpr})}
/ val:(identifier/string) {p.push({val})}
)( ao:(AND/OR) more:search_clause {p.push(ao,...more)} )*
{return p}
filter
= p:where_clause {SELECT.where = p}
where_clause = p:( n:NOT? {return n?[n]:[]} )(
OPEN xpr:where_clause CLOSE {p.push({xpr})}
/ comp:comparison {p.push(...comp)}
/ func:boolish {p.push(func)}
)( ao:(AND/OR) more:where_clause {p.push(ao,...more)} )*
{return p}
orderby
= ref:ref sort:( _ s:$("asc"/"desc") {return s})? {
SELECT.orderby = $(ref, sort && {sort})
}
count
= c:$[^,?&()]+ { SELECT.count = true }
//
// ---------- Expressions ------------
comparison "a comparison"
= a:operand _ o:$("eq"/"ne"/"lt"/"gt"/"le"/"ge") _ b:operand {
const op = { eq:'=', ne:'!=', lt:'<', gt:'>', le:'<=', ge:'>=' }[o]||o
return [ a, op, b ]
}
operand "an operand"
= val:number {return Number.isSafeInteger(val) ? {val} : { val:String(val), literal:'number' }}
/ val:string {return {val}}
/ function
/ ref
function "a function call"
= func:$[a-z]+ OPEN a:operand more:( COMMA o:operand {return o} )* CLOSE
{ return { func, args:[a,...more] }}
boolish "a boolean function"
= func:("contains"/"endswith"/"startswith") OPEN a:operand COMMA b:operand CLOSE
{ return { func, args:[a,b] }}
NOT = o "not"i _ {return 'not'}
AND = _ "and"i _ {return 'and'}
OR = _ "or"i _ {return 'or'}
//
// ---------- Literals -----------
string "Edm.String"
= "'" s:$("''"/[^'])* "'"
{return s.replace(/''/g,"'")}
number
= x:$( [+-]? [0-9]+ ("."[0-9]+)? ("e"[0-9]+)? )
{return Number(x)}
integer
= x:$( [+-]? [0-9]+ )
{return parseInt(x)}
identifier
= $([a-zA-Z][_a-zA-Z0-9]*)
//
// ---------- Punctuation ----------
COLON = o":"o
COMMA = o","o
SEMI = o";"o
OPEN = o"("o
CLOSE = o")"
//
// ---------- Whitespaces -----------
o "optional whitespaces" = $[ \t\n]*
_ "mandatory whitespaces" = $[ \t\n]+
//
// ------------------------------------

View File

@@ -1,15 +0,0 @@
{
"name": "@sap/cds-odata",
"version": "1.0.0",
"dependencies": {
"@sap/cds-compiler": "latest",
"@sap/cds": "^4.4.10"
},
"scripts": {
"postinstall": "rm -fr node_modules/@sap/cds/node_modules"
},
"devDependencies": {
"pegjs": "^0.10.0"
},
"private": true
}

View File

@@ -1,105 +0,0 @@
const cds = require("@sap/cds/lib"), {expect} = cds.test
cds.odata = require("../lib")
describe("$filter", () => {
describe("comparing expressions", () => {
const types = {
strings: "'some string'",
integers: 11,
decimals: 0.99,
// ...
}
it.each(Object.keys(types))("should support expressions with %s", (t) => {
expect (cds.odata.parse.url(`Foo?$filter=bar eq ${types[t]}`))
.to.eql (cds.parse.cql(`SELECT from Foo where bar = ${types[t]}`))
})
const operators = {
eq: '=',
lt: '<',
le: '<=',
gt: '>',
ge: '>=',
ne: '!=',
// ...
}
it.each(Object.keys(operators))("should support comparison operator '%s'", (op) => {
expect (cds.odata.parse.url(`Foo?$filter=bar ${op} 11`))
.to.eql (cds.parse.cql(`SELECT from Foo where bar ${operators[op]} 11`))
})
});
describe("logical expressions", () => {
it.each(['and','or'])("should support '%s'", (t) => {
expect (cds.odata.parse.url(`Foo?$filter=bar lt 11 ${t} name eq 'some name'`))
.to.eql (cds.parse.cql(`SELECT from Foo where bar < 11 ${t} name = 'some name'`))
})
it("should support 'not'", () => {
// REVISIT: We need to check with the Node.js team why they translated that to the equivalent of:
// not name like concat('%','sunny','%') escape '^'
expect (cds.odata.parse.url(`Foo?$filter= not contains(name,'sunny')`))
.to.eql (cds.parse.cql(`SELECT from Foo where not contains(name,'sunny')`))
});
// REVISIT: wait for compiler v2
it("should support group expr", () => {
expect (cds.odata.parse.url(`Foo?$filter= (unitPrice gt 11 and length(name) eq 12) or name eq 'Restless and Wild'`))
.to.eql (cds.parse.cql(`SELECT from Foo where (unitPrice > 11 and length(name) = 12) or name = 'Restless and Wild'`))
});
});
describe("function expressions", () => {
it("should support contains", () => {
expect (cds.odata.parse.url(`Foo?$filter= contains(name,'sunny')`))
.to.eql (cds.parse.cql(`SELECT from Foo where contains(name,'sunny')`))
});
it("should support startswith", () => {
expect (cds.odata.parse.url(`Foo?$filter= startswith(name,'sunny')`))
.to.eql (cds.parse.cql(`SELECT from Foo where startswith(name,'sunny')`))
});
it("should support endswith", () => {
expect (cds.odata.parse.url(`Foo?$filter= endswith(name,'sunny')`))
.to.eql (cds.parse.cql(`SELECT from Foo where endswith(name,'sunny')`))
});
it("should support length", () => {
expect (cds.odata.parse.url(`Foo?$filter= length(name) lt 11`))
.to.eql (cds.parse.cql(`SELECT from Foo where length(name) < 11`))
});
it("should support indexof", () => {
expect (cds.odata.parse.url(`Foo?$filter= indexof(name,'x') eq 11`))
.to.eql (cds.parse.cql(`SELECT from Foo where indexof(name,'x') = 11`))
});
it("should support substring", () => {
expect (cds.odata.parse.url(`Foo?$filter= substring(name,1) eq 'foo'`))
.to.eql (cds.parse.cql(`SELECT from Foo where substring(name,1) = 'foo'`))
});
it.each(['tolower','toupper','trim'])("should support '%s'", (fn) => {
expect (cds.odata.parse.url(`Foo?$filter= ${fn}(name) eq 'foo'`))
.to.eql (cds.parse.cql(`SELECT from Foo where ${fn}(name) = 'foo'`))
});
it("should support 'day'", () => {
expect (cds.odata.parse.url(`Foo?$filter= day(name) eq 11`))
.to.eql (cds.parse.cql(`SELECT from Foo where day(name) = 11`))
});
it("should support concat", () => {
expect (cds.odata.parse.url(`Foo?$filter= concat(name,'o') eq 'foo'`))
.to.eql (cds.parse.cql(`SELECT from Foo where concat(name,'o') = 'foo'`))
});
});
});

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,92 +0,0 @@
////////////////////////////////////////////////////////////////////////////
//
// 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.
//
////////////////////////////////////////////////////////////////////////////
using { OrdersService } from '../../srv/orders-service';
@odata.draft.enabled
annotate OrdersService.Orders with @(
UI: {
SelectionFields: [ createdAt, createdBy ],
LineItem: [
{Value: OrderNo, Label:'OrderNo'},
{Value: buyer, Label:'Customer'},
{Value: createdAt, Label:'Date'}
],
HeaderInfo: {
TypeName: 'Order', TypeNamePlural: 'Orders',
Title: {
Label: 'Order number ', //A label is possible but it is not considered on the ObjectPage yet
Value: OrderNo
},
Description: {Value: createdBy}
},
Identification: [ //Is the main field group
{Value: createdBy, Label:'Customer'},
{Value: createdAt, Label:'Date'},
{Value: OrderNo },
],
HeaderFacets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Created}', Target: '@UI.FieldGroup#Created'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Modified}', Target: '@UI.FieldGroup#Modified'},
],
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>Details}', Target: '@UI.FieldGroup#Details'},
{$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: 'Items/@UI.LineItem'},
],
FieldGroup#Details: {
Data: [
{Value: currency.code, Label:'Currency'}
]
},
FieldGroup#Created: {
Data: [
{Value: createdBy},
{Value: createdAt},
]
},
FieldGroup#Modified: {
Data: [
{Value: modifiedBy},
{Value: modifiedAt},
]
},
},
) {
createdAt @UI.HiddenFilter:false;
createdBy @UI.HiddenFilter:false;
};
annotate OrdersService.Orders_Items with @(
UI: {
LineItem: [
{Value: product_ID, Label:'Product ID'},
{Value: title, Label:'Product Title'},
{Value: price, Label:'Unit Price'},
{Value: amount, Label:'Quantity'},
],
Identification: [ //Is the main field group
{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

@@ -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,11 +0,0 @@
# This is the resource bundle of itelo
# __ldi.translation.uuid=c3431418-9caf-11e8-98d0-529269fb1459
# JCI app descriptor contains lower case TITLE
appTitle=Bookshop Sample
# JCI app descriptor contains lower case DESCRIPTION
appSubTitle=CAP Sample Application
# JCI app descriptor contains lower case DESCRIPTION
appDescription=CDS Sample Service

View File

@@ -1,170 +0,0 @@
{
"_version": "1.8.0",
"sap.app": {
"id": "orders",
"type": "application",
"title": "Order Books",
"description": "Sample Application",
"i18n": "i18n/i18n.properties",
"dataSources": {
"OrdersService": {
"uri": "/orders/",
"type": "OData",
"settings": {
"odataVersion": "4.0"
}
}
},
"-sourceTemplate": {
"id": "ui5template.basicSAPUI5ApplicationProject",
"-id": "ui5template.smartTemplate",
"-version": "1.40.12"
}
},
"sap.ui5": {
"dependencies": {
"libs": {
"sap.fe.templates": {}
}
},
"models": {
"i18n": {
"type": "sap.ui.model.resource.ResourceModel",
"uri": "i18n/i18n.properties"
},
"": {
"dataSource": "OrdersService",
"settings": {
"synchronizationMode": "None",
"operationMode": "Server",
"autoExpandSelect" : true,
"earlyRequests": true,
"groupProperties": {
"default": {
"submit": "Auto"
}
}
}
}
},
"routing": {
"routes": [
{
"pattern": ":?query:",
"name": "OrdersList",
"target": "OrdersList"
},
{
"pattern": "Orders({key}):?query:",
"name": "OrdersDetails",
"target": "OrdersDetails"
},
{
"pattern": "Orders({boo})/Items({boo2}):?query:",
"name": "OrderItemsDetails",
"target": "OrderItemsDetails"
},
{
"pattern": "Books({key}):?query:",
"name": "BooksDetails",
"target": "BooksDetails"
}
],
"targets": {
"OrdersList": {
"type": "Component",
"id": "OrdersList",
"name": "sap.fe.templates.ListReport",
"options": {
"settings" : {
"entitySet" : "Orders",
"navigation" : {
"Orders" : {
"detail" : {
"route" : "OrdersDetails"
}
}
}
}
}
},
"OrdersDetails": {
"type": "Component",
"id": "OrdersDetails",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet": "Orders",
"navigation" : {
"Items": {
"detail": {
"route": "OrderItemsDetails"
}
},
"book": {
"detail": {
"route": "BooksDetails"
}
},
"dummy": {
"detail": {
"route": "BooksDetails"
}
}
}
}
}
},
"OrderItemsDetails": {
"type": "Component",
"id": "OrderItemsDetails",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet": "Orders_Items"
}
}
},
"BooksDetails": {
"type": "Component",
"id": "BooksDetails",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet": "Books",
"navigation": {
"author": {
"detail": {
"route": "AuthorsDetails"
}
}
}
}
}
},
"AuthorsDetails": {
"type": "Component",
"id": "AuthorsDetails",
"name": "sap.fe.templates.ObjectPage",
"options": {
"settings" : {
"entitySet": "Authors"
}
}
}
}
},
"contentDensities": {
"compact": true,
"cozy": true
}
},
"sap.ui": {
"technology": "UI5",
"fullWidth": false
},
"sap.fiori": {
"registrationIds": [],
"archeType": "transactional"
}
}

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,26 +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;
}
// Activate extension package
using from '@capire/common';

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';

View File

@@ -1,8 +0,0 @@
{
"name": "@capire/orders",
"version": "1.0.0",
"dependencies": {
"@capire/common": "*",
"@sap/cds": "^4.3.0"
}
}

View File

@@ -1,5 +0,0 @@
using { sap.capire.orders as my } from '../db/schema';
service OrdersService {
entity Orders as projection on my.Orders;
}

View File

@@ -1,37 +0,0 @@
const cds = require ('@sap/cds')
class OrdersService extends cds.ApplicationService {
/** register custom handlers */
init(){
const { Orders_Items:OrderItems } = this.entities
this.before ('UPDATE', 'Orders', async function(req) {
const { ID, Items } = req.data
if (Items) for (let { product_ID, amount } of Items) {
const { amount:before } = await cds.tx(req).run (
SELECT.one.from (OrderItems, oi => oi.amount) .where ({up__ID:ID, product_ID})
)
if (amount != before) await this.orderChanged (product_ID, amount-before)
}
})
this.before ('DELETE', 'Orders', async function(req) {
const { ID } = req.data
const Items = await cds.tx(req).run (
SELECT.from (OrderItems, oi => { oi.product_ID, oi.amount }) .where ({up__ID:ID})
)
if (Items) await Promise.all (Items.map(it => this.orderChanged (it.product_ID, -it.amount)))
})
return super.init()
}
/** order changed -> broadcast event */
orderChanged (product, deltaAmount) {
// Emit events to inform subscribers about changes in orders
console.log ('> emitting:', 'OrderChanged', { product, deltaAmount })
return this.emit ('OrderChanged', { product, deltaAmount })
}
}
module.exports = OrdersService

2064
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -1,40 +1,29 @@
{ {
"name": "@capire/samples", "name": "@sap/capire-samples",
"version": "2.0.0", "description": "The umbrella project for all samples to easily setup for local development and tests.",
"description": "A monorepo with several samples for CAP.", "repository": "https://github.com/SAP-samples/cloud-cap-samples.git",
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
"author": "daniel.hutzel@sap.com", "author": "daniel.hutzel@sap.com",
"private": true,
"scripts": {
"lerna": "npx --no-install lerna -v > /dev/null || npm i lerna --no-save",
"install": "(npm -s run lerna) && lerna bootstrap --hoist",
"cleanup": "lerna clean -y && rm -fr node_modules",
"bookshop": "cds watch packages/bookshop",
"bookshop-enhanced": "cds watch packages/bookshop-enhanced",
"reviews-service": "cds watch packages/reviews-service",
"bookstore": "cds watch packages/bookstore",
"media-server": "cds watch packages/media-server"
},
"dependencies": { "dependencies": {
"@capire/bookshop": "./bookshop", "@sap/cds": "^3",
"@capire/common": "./common", "express": "^4"
"@capire/fiori": "./fiori",
"@capire/hello": "./hello",
"@capire/media": "./media",
"@capire/orders": "./orders",
"@capire/reviews": "./reviews"
}, },
"devDependencies": { "devDependencies": {
"pegjs": "^0.10.0", "sqlite3": "*"
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"sqlite3": "^5"
}, },
"scripts": { "--add-these-to-devDependencies-for-tests": {
"registry": "cd .registry && node server.js", "@types/jest": "*",
"bookshop": "cds watch bookshop", "jest": "*"
"fiori": "cds watch fiori",
"media": "cds watch media",
"mocha": "npx mocha || echo",
"jest": "npx jest",
"test": "npm run jest --silent"
}, },
"mocha": { "license": "SAP SAMPLE CODE LICENSE"
"parallel": true
},
"jest": {
"testEnvironment": "node"
},
"license": "SAP SAMPLE CODE LICENSE",
"private": true
} }

View File

@@ -0,0 +1,5 @@
ID;name
101;Emily Brontë
107;Charlotte Brontë
150;Edgar Allen Poe
170;Richard Carpenter
1 ID name
2 101 Emily Brontë
3 107 Charlotte Brontë
4 150 Edgar Allen Poe
5 170 Richard Carpenter

View File

@@ -0,0 +1,6 @@
ID;title;author_ID;stock
201;Wuthering Heights;101;12
207;Jane Eyre;107;11
251;The Raven;150;333
252;Eleonora;150;555
271;Catweazle;170;22
1 ID title author_ID stock
2 201 Wuthering Heights 101 12
3 207 Jane Eyre 107 11
4 251 The Raven 150 333
5 252 Eleonora 150 555
6 271 Catweazle 170 22

View File

@@ -0,0 +1,40 @@
namespace sap.capire.bookshop;
using { Currency, managed } from '@sap/cds/common';
entity Books : managed, additionalInfo {
key ID : Integer;
title : localized String(111);
descr : localized String(1111);
author : Association to Authors;
stock : Integer;
price : Decimal(9,2);
currency : Currency;
}
entity Authors : managed {
key ID : Integer;
name : String(111);
books : Association to many Books on books.author = $self;
}
entity Orders : managed {
key ID : UUID;
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many OrderItems on Items.parent = $self;
}
entity OrderItems {
key ID : UUID;
parent : Association to Orders;
book : Association to Books;
amount : Integer;
}
entity Movies: additionalInfo {
key ID : Integer;
name : String(111);
}
aspect additionalInfo{
genre: String(100);
language: String(200);
}

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