Compare commits

..

147 Commits

Author SHA1 Message Date
D065023
a2febd57db Merge branch 'dkom' of https://github.com/SAP-samples/cloud-cap-samples into dkom 2020-02-10 18:05:44 +01:00
D065023
5073af37d3 change folder 2020-02-10 18:05:34 +01:00
Dr. David Kunz
e8cda443ba Update README.md 2020-02-10 13:43:01 +01:00
D065023
da63d6c287 Merge branch 'dkom' of https://github.com/SAP-samples/cloud-cap-samples into dkom 2020-02-07 18:41:56 +01:00
Dr. David Kunz
009dfc53c5 Update README.md 2020-02-05 16:09:49 +01:00
Dr. David Kunz
d760516e3e Update README.md 2020-02-05 16:09:29 +01:00
Dr. David Kunz
261d238738 Update README.md 2020-02-04 09:40:53 +01:00
D065023
136eae69eb barebone 2020-01-08 18:29:28 +01:00
D065023
e0306850b4 prettier 2020-01-08 16:04:45 +01:00
D065023
b7f9b78988 prettier 2020-01-08 16:00:50 +01:00
D065023
4e5f31a29d where -> and 2020-01-08 16:00:24 +01:00
D065023
2d7efafd30 prettier 2020-01-08 16:00:07 +01:00
D065023
cf0b9c2a52 csv -> data 2020-01-08 15:28:01 +01:00
D065023
2053450404 rm tombstone 2020-01-08 13:52:23 +01:00
D065023
920600a5ff prettier 2020-01-08 13:49:59 +01:00
D065023
dc1ea91d9e prettier 2020-01-08 13:45:53 +01:00
D065023
9ba5aae999 fix bug 2020-01-08 13:43:38 +01:00
D065023
29aee15cf3 Alice doesnt need a password anymore 2020-01-07 14:47:05 +01:00
D065023
2ca5a79c7e renaming ext service 2020-01-07 12:47:30 +01:00
Dr. David Kunz
857b28aad0 Update README.md 2020-01-07 12:37:56 +01:00
Dr. David Kunz
91d1a9bcb2 Update README.md 2020-01-07 12:36:26 +01:00
Daniel
a33dbff74e restored master .cds for db/schema and cat-service 2020-01-04 07:33:35 +01:00
Daniel
7f65729dc9 Merge branch 'master' into dkom 2020-01-04 07:28:09 +01:00
Daniel
3c4aa7427e Merge branch 'master' into dkom 2020-01-03 03:10:26 +01:00
D065023
28859993ba rm file 2020-01-02 09:20:50 +01:00
D065023
e503f157f6 cosmetics 2019-12-30 14:20:11 +01:00
D065023
4eebfb5df4 cosmetics 2019-12-30 14:12:51 +01:00
D065023
28362cc835 cosmetics 2019-12-30 14:12:14 +01:00
D065023
78e6138718 minor 2019-12-30 14:09:57 +01:00
D065023
7db2c6e781 tombstone logic 2019-12-30 14:06:41 +01:00
D065023
afc5be2610 comment 2019-12-30 13:55:06 +01:00
D065023
5bc8d4dde0 cosmetics 2019-12-30 13:54:20 +01:00
D065023
60563dc816 cosmetics 2019-12-30 13:51:02 +01:00
D065023
4a1887f424 translations 2019-12-27 12:01:20 +01:00
D065023
c28287f9e4 typo 2019-12-27 11:01:58 +01:00
D065023
8cadac0051 cleanup readme 2019-12-27 10:25:35 +01:00
D065023
034c3b84f7 adjusted 2019-12-26 14:18:35 +01:00
D065023
b155754a72 adjusted 2019-12-26 14:16:59 +01:00
D065023
782e8a6696 adjusted README 2019-12-26 14:16:18 +01:00
D065023
f3c14a0625 cds -> latest 2019-12-23 18:06:04 +01:00
D065023
4cffa85079 rm todo 2019-12-23 17:26:40 +01:00
D065023
837e6bf1c8 minor 2019-12-23 17:23:00 +01:00
D065023
b394dbd234 rm event modelling 2019-12-23 17:19:53 +01:00
D065023
fdd0a256c4 made async api work 2019-12-23 15:54:52 +01:00
D065023
5b5d9da82e added eventing 2019-12-23 11:53:24 +01:00
D065023
2af54a520c Added README 2019-12-23 10:59:44 +01:00
D065023
128213aba7 rm comment 2019-12-23 10:14:14 +01:00
D065023
4bc2257cea cleanup 2019-12-23 10:12:00 +01:00
D065023
e16a343ce3 prettier 2019-12-23 10:10:03 +01:00
D065023
1f01bdf202 req.user.id === 'anonymous' in case of not logged in 2019-12-23 10:08:42 +01:00
Daniel
45843ab7bd cleaned up 2019-12-21 18:01:58 +01:00
D065023
421cea9f2b require utils 2019-12-20 16:12:29 +01:00
D065023
aa919f9d62 async conntext 2019-12-20 16:11:10 +01:00
D065023
ed814a1f75 hack for app studio 2019-12-20 16:09:11 +01:00
Daniel
c04c93cca6 c2 2019-12-20 13:37:50 +01:00
Daniel
2cde812edd cleaned up and working end to end now 2019-12-20 13:37:31 +01:00
Daniel
2f576dbb1b gaaaanz schmaler korridor 2019-12-19 18:23:38 +01:00
Daniel
7f7cd43bff adding test for GET Orders 2019-12-19 18:23:16 +01:00
Daniel
294f9feb36 added launch conf for bookshop 2019-12-19 18:22:54 +01:00
Daniel
2ebfcd8871 fixed: .contact 2019-12-19 18:22:31 +01:00
Daniel
963d0fbb6c constraint violation 2019-12-19 17:09:29 +01:00
Daniel
eebdd74bfe more work-arounds 2019-12-19 17:04:32 +01:00
Daniel
37810c9027 quick fix for empty value help 2019-12-19 16:55:02 +01:00
Daniel
48a086e9a1 quick fix for empty valuehelp 2019-12-19 16:54:21 +01:00
Daniel
659c347c71 .BusinessPartner -> .contact 2019-12-19 16:48:18 +01:00
Daniel
0bbb8e3d3b + auth conf --> we need to remove that later 2019-12-19 16:39:43 +01:00
Daniel
b3abcbcaae cleanup 2019-12-19 16:26:17 +01:00
Daniel
3716d4d5e3 fixed: auto-exposed addresses were missing 2019-12-19 16:22:42 +01:00
Daniel
226094e85c doesn' work with hana 2019-12-19 15:26:03 +01:00
Daniel
de149ea9b3 Merge branch 'D-kom-Demo' into dkom 2019-12-17 14:35:34 +01:00
Daniel
b6c1610817 updates parser version 2019-12-17 14:24:04 +01:00
Daniel
3df0981992 BUPA -> contact 2019-12-17 13:01:35 +01:00
Daniel
8c8c5f3f9d some more cosmetics tweaks 2019-12-17 12:55:09 +01:00
Daniel
2c0f69a161 some more cleanup 2019-12-17 12:46:10 +01:00
Daniel
54d0c8b35d simplified models and impl -> requires latest snapshots 2019-12-17 12:30:13 +01:00
D065023
3027a7a1e5 hack for app studio 2019-12-17 10:03:17 +01:00
David-Kunz
8eaf34f5d3 Update package.json 2019-12-17 09:59:11 +01:00
David-Kunz
0b0a22d126 Update schema.cds 2019-12-17 09:40:17 +01:00
Daniel
c0e1fb38ac cosmetics 2019-12-16 22:12:37 +01:00
D065023
74c155ca62 better 2019-12-16 21:56:32 +01:00
D065023
db16577235 no more bla 2019-12-16 21:55:33 +01:00
Daniel
53989cf609 Merge branch 'D-kom-Demo' into dkom 2019-12-16 21:54:49 +01:00
D065023
d678b51320 revert prefix 2019-12-16 21:52:43 +01:00
Daniel
5720d73b76 revised 2019-12-16 21:50:23 +01:00
D065023
06a6ac2201 fixed bug 2019-12-16 21:14:56 +01:00
D065023
125edc34e2 fixed 2019-12-16 21:12:03 +01:00
D065023
dc8e8c55df fix bug 2019-12-16 21:05:28 +01:00
D065023
f89acc00dd fix typo 2019-12-16 21:04:04 +01:00
D065023
3e725bcc26 refactor 2019-12-16 21:02:57 +01:00
D065023
dfea19334d use prefix 2019-12-16 20:22:12 +01:00
D065023
8f11de5430 cosmetics 2019-12-16 19:18:21 +01:00
D065023
38ce94d5cd syntax cleanup 2019-12-16 18:42:59 +01:00
D065023
ffe633a493 cleanup 2019-12-16 11:31:29 +01:00
D065023
e081182a7c cleanup 2019-12-16 11:30:42 +01:00
D065023
e4f8f13dbf typo 2019-12-16 11:28:06 +01:00
D065023
cc698ec23f rename ql -> query 2019-12-16 11:13:34 +01:00
D065023
382a4c562d prettier 2019-12-16 11:11:45 +01:00
D065023
811694cdf1 revert 2019-12-16 11:11:25 +01:00
D065023
83653bd095 Merge branch 'D-kom-Demo' of https://github.com/SAP-samples/cloud-cap-samples into D-kom-Demo 2019-12-16 10:29:44 +01:00
D065023
e27275d29a go to own database when remote system unavailable 2019-12-16 10:29:36 +01:00
Daniel
b9330d7f77 Merge branch 'master' into D-kom-Demo 2019-12-16 08:41:04 +01:00
D065023
f413b45e24 more checks 2019-12-13 14:26:22 +01:00
D065023
4a21b9edc3 cleaner 2019-12-13 14:20:04 +01:00
D065023
4bfd4430e1 rm comment 2019-12-13 10:42:18 +01:00
D065023
0bb3144aea rm console.logs 2019-12-13 10:41:11 +01:00
D065023
c1d2c4caef translations and cleanup 2019-12-13 09:00:56 +01:00
D065023
59f68c0f28 code reduction 2019-12-13 08:47:36 +01:00
D065023
5ba3458b27 added limit 2019-12-13 08:46:09 +01:00
D065023
199b2c8045 it works 2019-12-12 17:33:21 +01:00
Elena Oresharova
3b4abf5600 Fix side effects for address 2019-12-12 11:12:01 +01:00
D065023
f69c0ae190 fiori3 theme 2019-12-12 10:19:44 +01:00
D065023
f48cd1cc2f cosmetics 2019-12-12 10:14:23 +01:00
D065023
6bded9df98 more test data 2019-12-12 10:05:15 +01:00
D065023
5a659774b5 use msg as transaction box 2019-12-12 09:53:46 +01:00
D065023
17d6dc8cf8 rm delete 2019-12-12 08:47:50 +01:00
D065023
1a1686e340 cleanup 2019-12-11 16:38:38 +01:00
D065023
b298c9b708 fixed a bug 2019-12-11 16:37:51 +01:00
D065023
348a7b191e format 2019-12-11 13:26:22 +01:00
D065023
f56d4fe093 readonly 2019-12-11 13:22:00 +01:00
D065023
02942f5e1a nothing 2019-12-11 13:20:45 +01:00
D065023
1aa9237d20 needed to use several transactions 2019-12-11 13:19:50 +01:00
D065023
7a760cfaf8 cleanup 2019-12-11 12:52:06 +01:00
D065023
6c0d8fa444 bug fix 2019-12-11 12:49:26 +01:00
D065023
3b06003328 fixed bugs - do not change key names 2019-12-11 11:46:55 +01:00
D065023
e686b1819b fixed bugs 2019-12-11 11:46:44 +01:00
D065023
d36c2a97fa bug fix 2019-12-11 11:24:05 +01:00
D065023
f4e119342b bug fix 2019-12-11 11:19:21 +01:00
D065023
ddd02b52f2 eventing, renaming, subselecting 2019-12-11 11:12:57 +01:00
D065023
e6d5183cce impl msg mock 2019-12-06 14:35:02 +01:00
D065023
a191ecf88d rm toUpperCase for user id 2019-12-06 14:11:21 +01:00
D065023
140db39cd4 include authorization 2019-12-06 11:32:50 +01:00
D065023
68ee29598a translations 2019-12-06 11:03:59 +01:00
D065023
7deae997bb fixed minor issues 2019-12-06 10:59:50 +01:00
D065023
12aee3e38c fixed keys 2019-12-06 10:54:45 +01:00
Elena Oresharova
dfe876e2cf Fix 'City Name' label for the Address 2019-12-03 16:17:20 +01:00
Elena Oresharova
9e2c7a0974 Add side effect of filling the fields of an address once we have a selection 2019-12-03 13:54:02 +01:00
Elena Oresharova
30b2854fac Merge branch 'D-kom-Demo' of https://github.com/SAP-samples/cloud-cap-samples into D-kom-Demo 2019-12-03 13:37:07 +01:00
Elena Oresharova
4ca7e425ec Add additional columns to the value list for Addresses 2019-12-03 13:32:50 +01:00
D065023
28a51f4837 Added Country and Postal Code 2019-12-03 10:58:53 +01:00
D065023
692a360065 minor 2019-12-02 12:01:38 +01:00
D065023
b3d9fdb8b3 refactor 2019-12-02 10:57:52 +01:00
D065023
e9d10986ff rm console.log 2019-12-02 10:35:53 +01:00
D065023
7191f61806 User mapping and refactoring 2019-12-02 10:35:22 +01:00
D065023
e688e7ecee value help 2019-11-30 14:39:00 +01:00
D065023
4a2139a5f2 Address lookup to mocked S/4HANA system
What's working:
- Address information on OP
- Reading of S/4HANA Addresses
- Translations
2019-11-30 14:17:17 +01:00
D065023
ed3ecd502f async style 2019-11-29 12:24:22 +01:00
D065023
8f3d112558 prettier 2019-11-29 12:11:29 +01:00
174 changed files with 8129 additions and 2855 deletions

1
.env
View File

@@ -1 +0,0 @@
cds.features.snapi = y

View File

@@ -1,11 +1,9 @@
{
"extends": "eslint:recommended",
"env": {
"browser": true,
"node": true,
"es6": true,
"jest": true,
"mocha": true
"jest": true
},
"parserOptions": {
"ecmaVersion": 2018

View File

@@ -1,10 +0,0 @@
---
name: Question, feedback or bug?
about: Use our community!
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.16.2]
steps:
- uses: actions/checkout@v2
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v1
with:
node-version: ${{ matrix.node-version }}
- run: npm install
- run: npm test

2
.gitignore vendored
View File

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

View File

@@ -1 +0,0 @@
parallel: true

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": [
]
}

28
.vscode/launch.json vendored
View File

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

28
.vscode/tasks.json vendored
View File

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

379
LICENSE
View File

@@ -1,217 +1,190 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
SAP SAMPLE CODE LICENSE AGREEMENT
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
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.
1. Definitions.
1. LICENSE: Subject to the terms of this Agreement, SAP grants You a nonexclusive,
non-transferable, non-sublicensable, revocable, royalty-free,
limited license to use, copy, and modify the Sample Code solely for Your internal
business purposes.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
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.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
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.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
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.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
5. FREE AND OPEN SOURCE COMPONENTS: The Sample Code may include third party
free or open source components ("FOSS Components"). You may have additional
rights in such FOSS Components that are provided by the third party licensors
of those components.
6. THIRD PARTY DEPENDENCIES: The Sample Code may require third party software
dependencies ("Dependencies") for the use or operation of the Sample Code. These
Dependencies may be identified by SAP in Maven POM files, documentation or by
other means. SAP does not grant You any rights in or to such Dependencies under
this Agreement. You are solely responsible for the acquisition, installation
and use of such Dependencies.
7. WARRANTY:
a) If You are located outside the US or Canada: AS THE SAMPLE CODE IS PROVIDED
TO YOU FREE OF CHARGE, SAP DOES NOT GUARANTEE OR WARRANT ANY FEATURES OR
QUALITIES OF THE SAMPLE CODE OR GIVE ANY UNDERTAKING WITH REGARD TO ANY OTHER
QUALITY. NO SUCH WARRANTY OR UNDERTAKING SHALL BE IMPLIED BY YOU FROM ANY
DESCRIPTION IN THE SAMPLE CODE OR ANY OTHER MATERIALS, COMMUNICATION OR
ADVERTISEMENT. IN PARTICULAR, SAP DOES NOT WARRANT THAT THE SAMPLE CODE WILL BE
AVAILABLE UNINTERRUPTED, ERROR FREE, OR PERMANENTLY AVAILABLE. ALL WARRANTY
CLAIMS RESPECTING THE SAMPLE CODE ARE SUBJECT TO THE LIMITATION OF LIABILITY
STIPULATED IN SECTION 8 BELOW.
b) If You are located in the US or Canada: THE SAMPLE CODE IS LICENSED TO YOU
"AS IS", WITHOUT ANY WARRANTY, ESCROW, TRAINING, MAINTENANCE, OR SERVICE
OBLIGATIONS WHATSOEVER ON THE PART OF SAP. SAP MAKES NO EXPRESS OR IMPLIED
WARRANTIES OR CONDITIONS OF SALE OF ANY TYPE WHATSOEVER, INCLUDING BUT NOT
LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY AND OF FITNESS FOR A PARTICULAR
PURPOSE. IN PARTICULAR, SAP DOES NOT WARRANT THAT THE SAMPLE CODE WILL BE
AVAILABLE UNINTERRUPTED, ERROR FREE, OR PERMANENTLY AVAILABLE. YOU ASSUME ALL
RISKS ASSOCIATED WITH THE USE OF THE SAMPLE CODE, INCLUDING WITHOUT LIMITATION
RISKS RELATING TO QUALITY, AVAILABILITY, PERFORMANCE, DATA LOSS, AND UTILITY IN
A PRODUCTION ENVIRONMENT.
c) For all locations: SAP DOES NOT MAKE ANY REPRESENTATIONS OR WARRANTIES IN
RESPECT OF THIRD PARTY DEPENDENCIES, APIS, PRODUCTS AND SERVICES, INCLUDING BUT
NOT LIMITED TO IMPLIED WARRANTIES OF MERCHANTABILITY AND OF FITNESS FOR A
PARTICULAR PURPOSE. IN PARTICULAR, SAP DOES NOT WARRANT THAT THIRDPARTY
DEPENDENCIES, APIS, PRODUCTS AND SERVICES WILL BE AVAILABLE, ERROR FREE,
INTEROPERABLE WITH THE SAMPLE CODE, SUITABLE FOR ANY PARTICULAR PURPOSE OR NONINFRINGING.
YOU ASSUME ALL RISKS ASSOCIATED WITH THE USE OF THIRD
PARTY DEPENDENCIES, APIS, PRODUCTS AND SERVICES, INCLUDING WITHOUT LIMITATION
RISKS RELATING TO QUALITY, AVAILABILITY, PERFORMANCE, DATA LOSS, UTILITY IN A
PRODUCTION ENVIRONMENT, AND NON-INFRINGEMENT. IN NO EVENT WILL SAP BE LIABLE
DIRECTLY OR INDIRECTLY IN RESPECT OF ANY USE OF THIRD PARTY DEPENDENCIES, APIS,
PRODUCTS AND SERVICES BY YOU.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
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.
"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.
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.
"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).
10. EXPORT: The Sample Code is subject to German, EU and US export control
regulations. You confirm that: a) You will not use the Sample Code for, and
will not allow the Sample Code to be used for, any purposes prohibited by
German, EU and US law, including, without limitation, for the development,
design, manufacture or production of nuclear, chemical or biological weapons of
mass destruction; b) You are not located in Cuba, Iran, Sudan, Iraq, North
Korea, Syria, nor any other country to which the United States has prohibited
export or that has been designated by the U.S. Government as a "terrorist
supporting" country (any, an "US Embargoed Country"); c) You are not a citizen,
national or resident of, and are not under the control of, a US Embargoed
Country; d) You will not download or otherwise export or re-export the Sample
Code, directly or indirectly, to a US Embargoed Country nor to citizens,
nationals or residents of a US Embargoed Country; e) You are not listed on the
United States Department of Treasury lists of Specially Designated Nationals,
Specially Designated Terrorists, and Specially Designated Narcotic Traffickers,
nor listed on the United States Department of Commerce Table of Denial Orders
or any other U.S. government list of prohibited or restricted parties and f)
You will not download or otherwise export or re-export the Sample Code, directly
or indirectly, to persons on the above-mentioned lists.
"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.
11. SUPPORT: SAP does not offer support for the Sample Code.
"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."
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.
"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.
13. LAW/VENUE:
a) If You are located outside the US or Canada: This Agreement is governed by
and construed in accordance with the laws of Germany without reference to its
conflicts of law principles. You and SAP agree to submit to the exclusive
jurisdiction of, and venue in, the courts located in Karlsruhe, Germany in any
dispute arising out of or relating to this Agreement or the Sample Code. The
United Nations Convention on Contracts for the International Sale of Goods shall
not apply to this Agreement.
b) If You are located in the US or Canada: This Agreement shall be governed by
and construed in accordance with the laws of the State of New York, USA without
reference to its conflicts of law principles. You and SAP agree to submit to
the exclusive jurisdiction of, and venue in, the courts located in New York,
New York, USA in any dispute arising out of or relating to this Agreement or
the Sample Code. The United Nations Convention on Contracts for the
International Sale of Goods shall not apply to this Agreement.
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.
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.
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.
--------------------------------------------------------------------------------------------------------------------------
APIs
This project may include APIs to SAP or third-party products or services.
The use of these APIs, products and services may be subject to additional
agreements. In no event shall the application of the Apache Software
License, v.2 to this project grant any rights in or to these APIs,
products or services that would alter, expand, be inconsistent with,
or supersede any terms of these additional agreements. “API” means
application programming interfaces, as well as their respective
specifications and implementing code that allows other software
products to communicate with or call on SAP or third party products
or services (for example, SAP Enterprise Services, BAPIs, Idocs, RFCs
and ABAP calls or other user exits) and may be made available through
SAP or third party products, SDKs, documentation or other media.
v1.0-071618

1
NOTICE Normal file
View File

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

View File

@@ -1,58 +1,73 @@
# Welcome to cap/samples
# cloud-cap-samples
Find here a collection of samples for the [SAP Cloud Application Programming Model](https://cap.cloud.sap) organized in a simplistic [monorepo setup](samples.md#all-in-one-monorepo). &rarr; See [**Overview** of contained samples](samples.md)
This is a monorepository for sample projects on [SAP Cloud Application Programming Model](https://cap.cloud.sap).
![](https://github.com/SAP-samples/cloud-cap-samples/workflows/CI/badge.svg)
## Description
### Preliminaries
This repository provides a list of samples and reusable packages created based on SAP Cloud Application Programming Model.
The SAP Cloud Application Programming Model enables you to quickly create business applications by allowing you to focus on your domain logic. It offers a consistent end-to-end programming model that includes languages, libraries and APIs tailored for full-stack development on SAP Cloud Platform.
1. [Install @sap/cds-dk](https://cap.cloud.sap/docs/get-started/) as documented in [capire](https://cap.cloud.sap)
2. _Optional:_ [Use Visual Studio Code](https://cap.cloud.sap/docs/get-started/in-vscode)
The samples provided can be run in a local setup on SQLite Database.
### Download
## Requirements
* [Node.js](https://nodejs.org/en/) v8 or higher
* [Git](https://git-scm.com)
* [SQLite DB](https://www.sqlite.org/download.html) (Windows only; pre-installed on Mac/Linux)
Clone this repo as shown below, if you have [git](https://git-scm.com/downloads) installed,
otherwise [download as zip file](archive/master.zip).
#### Optional (if you want to import the code into an editor)
* [VS Code](https://code.visualstudio.com)
* [Add CDS extension to VS](https://cap.cloud.sap/docs/get-started/in-vscode#add-cds-editor)
## Download and Installation
#### Install `cds` development kit
```sh
git clone https://github.com/sap-samples/cloud-cap-samples samples
cd samples
# sets the registry for `@sap` packages
npm set @sap:registry=https://npm.sap.com
npm install -g @sap/cds-dk
cds #> test-run it
```
Got issues? Check out the [documentation](https://cap.cloud.sap/docs/get-started/).
### Setup
#### Clone and build the application
`git clone https://github.com/SAP-samples/cloud-cap-samples samples && cd samples && npm i`
In the samples folder run:
#### Run the samples
```sh
npm install
```
With that you're ready to run the samples, e.g. start the [_bookshop_](./packages/bookshop) sample as follows:
### Run
`npm run bookshop`
With that you're ready to run the samples, for example:
## Test
```sh
cds watch bookshop
```
After that open this link in your browser: [http://localhost:4004](http://localhost:4004)
### 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.
For example, try these links in your browser:
- <http://localhost:4004> to test with generic index page.
- <http://localhost:4004/fiori.html> to test with Fiori sandbox.
## Get Support
## Debug
Check out the documentation at [https://cap.cloud.sap](https://cap.cloud.sap). <br>
In case you have a question, find a bug, or otherwise need support, please use our [community](https://answers.sap.com/tags/9f13aee1-834c-4105-8e43-ee442775e5ce).
For example, in [VS Code](https://code.visualstudio.com) switch to _Debug_ view and launch one of the prepared _cds run_ launch configurations.
## Limitations
None
## Known Issues
None
## How to obtain support
Check out the documentation on https://cap.cloud.sap. In case you find a bug, or you need additional support, please open an issue [here](https://github.com/SAP-samples/cloud-cap-samples/issues/new) in GitHub.
## To-Do (upcoming changes)
None
## License
Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the Apache Software License, version 2.0 except as noted otherwise in the [LICENSE](/LICENSE) file.
Copyright (c) 2019 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 +0,0 @@
cds.features.snapi = y

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,45 +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: { descr:'( click on a row to see details... )' },
order: { amount:1, succeeded:'', failed:'' }
},
methods: {
search: ({target:{value:v}}) => books.fetch (v && '$search='+v),
async fetch (_filter='') {
const columns = 'ID,title,author,price,stock', details = 'genre,currency'
const {data} = await GET(`/Books?$select=${columns}&$expand=${details}&${_filter}`)
books.list = data.value
},
async inspect () {
const book = books.book = books.list [event.currentTarget.rowIndex-1]
book.descr || await GET(`/Books/${book.ID}/descr/$value`) .then (({data}) => book.descr = data)
books.order = { amount:1 }
setTimeout (()=> $('form > input').focus(), 111)
},
submitOrder () { event.preventDefault()
const {book,order} = books, amount = parseInt (order.amount) || 1
POST(`/submitOrder`, { amount, book: book.ID })
.then (()=> books.order = { amount, succeeded: `Successfully orderd ${amount} item(s).` })
.catch (e=> books.order = { amount, failed: e.response.data.error.message })
GET(`/Books/${book.ID}/stock/$value`).then (res => book.stock = res.data)
}
}
})
// initially fill list of books
books.fetch()

View File

@@ -1,59 +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>
#books tr:hover { background: #f2f2f2; cursor: pointer; }
form { float:right; display:flex; flex-direction: row-reverse }
form #amount { width: 5em }
.is-success { color: #0d920d }
.has-error { color: #df1010 }
</style>
</head>
<body class="small-container", style="margin-top: 70px;">
<div id='app'>
<h1> Capire Books </h1>
<input type="text" placeholder="Search..." @input="search">
<table id='books'>
<thead>
<th> Book </th>
<th> Author </th>
<th> Genre </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>{{ book.currency.symbol }} {{ book.price }}</td>
</tr>
</table>
<div v-if="book.title">
<label style="text-align:right">
<span class="is-success"> {{ order.succeeded }} </span>
<span class="has-error"> {{ order.failed }} </span>
&nbsp;&nbsp; {{ book.stock }} in stock
</label>
<form @submit="submitOrder">
<input type="number" id="amount" v-model="order.amount" v-bind:class="{ 'has-error': order.failed }">
<input type="submit" value="Order:" class="muted-button">
</form>
</div>
<h4> {{ book.title }} </h4>
<p> {{ book.descr }} </p>
</div>
</body>
<script src="app.js"></script>
</html>

View File

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

View File

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

View File

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

View File

@@ -1,22 +0,0 @@
{
"name": "@capire/bookshop",
"version": "1.0.0",
"description": "A simple self-contained bookshop service.",
"dependencies": {
"@capire/common": "../common",
"@sap/cds": ">=3.33.1",
"express": "^4.17.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 and Layouts](https://cap.cloud.sap/docs/get-started/projects#sharing-and-reusing-content) | [`./`](./) |
| [Defining Domain Models](https://cap.cloud.sap/docs/guides/domain-models) | [`./db/schema.cds`](./db/schema.cds) |
| [Defining Services](https://cap.cloud.sap/docs/guides/providing-services) | [`./srv/*.cds`](./srv) |
| [Single-purposed Services](https://cap.cloud.sap/docs/guides/providing-services#single-purposed-services) | [`./srv/*.cds`](./srv) |
| [Generic Providers](https://cap.cloud.sap/docs/guides/providing-services) | http://localhost:4004 |
| Using 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/get-started/projects#sharing-and-reusing-content) | [`./index.cds`](./index.cds) |

View File

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

View File

@@ -1,20 +0,0 @@
const cds = require('@sap/cds')
module.exports = async function (){
const db = await cds.connect.to('db') // connect to database service
const { Books } = db.entities // get reflected definitions
// Reduce stock of ordered books if available stock suffices
this.on ('submitOrder', async req => {
const {book,amount} = req.data
const n = await UPDATE (Books, book)
.with ({ stock: {'-=': amount }})
.where ({ stock: {'>=': amount }})
n > 0 || 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!`
})
}

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,51 +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:
### ------------------------------------------------------------------------
# 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,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,4 +0,0 @@
{
"name": "@capire/common",
"version": "1.0.0"
}

View File

@@ -1 +0,0 @@
cds.features.snapi = y

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -1,120 +0,0 @@
using OrdersService from '@capire/orders/srv/orders-service';
annotate OrdersService.Books with {
price @Common.FieldControl: #ReadOnly;
}
////////////////////////////////////////////////////////////////////////////
//
// Common
//
annotate OrdersService.OrderItems with {
book @(
Common: {
Text: book.title,
FieldControl: #Mandatory
},
ValueList.entity:'Books',
);
amount @(
Common.FieldControl: #Mandatory
);
}
@odata.draft.enabled
annotate OrdersService.Orders with @(
UI: {
////////////////////////////////////////////////////////////////////////////
//
// Lists of Orders
//
SelectionFields: [ createdAt, createdBy ],
LineItem: [
{Value: createdBy, Label:'Customer'},
{Value: createdAt, Label:'Date'}
],
////////////////////////////////////////////////////////////////////////////
//
// Order Details
//
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;
};
//The enity types name is OrdersService.my_bookshop_OrderItems
//The annotations below are not generated in edmx WHY?
annotate OrdersService.OrderItems with @(
UI: {
HeaderInfo: {
TypeName: 'Order Item', TypeNamePlural: ' ',
Title: {
Value: book.title
},
Description: {Value: book.descr}
},
// There is no filterbar for items so the selctionfileds is not needed
SelectionFields: [ book_ID ],
////////////////////////////////////////////////////////////////////////////
//
// Lists of OrderItems
//
LineItem: [
{Value: book_ID, Label:'Book'},
//The following entry is only used to have the assoication followed in the read event
{Value: book.price, Label:'Book Price'},
{Value: amount, Label:'Quantity'},
],
Identification: [ //Is the main field group
//{Value: ID, Label:'ID'}, //A guid shouldn't be on the UI
{Value: book_ID, Label:'Book'},
{Value: amount, Label:'Amount'},
],
Facets: [
{$Type: 'UI.ReferenceFacet', Label: '{i18n>OrderItems}', Target: '@UI.Identification'},
],
},
);

View File

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

View File

@@ -1,3 +0,0 @@
// Proxy for importing schema from bookshop sample
using from '@capire/bookshop';
namespace sap.capire.bookshop;

View File

@@ -1,22 +0,0 @@
{
"name": "@capire/fiori",
"version": "1.0.0",
"dependencies": {
"@capire/bookshop": "../bookshop",
"@capire/orders": "../orders",
"@capire/common": "../common",
"@sap/cds": ">=3.33.1",
"express": "^4.17.1"
},
"scripts": {
"start": "cds run --in-memory?",
"watch": "cds watch"
},
"cds": {
"requires": {
"db": {
"kind": "sql"
}
}
}
}

View File

@@ -1,3 +0,0 @@
// Proxy for importing services from bookshop sample
using from '@capire/bookshop';
annotate AdminService with @impl:'srv/admin-service.js';

View File

@@ -1,8 +0,0 @@
const cds = require('@sap/cds')
module.exports = cds.service.impl (async function() {
const {Books} = cds.entities
const {ID} = await SELECT.one.from(Books).columns('max(ID) as ID')
let newID = ID - ID % 100 + 100
this.before ('NEW','Books', req => req.data.ID = ++newID)
})

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

View File

@@ -1,16 +0,0 @@
using { sap.capire.bookshop.Books } from '@capire/bookshop';
using { Currency, managed, cuid } from '@sap/cds/common';
namespace sap.capire.bookshop;
entity Orders : cuid, managed {
OrderNo : String @title:'Order Number'; //> readable key
Items : Composition of many OrderItems on Items.parent = $self;
currency : Currency;
}
entity OrderItems : cuid {
parent : Association to Orders;
book : Association to Books;
amount : Integer;
netAmount : Decimal(9,2);
}

View File

@@ -1,4 +0,0 @@
{
"name": "@capire/orders",
"version": "1.0.0"
}

View File

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

View File

@@ -1,21 +0,0 @@
const cds = require('@sap/cds')
module.exports = cds.service.impl(function() {
const { Books } = cds.entities
// Reduce stock of ordered books if available stock suffices
this.before ('CREATE', 'Orders', (req) => {
const { Items: items } = req.data
return cds.transaction(req) .run (items.map (item =>
UPDATE (Books) .where ('ID =', item.book_ID)
.and ('stock >=', item.amount)
.set ('stock -=', item.amount)
)) .then (all => all.forEach ((affectedRows,i) => {
if (affectedRows === 0) req.error (409,
`${items[i].amount} exceeds stock for book #${items[i].book_ID}`
)
}))
})
})

View File

@@ -1,33 +1,28 @@
{
"name": "@capire/samples",
"version": "2.0.0",
"description": "A monorepo with several samples for CAP.",
"repository": "https://github.com/sap-samples/cloud-cap-samples.git",
"name": "@sap/capire-samples",
"description": "The umbrella project for all samples to easily setup for local development and tests.",
"repository": "https://github.com/SAP-samples/cloud-cap-samples.git",
"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"
},
"dependencies": {
"@capire/bookshop": "file:bookshop",
"@capire/common": "file:common",
"@capire/fiori": "file:fiori",
"@capire/orders": "file:orders",
"@capire/reviews": "file:reviews",
"@capire/tests": "file:test"
"@sap/cds": "latest",
"express": "*"
},
"devDependencies": {
"chai": "^4.2.0",
"chai-as-promised": "^7.1.1",
"chai-subset": "^1.6.0",
"sqlite3": "^4"
"sqlite3": "*"
},
"scripts": {
"bookshop": "cds watch bookshop",
"fiori": "cds watch fiori",
"mocha": "npx mocha || echo",
"jest": "npx jest --verbose",
"test": "npm run jest -s"
"--add-these-to-devDependencies-for-tests": {
"@types/jest": "*",
"jest": "*"
},
"jest": {
"testEnvironment": "node"
},
"license": "SAP SAMPLE CODE LICENSE",
"private": true
"license": "SAP SAMPLE CODE LICENSE"
}

View File

@@ -0,0 +1 @@
using from '@sap/capire-bookshop/app';

View File

@@ -0,0 +1,25 @@
/*
In this model we demonstrate how to add Genres to Books in
as if it was an external extension. For example we use
CDS Aspects' to extend the core domain model's Books entity
as well as the AdminService.
*/
namespace sap.capire.bookshop;
using { sap.capire.reviews.ReviewsService as external } from '@sap/capire-reviews';
using { sap.capire.bookshop.Books } from '@sap/capire-bookshop/db/schema';
using { sap.common.CodeList } from '@sap/cds/common';
// Extending Books by Reviews and Genres
extend Books with {
reviews : Composition of many external.Reviews on reviews.subject = ID;
rating : external.Reviews.rating;
genre : Association to Genres;
}
// Hierarchical Code List for Genres
entity Genres : CodeList {
key ID : Integer;
children : Composition of many Genres on children.parent = $self;
parent : Association to Genres;
}

View File

@@ -0,0 +1,29 @@
{
"name": "@sap/capire-bookshop-enhanced",
"version": "1.0.0",
"description": "A sample for extending a base application package, in this case bookshop, e.g. in context of verticalization or customization.",
"repository": "https://github.com/SAP-samples/cloud-cap-samples.git",
"license": "SAP SAMPLE CODE LICENSE",
"dependencies": {
"@sap/capire-bookshop": "^1.0.0",
"@sap/capire-reviews": "^1.0.0",
"@sap/cds": "latest",
"express": "*"
},
"scripts": {
"reviews-service": "PORT=5005 cds run ../reviews-service --bind --in-memory?",
"start": "cds run --in-memory?",
"watch": "cds watch"
},
"cds": {
"requires": {
"sap.capire.reviews.ReviewsService": {
"kind": "odata",
"model": "@sap/capire-reviews"
},
"messaging": {
"kind": "file-based-messaging"
}
}
}
}

View File

@@ -0,0 +1,9 @@
namespace sap.capire.bookshop;
using { AdminService } from '@sap/capire-bookshop/srv/admin-service';
using { sap.capire.bookshop } from '../db/schema';
@impl:'srv/services'
extend service AdminService with {
entity Genres as projection on bookshop.Genres;
}

View File

@@ -0,0 +1,30 @@
const cds = require ('@sap/cds')
module.exports = cds.service.impl (async()=>{
const ReviewsService = await cds.connect.to ('sap.capire.reviews.ReviewsService')
const CatalogService = await cds.connect.to ('CatalogService')
const db = await cds.connect.to ('db')
// import model definitions from connected services to work with subsequently
const { Books } = db.entities
const { Reviews } = ReviewsService.entities
CatalogService.impl (srv => {
// delegate requests to read reviews to ReviewsService
srv.on ('READ', 'Books/reviews', (req) => {
const [ subject ] = req.params
const tx = ReviewsService.transaction (req)
return tx.run (SELECT.from (Reviews) .where ({subject}))
})
})
// react on event messages from reviews service
ReviewsService.on ('reviewed', (msg) => {
console.debug ('> received:', msg.event, msg.data)
const { subject, rating } = msg.data
const tx = db // TODO: db.transaction (msg)
return tx.run (UPDATE (Books, subject) .with ({rating}))
// return tx.update (Books, subject) .with ({rating})
})
})

View File

@@ -3,15 +3,10 @@
# Genres
#
GET http://localhost:4004/test/Genres?
GET http://localhost:4004/admin/Genres?
###
GET http://localhost:4004/test/Genres?
&$filter=parent_ID eq null&$select=name
&$expand=children($select=name)
###
POST http://localhost:4004/test/Genres?
POST http://localhost:4004/admin/Genres?
Content-Type: application/json
{ "ID":100, "name":"Some Sample Genres...", "children":[
@@ -26,13 +21,13 @@ Content-Type: application/json
]}
###
GET http://localhost:4004/test/Genres(100)?
GET http://localhost:4004/admin/Genres(100)?
# &$expand=children
# &$expand=children($expand=children($expand=children($expand=children)))
###
DELETE http://localhost:4004/test/Genres(103)
DELETE http://localhost:4004/admin/Genres(103)
###
DELETE http://localhost:4004/test/Genres(100)
DELETE http://localhost:4004/admin/Genres(100)
###

View File

@@ -0,0 +1,32 @@
#################################################
#
# Reviews Service
#
### Use this one for ReviewsService running as a separate process
# Note: use 5005 instead of 4004 in case of separate service
POST http://localhost:5005/reviews/Reviews
# POST http://localhost:4004/reviews/Reviews
Content-Type: application/json;IEEE754Compatible=true
{"subject":"201", "rating":"5", "title":"boo"}
### Direct Request to ReviewsService
# Note: use 5005 instead of 4004 in case of separate service
GET http://localhost:5005/reviews/Reviews?
# GET http://localhost:4004/reviews/Reviews?
# &$filter=subject eq '201'
### Request to CatalogService > delegated to ReviewsService
GET http://localhost:4004/browse/Books(201)/reviews
### Alternative OData URL
GET http://localhost:4004/browse/Books/201/reviews
###
GET http://localhost:4004/browse/Books(201)?
&$select=ID,title,rating
# &$expand=reviews
# Note: the latter only works in case of ReviewsService in same process

View File

@@ -0,0 +1,3 @@
{
"printWidth": 120
}

View File

@@ -0,0 +1,65 @@
# Bookshop With Address Data From SAP S/4HANA
This is an extended bookshop with business-partner address data from SAP S/4HANA.
When the user creates an order and uses the value help of the shipping address,
a synchronous request to SAP S/4HANA is triggered yielding all possible addresses
belonging to this business partner. Once an address is selected, its data
is replicated into a local database. To keep data in sync, an event handler
is registered which listens to all changes of business partners and updates the
local database table.
## Prerequisites
`@sap/cds` >= 1.30
## Running With Mocks
Just execute the following command in the `bookshop` folder.
```
cds run --in-memory --with-mocks
```
## Running With an S/4HANA Backend
To run your app in non-mock mode you need an SAP S/4HANA Cloud system and connect it to your SAP Cloud Platform. You can use the
[SAP Cloud Platform Extension Factory](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/346864df64f24011b49abee07bbd79af.html) to automate parts of this task. You need to enable synchronous APIs as well as events that are sent whenever business partners are changed.
To run the app locally, you need to create a `default-env.json` file in the `bookshop` folder containing the binding information (credentials of Enterprise Messaging as well as the destination to the business-partner service).
Provide the credentials in the `cds.requires` section of the `package.json` file in the `bookshop` folder, e.g.
```json
"cds": {
"requires": {
"API_BUSINESS_PARTNER": {
"kind": "odata",
"model": "srv/external",
"credentials": {
"destination": "cap-api098"
}
},
"messaging": {
"kind": "enterprise-messaging",
"credentials": {
"prefix": "sap/S4HANAOD/c098/BO"
}
}
}
}
```
Here, `destination` is the destination of your business-partner service and `prefix` is the prefix
of the topic of the events.
Then simply run the following command in the `bookshop` folder.
```
cds run --in-memory
```
## User Flow
After starting the app, go to http://localhost:4004/fiori.html#Shell-home and open the app `Manage Orders` to create an order.
Use the value help of the shipping address to select an address. Create an order item and save the order.
Then change the address of your business partner (in the mocked case you can trigger the PATCH request in `req.http` ). Refresh
the object page of your order and see the change.

View File

@@ -0,0 +1,22 @@
Books = Books
Book = Book
ID = ID
Title = Title
Author = Author
AuthorID = Author ID
Stock = Stock
Name = Name
AuthorName = Author's Name
Authors = Authors
Order = Order
OrderItems = Order Items
Orders = Orders
Price = Price
shippingAddress = Shipping Address
cityName = City Name
houseNumber = House Number
streetName = Street Name
postalCode = Postal Code
country = Country
AddressID = Address ID
contact = Contact

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@@ -28,7 +28,7 @@
navigationMode: "embedded"
},
"manage-orders": {
title: "Order Books",
title: "Manage Orders",
description: "... testing FE v42",
additionalInformation: "SAPUI5.Component=orders",
applicationType : "URL",
@@ -40,8 +40,7 @@
</script>
<script src="https://sapui5.hana.ondemand.com/test-resources/sap/ushell/bootstrap/sandbox.js"></script>
<!-- <script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js" -->
<script src="https://sapui5.hana.ondemand.com/1.78.6/resources/sap-ui-core.js"
<script src="https://sapui5.hana.ondemand.com/resources/sap-ui-core.js"
data-sap-ui-libs="sap.m, sap.ushell, sap.collaboration, sap.ui.layout"
data-sap-ui-compatVersion="edge"
data-sap-ui-theme="sap_fiori_3"

View File

@@ -6,5 +6,3 @@ using from './admin/fiori-service';
using from './browse/fiori-service';
using from './orders/fiori-service';
using from './common';
using from '@capire/common';

View File

@@ -0,0 +1,243 @@
using AdminService from '../../srv/admin-service';
annotate AdminService.Books with {
price @Common.FieldControl : #ReadOnly;
}
////////////////////////////////////////////////////////////////////////////
//
// Common
//
annotate AdminService.OrderItems with {
book @(
Common : {
Text : book.title,
FieldControl : #Mandatory
},
ValueList.entity : 'Books',
);
amount @(Common.FieldControl : #Mandatory);
}
annotate AdminService.Orders with {
shippingAddress @(Common : {
FieldControl : #Mandatory,
ValueList : {
CollectionPath : 'Addresses',
Label : 'Addresses',
SearchSupported : 'true',
Parameters : [
{
$Type : 'Common.ValueListParameterOut',
LocalDataProperty : 'shippingAddress_ID',
ValueListProperty : 'ID'
},
{
$Type : 'Common.ValueListParameterOut',
LocalDataProperty : 'shippingAddress_contact',
ValueListProperty : 'contact'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'postalCode'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'cityName'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'country'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'streetName'
},
{
$Type : 'Common.ValueListParameterDisplayOnly',
ValueListProperty : 'houseNumber'
},
]
},
});
}
////////////////////////////////////////////////////////////////////////////
//
// UI
//
annotate AdminService.Orders with @(UI : {
////////////////////////////////////////////////////////////////////////////
//
// Lists of Orders
//
SelectionFields : [
createdAt,
createdBy
],
LineItem : [
{
Value : createdBy,
Label : 'Customer'
},
{
Value : createdAt,
Label : 'Date'
}
],
////////////////////////////////////////////////////////////////////////////
//
// Order Details
//
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
// labels not considered
{
Value : createdBy,
Label : 'Customer'
},
{
Value : createdAt,
Label : 'Date'
},
{Value : OrderNo},
{
Value : 'shippingAddress_ID',
Label : 'Address ID'
}
],
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>shippingAddress}',
Target : '@UI.FieldGroup#ShippingAddress'
},
{
$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},
]},
FieldGroup #ShippingAddress : {Data : [
{
Value : shippingAddress_ID,
Label : '{i18n>shippingAddress}'
},
{
Value : shippingAddress.houseNumber,
Label : '{i18n>houseNumber}'
},
{
Value : shippingAddress.streetName,
Label : '{i18n>streetName}'
},
{
Value : shippingAddress.cityName,
Label : '{i18n>cityName}'
},
{
Value : shippingAddress.postalCode,
Label : '{i18n>postalCode}'
},
]},
},
Common.SideEffects : {
EffectTypes : #ValueChange,
SourceProperties : [shippingAddress_ID],
TargetProperties : [
shippingAddress.country,
shippingAddress.houseNumber,
shippingAddress.streetName,
shippingAddress.cityName,
shippingAddress.postalCode
]
},
) {
createdAt @UI.HiddenFilter : false;
createdBy @UI.HiddenFilter : false;
};
//The enity types name is AdminService.my_bookshop_OrderItems
//The annotations below are not generated in edmx WHY?
annotate AdminService.OrderItems with @(UI : {
HeaderInfo : {
TypeName : 'Order Item',
TypeNamePlural : ' ',
Title : {Value : book.title},
Description : {Value : book.descr}
},
// There is no filterbar for items so the selctionfileds is not needed
SelectionFields : [book_ID],
////////////////////////////////////////////////////////////////////////////
//
// Lists of OrderItems
//
LineItem : [
{
Value : book_ID,
Label : 'Book'
},
//The following entry is only used to have the assoication followed in the read event
{
Value : book.price,
Label : 'Book Price'
},
{
Value : amount,
Label : 'Quantity'
},
],
Identification : [ //Is the main field group
//{Value: ID, Label:'ID'}, //A guid shouldn't be on the UI
{
Value : book_ID,
Label : 'Book'
},
{
Value : amount,
Label : 'Amount'
},
],
Facets : [{
$Type : 'UI.ReferenceFacet',
Label : '{i18n>OrderItems}',
Target : '@UI.Identification'
}, ],
}, );

View File

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

View File

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

View File

@@ -1,6 +1,6 @@
ID;title;descr;author_ID;stock;price;currency_code;genre_ID
201;Wuthering Heights;"Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym ""Ellis Bell"". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850.";101;12;11.11;GBP;11
207;Jane Eyre;"Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name ""Currer Bell"", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism.";107;11;12.34;GBP;11
251;The Raven;"""The Raven"" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word ""Nevermore"". The poem makes use of folk, mythological, religious, and classical references.";150;333;13.13;USD;16
252;Eleonora;"""Eleonora"" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively ""happy"" ending.";150;555;14;USD;16
271;Catweazle;Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts.;170;22;15;EUR;13
ID;title;descr;author_ID;stock;price;currency_code
201;Wuthering Heights;"Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym ""Ellis Bell"". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850.";101;12;11.11;GBP
207;Jane Eyre;"Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name ""Currer Bell"", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism.";107;11;12.34;GBP
251;The Raven;"""The Raven"" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word ""Nevermore"". The poem makes use of folk, mythological, religious, and classical references.";150;333;13.13;USD
252;Eleonora;"""Eleonora"" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively ""happy"" ending.";150;555;14;USD
271;Catweazle;Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts.;170;22;15;EUR
1 ID title descr author_ID stock price currency_code genre_ID
2 201 Wuthering Heights Wuthering Heights, Emily Brontë's only novel, was published in 1847 under the pseudonym "Ellis Bell". It was written between October 1845 and June 1846. Wuthering Heights and Anne Brontë's Agnes Grey were accepted by publisher Thomas Newby before the success of their sister Charlotte's novel Jane Eyre. After Emily's death, Charlotte edited the manuscript of Wuthering Heights and arranged for the edited version to be published as a posthumous second edition in 1850. 101 12 11.11 GBP 11
3 207 Jane Eyre Jane Eyre /ɛər/ (originally published as Jane Eyre: An Autobiography) is a novel by English writer Charlotte Brontë, published under the pen name "Currer Bell", on 16 October 1847, by Smith, Elder & Co. of London. The first American edition was published the following year by Harper & Brothers of New York. Primarily a bildungsroman, Jane Eyre follows the experiences of its eponymous heroine, including her growth to adulthood and her love for Mr. Rochester, the brooding master of Thornfield Hall. The novel revolutionised prose fiction in that the focus on Jane's moral and spiritual development is told through an intimate, first-person narrative, where actions and events are coloured by a psychological intensity. The book contains elements of social criticism, with a strong sense of Christian morality at its core and is considered by many to be ahead of its time because of Jane's individualistic character and how the novel approaches the topics of class, sexuality, religion and feminism. 107 11 12.34 GBP 11
4 251 The Raven "The Raven" is a narrative poem by American writer Edgar Allan Poe. First published in January 1845, the poem is often noted for its musicality, stylized language, and supernatural atmosphere. It tells of a talking raven's mysterious visit to a distraught lover, tracing the man's slow fall into madness. The lover, often identified as being a student, is lamenting the loss of his love, Lenore. Sitting on a bust of Pallas, the raven seems to further distress the protagonist with its constant repetition of the word "Nevermore". The poem makes use of folk, mythological, religious, and classical references. 150 333 13.13 USD 16
5 252 Eleonora "Eleonora" is a short story by Edgar Allan Poe, first published in 1842 in Philadelphia in the literary annual The Gift. It is often regarded as somewhat autobiographical and has a relatively "happy" ending. 150 555 14 USD 16
6 271 Catweazle Catweazle is a British fantasy television series, starring Geoffrey Bayldon in the title role, and created by Richard Carpenter for London Weekend Television. The first series, produced and directed by Quentin Lawrence, was screened in the UK on ITV in 1970. The second series, directed by David Reid and David Lane, was shown in 1971. Each series had thirteen episodes, most but not all written by Carpenter, who also published two books based on the scripts. 170 22 15 EUR 13

View File

@@ -1,5 +1,4 @@
ID;locale;title;descr
201;de;Sturmhöhe;Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (18181848). Der 1847 unter dem Pseudonym Ellis Bell veröffentlichte Roman wurde vom viktorianischen Publikum weitgehend abgelehnt, heute gilt er als ein Klassiker der britischen Romanliteratur des 19. Jahrhunderts.
201;fr;Les Hauts de Hurlevent;Les Hauts de Hurlevent (titre original : Wuthering Heights), parfois orthographié Les Hauts de Hurle-Vent, est l'unique roman d'Emily Brontë, publié pour la première fois en 1847 sous le pseudonyme dEllis Bell. Loin d'être un récit moralisateur, Emily Brontë achève néanmoins le roman dans une atmosphère sereine, suggérant le triomphe de la paix et du Bien sur la vengeance et le Mal.
207;de;Jane Eyre;Jane Eyre. Eine Autobiographie (Originaltitel: Jane Eyre. An Autobiography), erstmals erschienen im Jahr 1847 unter dem Pseudonym Currer Bell, ist der erste veröffentlichte Roman der britischen Autorin Charlotte Brontë und ein Klassiker der viktorianischen Romanliteratur des 19. Jahrhunderts. Der Roman erzählt in Form einer Ich-Erzählung die Lebensgeschichte von Jane Eyre (ausgesprochen /ˌdʒeɪn ˈɛə/), die nach einer schweren Kindheit eine Stelle als Gouvernante annimmt und sich in ihren Arbeitgeber verliebt, jedoch immer wieder um ihre Freiheit und Selbstbestimmung kämpfen muss. Als klein, dünn, blass, stets schlicht dunkel gekleidet und mit strengem Mittelscheitel beschrieben, gilt die Heldin des Romans Jane Eyre nicht zuletzt aufgrund der Kino- und Fernsehversionen der melodramatischen Romanvorlage als die bekannteste englische Gouvernante der Literaturgeschichte
252;de;Eleonora;“Eleonora” ist eine Erzählung von Edgar Allan Poe. Sie wurde 1841 erstveröffentlicht. In ihr geht es um das Paradox der Treue in der Treulosigkeit.
1 ID locale title descr
2 201 de Sturmhöhe Sturmhöhe (Originaltitel: Wuthering Heights) ist der einzige Roman der englischen Schriftstellerin Emily Brontë (1818–1848). Der 1847 unter dem Pseudonym Ellis Bell veröffentlichte Roman wurde vom viktorianischen Publikum weitgehend abgelehnt, heute gilt er als ein Klassiker der britischen Romanliteratur des 19. Jahrhunderts.
201 fr Les Hauts de Hurlevent Les Hauts de Hurlevent (titre original : Wuthering Heights), parfois orthographié Les Hauts de Hurle-Vent, est l'unique roman d'Emily Brontë, publié pour la première fois en 1847 sous le pseudonyme d’Ellis Bell. Loin d'être un récit moralisateur, Emily Brontë achève néanmoins le roman dans une atmosphère sereine, suggérant le triomphe de la paix et du Bien sur la vengeance et le Mal.
3 207 de Jane Eyre Jane Eyre. Eine Autobiographie (Originaltitel: Jane Eyre. An Autobiography), erstmals erschienen im Jahr 1847 unter dem Pseudonym Currer Bell, ist der erste veröffentlichte Roman der britischen Autorin Charlotte Brontë und ein Klassiker der viktorianischen Romanliteratur des 19. Jahrhunderts. Der Roman erzählt in Form einer Ich-Erzählung die Lebensgeschichte von Jane Eyre (ausgesprochen /ˌdʒeɪn ˈɛə/), die nach einer schweren Kindheit eine Stelle als Gouvernante annimmt und sich in ihren Arbeitgeber verliebt, jedoch immer wieder um ihre Freiheit und Selbstbestimmung kämpfen muss. Als klein, dünn, blass, stets schlicht dunkel gekleidet und mit strengem Mittelscheitel beschrieben, gilt die Heldin des Romans Jane Eyre nicht zuletzt aufgrund der Kino- und Fernsehversionen der melodramatischen Romanvorlage als die bekannteste englische Gouvernante der Literaturgeschichte
4 252 de Eleonora “Eleonora” ist eine Erzählung von Edgar Allan Poe. Sie wurde 1841 erstveröffentlicht. In ihr geht es um das Paradox der Treue in der Treulosigkeit.

View File

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

View File

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

View File

@@ -0,0 +1,47 @@
{
"name": "@sap/capire-bookshop",
"version": "1.0.0",
"description": "A simple bookshop application, build in a self-contained all-in-one fashion, i.e. w/o reusing other packages.",
"license": "SAP SAMPLE CODE LICENSE",
"dependencies": {
"@sap/cds": "latest",
"@sap/xb-msg-amqp-v100": "^0.9.31-SNAPSHOT",
"express": "*",
"passport": "^0.4.0",
"sqlite3": "^4.1.0"
},
"scripts": {
"start": "cds run --in-memory?",
"watch": "cds watch"
},
"cds": {
"requires": {
"API_BUSINESS_PARTNER": {
"kind": "odata",
"model": "srv/external",
"--credentials": {
"destination": "cap-api098"
}
},
"--messaging": {
"kind": "enterprise-messaging",
"credentials": {
"prefix": "sap/S4HANAOD/c098/BO"
}
}
},
"auth": {
"passport": {
"strategy": "mock",
"users": {
"alice": {
"roles": [
"admin"
],
"ID": "ALICE"
}
}
}
}
}
}

View File

@@ -0,0 +1,19 @@
PATCH http://localhost:4004/api-business-partner/A_BusinessPartnerAddress(BusinessPartner='ALICE',AddressID='62640')
Content-Type: application/json
Authorization: Basic QUxJQ0Utc2VjcmV0
{
"PostalCode": "123456",
"CityName": "AlteredTown"
}
###
GET http://localhost:4004/admin/Orders(ID=7e2f2640-6866-4dcf-8f4d-3027aa831cad,IsActiveEntity=false)?
&$expand=shippingAddress($select=cityName,houseNumber,postalCode,streetName)
Authorization: Basic QUxJQ0U6c2VjcmV0
###
DELETE http://localhost:4004/api-business-partner/A_BusinessPartnerAddress(BusinessPartner='ALICE',AddressID='62640')

View File

@@ -0,0 +1,16 @@
using { sap.capire.bookshop as my } from '../db/schema';
service AdminService @(requires:'admin') {
entity Books as projection on my.Books;
entity Authors as projection on my.Authors;
entity Orders as select from my.Orders;
}
// Enable Fiori Draft for Orders
annotate AdminService.Orders with @odata.draft.enabled;
// annotate AdminService.Books with @odata.draft.enabled;
// Temporary workaround -> https://github.wdf.sap.corp/cap/issues/issues/3121
extend service AdminService with {
entity OrderItems as select from my.OrderItems;
}

View File

@@ -0,0 +1,77 @@
const cds = require('@sap/cds')
module.exports = cds.service.impl(async () => {
// We are mashing up three services...
const bupa = await cds.connect.to('API_BUSINESS_PARTNER')
const admin = await cds.connect.to('AdminService')
const db = await cds.connect.to('db')
// Using reflected definitions from connected services/database
const { Addresses: externalAddresses } = bupa.entities // projection on external addresses
const { Books, Addresses } = db.entities('sap.capire.bookshop') // entities in local database
// Delegate ValueHelp requests to S/4 backend, fetching current user's addresses from there
admin.on('READ', 'Addresses', req => {
console.log('Delegating to S/4 bupa service...')
const UsersAddresses = SELECT.from(externalAddresses)
.where({ contact: req.user.id })
.and(req.query.SELECT.where)
return bupa.tx(req).run(UsersAddresses)
})
// Replicate chosen addresses from S/4 when filling orders.
admin.before('PATCH', 'Orders', async req => {
const assigned = { ID: req.data.shippingAddress_ID, contact: req.user.id }
if (!assigned.ID) return //> something else
const local = db.transaction(req)
const [replica] = await local.read(Addresses).where(assigned)
if (replica) return //> already replicated
const [address] = await bupa.tx(req).run(SELECT.from(externalAddresses).where(assigned))
if (address) return local.create(Addresses).entries(address)
})
// Subscribe to S/4 event to update local replicas when sources change in S/4.
bupa.on('BusinessPartner/Changed', async msg => {
console.log('>> received:', msg.data)
const BuPaID = msg.data.KEY[0].BUSINESSPARTNER //> S/4HANA's weird payload format
const { SELECT, UPDATE } = cds.ql(msg) //> convenient alternative to <srv>.transaction(req).run(SELECT...)
// fetch affected entries from local replicas
const replicas = await SELECT.from(Addresses).where({ contact: BuPaID })
if (replicas.length === 0) return //> not affected
// fetch changed data from S/4 -> might be less than local due to deletes
const changed = await SELECT.from(externalAddresses).where({
contact: BuPaID,
ID: replicas.map(({ ID }) => ID)
})
// update local replicas with changes from S/4
const local = db.transaction(msg) //> using that variant to benefit from bulk runs
return local.run(changed.map(a => UPDATE(Addresses, a.ID).with(a)))
})
// Validate incoming orders and reduce books' stocks.
admin.before('CREATE', 'Orders', async req => {
const { Items } = req.data
// validate input...
if (!Items || Items.length === 0) return req.reject('Please order at least one item.')
if (!req.data.shippingAddress_ID) return req.reject('Please enter a valid shipping address.', 'shippingAddress_ID')
// reduce stock on ordered books...
const all = await db.tx(req).run(
Items.map(each =>
UPDATE(Books)
.where('ID =', each.book_ID)
.and('stock >=', each.amount)
.set('stock -=', each.amount)
)
)
all.forEach(
(affectedRows, i) =>
affectedRows > 0 || req.error(409, `${Items[i].amount} exceeds stock for book #${Items[i].book_ID}`)
)
})
})
require('./utils')

View File

@@ -1,10 +1,13 @@
using { sap.capire.bookshop as my } from '../db/schema';
service CatalogService @(path:'/browse') {
@path:'/browse'
service CatalogService {
@readonly entity Books as SELECT from my.Books {*,
author.name as author
} excluding { createdBy, modifiedBy };
@requires_: 'authenticated-user'
action submitOrder (book : Books.ID, amount: Integer);
@insertonly entity Orders as projection on my.Orders;
}

View File

@@ -0,0 +1,29 @@
const cds = require('@sap/cds')
const { Books } = cds.entities
/** Service implementation for CatalogService */
module.exports = cds.service.impl(function () {
this.after('READ', 'Books', each => each.stock > 111 && _addDiscount2(each, 11))
this.before('CREATE', 'Orders', _reduceStock)
})
/** Add some discount for overstocked books */
function _addDiscount2 (each, discount) {
each.title += ` -- ${discount}% discount!`
}
/** Reduce stock of ordered books if available stock suffices */
async function _reduceStock (req) {
const { Items: OrderItems } = req.data
const all = await cds.transaction(req).run(() =>
OrderItems.map(order =>
UPDATE(Books)
.set('stock -=', order.amount)
.where('ID =', order.book_ID)
.and('stock >=', order.amount)
)
)
all.forEach((affectedRows, i) => {
if (affectedRows === 0) req.error(409, `${OrderItems[i].amount} exceeds stock for book #${OrderItems[i].book_ID}`)
})
}

View File

@@ -0,0 +1,39 @@
using { API_BUSINESS_PARTNER as external } from './external/API_BUSINESS_PARTNER.csn';
/**
* Tailor the imported API to our needs...
*/
extend service external with {
/**
* Simplified view on external addresses
*/
// @cds.persistence.skip: false
@mashup entity Addresses as projection on external.A_BusinessPartnerAddress {
key AddressID as ID,
key BusinessPartner as contact,
Country as country,
CityName as cityName,
PostalCode as postalCode,
StreetName as streetName,
HouseNumber as houseNumber
}
}
/**
* Add an entity to replicate external address data for quick access,
* e.g. when displaying lists of orders.
*/
@cds.persistence:{table,skip:false} //> create a table with the view's inferred signature
@cds.autoexpose //> auto-expose in services as targets for ValueHelps and joins
entity sap.capire.bookshop.Addresses as projection on external.Addresses;
/**
* Extend Orders with references to replicated external Addresses
*/
using { sap.capire.bookshop } from '../db/schema';
extend bookshop.Orders with {
shippingAddress : Association to bookshop.Addresses;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

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