├── .gitignore ├── .reuse └── dep5 ├── FRI.md ├── LICENSES └── Apache-2.0.txt ├── README.md ├── command-prompt-admin.png ├── exercises ├── 10 │ └── readme.md ├── 01 │ ├── command-palette.png │ ├── install-code-path.png │ ├── output.png │ ├── readme.md │ ├── run.bash │ ├── sucessfully-installed.png │ ├── vscode-extension-import.png │ └── vscode-extension.png ├── 02 │ ├── basic-odata-service.png │ ├── default-shell-windows.png │ ├── initialized-project-in-vscode.png │ ├── integrated-terminal-in-command-palette.png │ ├── integrated-terminal-in-view.png │ ├── readme.md │ └── run.bash ├── 03 │ ├── books-authors-metadata-document.png │ ├── cat-service.cds │ ├── command-completion.png │ ├── data-model.cds │ ├── navigation-properties.png │ ├── readme.md │ └── run.bash ├── 04 │ ├── books-and-authors.png │ ├── csv-files.png │ ├── my.bookshop-Authors.csv │ ├── my.bookshop-Books.csv │ ├── raw-option.png │ ├── readme.md │ └── run.bash ├── 05 │ ├── cat-service.cds │ ├── common-cds.png │ ├── data-model.cds │ ├── import-collection.png │ ├── orders-entity.png │ ├── postman-05.json │ ├── postman-collection.png │ ├── readme.md │ └── run.bash ├── 06 │ ├── postman-06.json │ ├── postman-collection-06.png │ ├── readme.md │ ├── readonly-annotations.png │ └── run.bash ├── 07 │ ├── collection-runner.png │ ├── orderinfo-entityset.png │ ├── postman-07.json │ ├── postman-collection-07.png │ ├── readme.md │ └── two-services.png ├── 08 │ ├── breakpoint-set.png │ ├── debug-buttons.png │ ├── debug-console.png │ ├── discount.png │ ├── handlers-dir.png │ ├── readme.md │ └── running-debug.png └── 09 │ ├── app-directory.png │ ├── browse-books-app.png │ ├── empty-fiori-launchpad.png │ ├── empty-table.png │ ├── launchpad-with-tile.png │ ├── readme.md │ └── title-in-browser-tab.png └── prerequisites.md /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | 8 | # Runtime data 9 | pids 10 | *.pid 11 | *.seed 12 | *.pid.lock 13 | 14 | # Directory for instrumented libs generated by jscoverage/JSCover 15 | lib-cov 16 | 17 | # Coverage directory used by tools like istanbul 18 | coverage 19 | 20 | # nyc test coverage 21 | .nyc_output 22 | 23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 24 | .grunt 25 | 26 | # Bower dependency directory (https://bower.io/) 27 | bower_components 28 | 29 | # node-waf configuration 30 | .lock-wscript 31 | 32 | # Compiled binary addons (https://nodejs.org/api/addons.html) 33 | build/Release 34 | 35 | # Dependency directories 36 | node_modules/ 37 | jspm_packages/ 38 | 39 | # TypeScript v1 declaration files 40 | typings/ 41 | 42 | # Optional npm cache directory 43 | .npm 44 | 45 | # Optional eslint cache 46 | .eslintcache 47 | 48 | # Optional REPL history 49 | .node_repl_history 50 | 51 | # Output of 'npm pack' 52 | *.tgz 53 | 54 | # Yarn Integrity file 55 | .yarn-integrity 56 | 57 | # dotenv environment variables file 58 | .env 59 | 60 | # next.js build output 61 | .next 62 | 63 | # Vim swap files 64 | *.swp 65 | 66 | # macOS stuff 67 | .DS_Store 68 | -------------------------------------------------------------------------------- /.reuse/dep5: -------------------------------------------------------------------------------- 1 | Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ 2 | Upstream-Name: cloud-cap-nodejs-codejam 3 | Upstream-Contact: DJ Adams 4 | Source: https://github.com/SAP-samples/cloud-cap-nodejs-codejam 5 | Disclaimer: The code in this project may include calls to APIs (“API Calls”) of 6 | SAP or third-party products or services developed outside of this project 7 | (“External Products”). 8 | “APIs” means application programming interfaces, as well as their respective 9 | specifications and implementing code that allows software to communicate with 10 | other software. 11 | API Calls to External Products are not licensed under the open source license 12 | that governs this project. The use of such API Calls and related External 13 | Products are subject to applicable additional agreements with the relevant 14 | provider of the External Products. In no event shall the open source license 15 | that governs this project grant any rights in or to any External Products,or 16 | alter, expand or supersede any terms of the applicable additional agreements. 17 | If you have a valid license agreement with SAP for the use of a particular SAP 18 | External Product, then you may make use of any API Calls included in this 19 | project’s code for that SAP External Product, subject to the terms of such 20 | license agreement. If you do not have a valid license agreement for the use of 21 | a particular SAP External Product, then you may only make use of any API Calls 22 | in this project for that SAP External Product for your internal, non-productive 23 | and non-commercial test and evaluation of such API Calls. Nothing herein grants 24 | you any rights to use or access any SAP External Product, or provide any third 25 | parties the right to use of access any SAP External Product, through API Calls. 26 | 27 | Files: * 28 | Copyright: 2020 SAP SE or an SAP affiliate company and cloud-cap-nodejs-codejam contributors 29 | License: Apache-2.0 30 | -------------------------------------------------------------------------------- /FRI.md: -------------------------------------------------------------------------------- 1 | # Frequently Reported Issues 2 | 3 | This document describes issues that have been encountered during previous instances of this CodeJam, with their solutions. 4 | 5 | ## Node.js related 6 | 7 | ### EACCES permissions errors 8 | 9 | Did you encounter the following error during the installation of global node module (with the `npm install -g` command)? 10 | ``` 11 | user:~/bookshop$ npm i -g @sap/cds-dk 12 | 13 | npm WARN deprecated fsevents@1.2.9: One of your dependencies needs to upgrade to fsevents v2: 1) Proper nodejs v10+ support 2) No more fetching binaries from AWS, smaller package size 14 | npm WARN checkPermissions Missing write access to /usr/local/lib/node_modules 15 | npm ERR! path /usr/local/lib/node_modules 16 | npm ERR! code EACCES 17 | npm ERR! errno -13 18 | npm ERR! syscall access 19 | npm ERR! Error: EACCES: permission denied, access '/usr/local/lib/node_modules' 20 | npm ERR! { Error: EACCES: permission denied, access '/usr/local/lib/node_modules' 21 | npm ERR! stack: 'Error: EACCES: permission denied, access \'/usr/local/lib/node_modules\'', 22 | npm ERR! errno: -13, 23 | npm ERR! code: 'EACCES', 24 | npm ERR! syscall: 'access', 25 | npm ERR! path: '/usr/local/lib/node_modules' } 26 | npm ERR! 27 | npm ERR! The operation was rejected by your operating system. 28 | npm ERR! It is likely you do not have the permissions to access this file as the current user 29 | npm ERR! 30 | npm ERR! If you believe this might be a permissions issue, please double-check the 31 | npm ERR! permissions of the file and its containing directories, or try running 32 | npm ERR! the command again as root/Administrator (though this is not recommended). 33 | 34 | npm ERR! A complete log of this run can be found in: 35 | npm ERR! /home/user/.npm/_logs/2019-11-25T12_55_47_445Z-debug.log 36 | ``` 37 | 38 | **Solution** 39 | 40 | Change npm’s default directory to solve this issue. You can find more information [here](https://docs.npmjs.com/resolving-eacces-permissions-errors-when-installing-packages-globally). 41 | 42 | ## Build related 43 | 44 | ### Python error on Windows 45 | 46 | Is your build command `mbt build -p=cf` (or `npm run build:mta`) failing, as shown below? 47 | 48 | Error log: 49 | ``` 50 | > @sap/hana-client@2.3.123 install C:\Users\user\bookshop\db\node_modules\@sap\hdi-deploy\node_modules\@sap\hana-client 51 | > node build.js 52 | 53 | ..{ Error: Command failed: node-gyp configure 54 | gyp ERR! configure error 55 | gyp ERR! stack Error: Can't find Python executable "python", you can set the PYTHON env variable. 56 | gyp ERR! stack at PythonFinder.failNoPython (C:\Program Files\nodejs\node_modules\npm\node_modules\node-gyp\lib\configure.js:484:19) 57 | gyp ERR! stack at PythonFinder. (C:\Program Files\nodejs\node_modules\npm\node_modules\node-gyp\lib\configure.js:509:16) 58 | gyp ERR! stack at C:\Program Files\nodejs\node_modules\npm\node_modules\graceful-fs\polyfills.js:282:31 59 | gyp ERR! stack at FSReqWrap.oncomplete (fs.js:153:21) 60 | gyp ERR! System Windows_NT 10.0.17763 61 | gyp ERR! command "C:\\Program Files\\nodejs\\node.exe" "C:\\Program Files\\nodejs\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" "configure" 62 | gyp ERR! cwd C:\Users\user\bookshop\db\node_modules\@sap\hdi-deploy\node_modules\@sap\hana-client 63 | gyp ERR! node -v v10.16.0 64 | gyp ERR! node-gyp -v v3.8.0 65 | gyp ERR! not ok 66 | 67 | at ChildProcess.exithandler (child_process.js:294:12) 68 | at ChildProcess.emit (events.js:198:13) 69 | at maybeClose (internal/child_process.js:982:16) 70 | at Process.ChildProcess._handle.onexit (internal/child_process.js:259:5) 71 | killed: false, 72 | code: 1, 73 | signal: null, 74 | cmd: 'node-gyp configure' } '\r\nC:\\Users\\user\\bookshop\\db\\node_modules\\@sap\\hdi-deploy\\node_modules\\@sap\\hana-client>if not defined npm_config_node_gyp (node "C:\\Program Files\\nodejs\\node_modules\\npm\\node_modules\\npm-lifecycle\\node-gyp-bin\\\\..\\..\\node_modules\\node-gyp\\bin\\node-gyp.js" configure ) else (node "C:\\Program Files\\nodejs\\node_modules\\npm\\node_modules\\node-gyp\\bin\\node-gyp.js" configure ) \r\n' 75 | npm WARN deploy@ No description 76 | npm WARN deploy@ No repository field. 77 | npm WARN deploy@ No license field. 78 | 79 | npm ERR! code ELIFECYCLE 80 | npm ERR! errno 4294967295 81 | npm ERR! @sap/hana-client@2.3.123 install: `node build.js` 82 | npm ERR! Exit status 4294967295 83 | npm ERR! 84 | npm ERR! Failed at the @sap/hana-client@2.3.123 install script. 85 | npm ERR! This is probably not a problem with npm. There is likely additional logging output above. 86 | 87 | npm ERR! A complete log of this run can be found in: 88 | npm ERR! C:\Users\user\AppData\Roaming\npm-cache\_logs\2019-06-11T14_05_28_281Z-debug.log 89 | [2019-06-11 14:05:28] ERROR build of the "bookshop-db" module failed when executing commands: execution of the "C:\Program Files\nodejs\npm.cmd" command failed while waiting for finish: exit status 4294967295 90 | make: *** [Makefile_tmp.mta:39: bookshop-db] Error 1 91 | Error: execution of the "Makefile_tmp.mta" file failed 92 | ``` 93 | 94 | **Solution** 95 | 96 | Install the [Windows build tools](https://github.com/felixrieseberg/windows-build-tools) via `npm install -g windows-build-tools`. 97 | 98 | 99 | ## Error on Windows_NT 100 | 101 | Is your build command `mbt build -p=cf` (or `npm run build:mta`) failing, as shown below (this has only been seen to occur on Azure based Windows VMs)? 102 | 103 | Error log: 104 | ``` 105 | > @sap/hana-client@2.3.123 install C:\Users\user\bookshop\db\node_modules\@sap\hdi-deploy\node_modules\@sap\hana-client 106 | > node build.js 107 | 108 | C:\Users\user\bookshop\db\node_modules\@sap\hdi-deploy\node_modules\@sap\hana-client\lib\index.js:135 109 | throw ex; 110 | ^ 111 | 112 | Error: The module '\\?\C:\Users\user\bookshop\db\node_modules\@sap\hdi-deploy\node_modules\@sap\hana-client\prebuilt\ntamd64-msvc2010\hana-client.node' 113 | was compiled against a different Node.js version using 114 | NODE_MODULE_VERSION 48. This version of Node.js requires 115 | NODE_MODULE_VERSION 64. Please try re-compiling or re-installing 116 | the module (for instance, using `npm rebuild` or `npm install`). 117 | at Object.Module._extensions..node (internal/modules/cjs/loader.js:805:18) 118 | at Module.load (internal/modules/cjs/loader.js:653:32) 119 | at tryModuleLoad (internal/modules/cjs/loader.js:593:12) 120 | at Function.Module._load (internal/modules/cjs/loader.js:585:3) 121 | at Module.require (internal/modules/cjs/loader.js:690:17) 122 | at require (internal/modules/cjs/helpers.js:25:18) 123 | at Object. (C:\Users\user\bookshop\db\node_modules\@sap\hdi-deploy\node_modules\@sap\hana-client\lib\index.js:127:14) 124 | at Module._compile (internal/modules/cjs/loader.js:776:30) 125 | at Object.Module._extensions..js (internal/modules/cjs/loader.js:787:10) 126 | at Module.load (internal/modules/cjs/loader.js:653:32) 127 | ``` 128 | 129 | **Solution** 130 | 131 | Install the dependencies of the `db` module manually and suppress the npm scripts: 132 | 133 | ```bash 134 | cd db 135 | npm install --ignore-scripts 136 | ``` 137 | -------------------------------------------------------------------------------- /LICENSES/Apache-2.0.txt: -------------------------------------------------------------------------------- 1 | Apache License 2 | 3 | Version 2.0, January 2004 4 | 5 | http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, 6 | AND DISTRIBUTION 7 | 8 | 1. Definitions. 9 | 10 | 11 | 12 | "License" shall mean the terms and conditions for use, reproduction, and distribution 13 | as defined by Sections 1 through 9 of this document. 14 | 15 | 16 | 17 | "Licensor" shall mean the copyright owner or entity authorized by the copyright 18 | owner that is granting the License. 19 | 20 | 21 | 22 | "Legal Entity" shall mean the union of the acting entity and all other entities 23 | that control, are controlled by, or are under common control with that entity. 24 | For the purposes of this definition, "control" means (i) the power, direct 25 | or indirect, to cause the direction or management of such entity, whether 26 | by contract or otherwise, or (ii) ownership of fifty percent (50%) or more 27 | of the outstanding shares, or (iii) beneficial ownership of such entity. 28 | 29 | 30 | 31 | "You" (or "Your") shall mean an individual or Legal Entity exercising permissions 32 | granted by this License. 33 | 34 | 35 | 36 | "Source" form shall mean the preferred form for making modifications, including 37 | but not limited to software source code, documentation source, and configuration 38 | files. 39 | 40 | 41 | 42 | "Object" form shall mean any form resulting from mechanical transformation 43 | or translation of a Source form, including but not limited to compiled object 44 | code, generated documentation, and conversions to other media types. 45 | 46 | 47 | 48 | "Work" shall mean the work of authorship, whether in Source or Object form, 49 | made available under the License, as indicated by a copyright notice that 50 | is included in or attached to the work (an example is provided in the Appendix 51 | below). 52 | 53 | 54 | 55 | "Derivative Works" shall mean any work, whether in Source or Object form, 56 | that is based on (or derived from) the Work and for which the editorial revisions, 57 | annotations, elaborations, or other modifications represent, as a whole, an 58 | original work of authorship. For the purposes of this License, Derivative 59 | Works shall not include works that remain separable from, or merely link (or 60 | bind by name) to the interfaces of, the Work and Derivative Works thereof. 61 | 62 | 63 | 64 | "Contribution" shall mean any work of authorship, including the original version 65 | of the Work and any modifications or additions to that Work or Derivative 66 | Works thereof, that is intentionally submitted to Licensor for inclusion in 67 | the Work by the copyright owner or by an individual or Legal Entity authorized 68 | to submit on behalf of the copyright owner. For the purposes of this definition, 69 | "submitted" means any form of electronic, verbal, or written communication 70 | sent to the Licensor or its representatives, including but not limited to 71 | communication on electronic mailing lists, source code control systems, and 72 | issue tracking systems that are managed by, or on behalf of, the Licensor 73 | for the purpose of discussing and improving the Work, but excluding communication 74 | that is conspicuously marked or otherwise designated in writing by the copyright 75 | owner as "Not a Contribution." 76 | 77 | 78 | 79 | "Contributor" shall mean Licensor and any individual or Legal Entity on behalf 80 | of whom a Contribution has been received by Licensor and subsequently incorporated 81 | within the Work. 82 | 83 | 2. Grant of Copyright License. Subject to the terms and conditions of this 84 | License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 85 | no-charge, royalty-free, irrevocable copyright license to reproduce, prepare 86 | Derivative Works of, publicly display, publicly perform, sublicense, and distribute 87 | the Work and such Derivative Works in Source or Object form. 88 | 89 | 3. Grant of Patent License. Subject to the terms and conditions of this License, 90 | each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, 91 | no-charge, royalty-free, irrevocable (except as stated in this section) patent 92 | license to make, have made, use, offer to sell, sell, import, and otherwise 93 | transfer the Work, where such license applies only to those patent claims 94 | licensable by such Contributor that are necessarily infringed by their Contribution(s) 95 | alone or by combination of their Contribution(s) with the Work to which such 96 | Contribution(s) was submitted. If You institute patent litigation against 97 | any entity (including a cross-claim or counterclaim in a lawsuit) alleging 98 | that the Work or a Contribution incorporated within the Work constitutes direct 99 | or contributory patent infringement, then any patent licenses granted to You 100 | under this License for that Work shall terminate as of the date such litigation 101 | is filed. 102 | 103 | 4. Redistribution. You may reproduce and distribute copies of the Work or 104 | Derivative Works thereof in any medium, with or without modifications, and 105 | in Source or Object form, provided that You meet the following conditions: 106 | 107 | (a) You must give any other recipients of the Work or Derivative Works a copy 108 | of this License; and 109 | 110 | (b) You must cause any modified files to carry prominent notices stating that 111 | You changed the files; and 112 | 113 | (c) You must retain, in the Source form of any Derivative Works that You distribute, 114 | all copyright, patent, trademark, and attribution notices from the Source 115 | form of the Work, excluding those notices that do not pertain to any part 116 | of the Derivative Works; and 117 | 118 | (d) If the Work includes a "NOTICE" text file as part of its distribution, 119 | then any Derivative Works that You distribute must include a readable copy 120 | of the attribution notices contained within such NOTICE file, excluding those 121 | notices that do not pertain to any part of the Derivative Works, in at least 122 | one of the following places: within a NOTICE text file distributed as part 123 | of the Derivative Works; within the Source form or documentation, if provided 124 | along with the Derivative Works; or, within a display generated by the Derivative 125 | Works, if and wherever such third-party notices normally appear. The contents 126 | of the NOTICE file are for informational purposes only and do not modify the 127 | License. You may add Your own attribution notices within Derivative Works 128 | that You distribute, alongside or as an addendum to the NOTICE text from the 129 | Work, provided that such additional attribution notices cannot be construed 130 | as modifying the License. 131 | 132 | You may add Your own copyright statement to Your modifications and may provide 133 | additional or different license terms and conditions for use, reproduction, 134 | or distribution of Your modifications, or for any such Derivative Works as 135 | a whole, provided Your use, reproduction, and distribution of the Work otherwise 136 | complies with the conditions stated in this License. 137 | 138 | 5. Submission of Contributions. Unless You explicitly state otherwise, any 139 | Contribution intentionally submitted for inclusion in the Work by You to the 140 | Licensor shall be under the terms and conditions of this License, without 141 | any additional terms or conditions. Notwithstanding the above, nothing herein 142 | shall supersede or modify the terms of any separate license agreement you 143 | may have executed with Licensor regarding such Contributions. 144 | 145 | 6. Trademarks. This License does not grant permission to use the trade names, 146 | trademarks, service marks, or product names of the Licensor, except as required 147 | for reasonable and customary use in describing the origin of the Work and 148 | reproducing the content of the NOTICE file. 149 | 150 | 7. Disclaimer of Warranty. Unless required by applicable law or agreed to 151 | in writing, Licensor provides the Work (and each Contributor provides its 152 | Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 153 | KIND, either express or implied, including, without limitation, any warranties 154 | or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR 155 | A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness 156 | of using or redistributing the Work and assume any risks associated with Your 157 | exercise of permissions under this License. 158 | 159 | 8. Limitation of Liability. In no event and under no legal theory, whether 160 | in tort (including negligence), contract, or otherwise, unless required by 161 | applicable law (such as deliberate and grossly negligent acts) or agreed to 162 | in writing, shall any Contributor be liable to You for damages, including 163 | any direct, indirect, special, incidental, or consequential damages of any 164 | character arising as a result of this License or out of the use or inability 165 | to use the Work (including but not limited to damages for loss of goodwill, 166 | work stoppage, computer failure or malfunction, or any and all other commercial 167 | damages or losses), even if such Contributor has been advised of the possibility 168 | of such damages. 169 | 170 | 9. Accepting Warranty or Additional Liability. While redistributing the Work 171 | or Derivative Works thereof, You may choose to offer, and charge a fee for, 172 | acceptance of support, warranty, indemnity, or other liability obligations 173 | and/or rights consistent with this License. However, in accepting such obligations, 174 | You may act only on Your own behalf and on Your sole responsibility, not on 175 | behalf of any other Contributor, and only if You agree to indemnify, defend, 176 | and hold each Contributor harmless for any liability incurred by, or claims 177 | asserted against, such Contributor by reason of your accepting any such warranty 178 | or additional liability. END OF TERMS AND CONDITIONS 179 | 180 | APPENDIX: How to apply the Apache License to your work. 181 | 182 | To apply the Apache License to your work, attach the following boilerplate 183 | notice, with the fields enclosed by brackets "[]" replaced with your own identifying 184 | information. (Don't include the brackets!) The text should be enclosed in 185 | the appropriate comment syntax for the file format. We also recommend that 186 | a file or class name and description of purpose be included on the same "printed 187 | page" as the copyright notice for easier identification within third-party 188 | archives. 189 | 190 | Copyright [yyyy] [name of copyright owner] 191 | 192 | Licensed under the Apache License, Version 2.0 (the "License"); 193 | 194 | you may not use this file except in compliance with the License. 195 | 196 | You may obtain a copy of the License at 197 | 198 | http://www.apache.org/licenses/LICENSE-2.0 199 | 200 | Unless required by applicable law or agreed to in writing, software 201 | 202 | distributed under the License is distributed on an "AS IS" BASIS, 203 | 204 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 205 | 206 | See the License for the specific language governing permissions and 207 | 208 | limitations under the License. 209 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![](https://img.shields.io/badge/STATUS-NOT%20CURRENTLY%20MAINTAINED-red.svg?longCache=true&style=flat) 2 | 3 | # Important Notice 4 | This public repository is read-only and no longer maintained. For the latest sample code repositories, visit the [SAP Samples](https://github.com/SAP-samples) organization. 5 | 6 | # CodeJam - SAP Cloud Application Programming Model - Node.js 7 | 8 | [![REUSE status](https://api.reuse.software/badge/github.com/SAP-samples/cloud-cap-nodejs-codejam)](https://api.reuse.software/info/github.com/SAP-samples/cloud-cap-nodejs-codejam) 9 | 10 | ## Description 11 | 12 | This repository contains the material for the CodeJam on SAP Cloud Application Programming Model with Node.js. Prerequisites and recommendations for this CodeJam are documented in the [prerequisites](prerequisites.md) file. 13 | 14 | ### CodeJam overview 15 | 16 | The focus of this CodeJam is the SAP Cloud Application Programming Model ("CAP" for short) with Node.js. Through a series of developer focused hands-on exercises you'll get to know the CAP related tools, create a CAP project and learn about various aspects such as compilation, persistence, OData operations, data models & service definitions and how they relate, and dig into annotations, custom logic and debugging. You'll also add a user interface (UI) layer, and see how the entire set of artifacts can be deployed to the SAP Cloud Platform. 17 | 18 | ### Material organization 19 | 20 | The material consists of a series of exercises that are to be done in order (each one building on the previous one). Each exercise is contained in a directory, with a main 'readme' file containing the core exercise instructions, with optional supporting files, such as screenshots and sample files. 21 | 22 | ### Following the exercises 23 | 24 | Except for some of the content in exercise 09, we strongly recommend you avoid copy/pasting content - there isn't much to type in all, and learning works better when it starts with osmosis through the fingertips! Everything you need to enter will be described in each of the exercise 'readme' files. 25 | 26 | At the end of each exercise there are questions. These questions are designed to help you think about the content just covered, and are to be discussed with the entire CodeJam class, at the end of each exercise. If you finish an exercise early, please resist the temptation to continue with the next one. Instead, explore what you've just done and see if you can find out more about the subject that was covered. That way we all stay on track together and can benefit from some reflection via the questions (and answers). 27 | 28 | :point_right: Where there's an action for you to perform, it will be prefixed with this pointing symbol, to help you focus on where you are in each exercise. 29 | 30 | ### The exercises 31 | 32 | Here's an overview of the exercises in this CodeJam. 33 | 34 | - [Exercise 01 - Installing the CAP related tools](exercises/01/) 35 | - [Exercise 02 - Creating a new CAP project](exercises/02/) 36 | - [Exercise 03 - Enhancing the project & adding persistence](exercises/03/) 37 | - [Exercise 04 - Adding data & testing OData operations](exercises/04) 38 | - [Exercise 05 - Adding a further entity, using common definition features](exercises/05) 39 | - [Exercise 06 - Enhancing the service with annotations](exercises/06/) 40 | - [Exercise 07 - Defining a second service](exercises/07/) 41 | - [Exercise 08 - Adding custom logic, and debugging](exercises/08/) 42 | - [Exercise 09 - Introducing an app at the UI layer](exercises/09/) 43 | - [Exercise 10 - Deploy the app to Cloud Foundry](exercises/10/) 44 | 45 | ### Feedback 46 | 47 | At the end of this CodeJam, we would be really grateful if you could spend a minute providing us with your thoughts in this [feedback form](https://bit.ly/codejam-cap-nodejs-feedback). Thank you. 48 | 49 | 50 | ## Requirements 51 | 52 | The requirements to follow the exercises in this repository, including hardware and software, are detailed in the [prerequisites](prerequisites.md) file. 53 | 54 | ## FAQ 55 | 56 | In case you encounter problems, you may find answers in the Frequently Reported Issues document ([FRI](FRI.md)). 57 | 58 | 59 | ## Download and installation 60 | 61 | You do not need to download this repository nor install anything from it. You can just follow the exercises by visiting each of them as listed in the [exercises](#the-exercises) section. 62 | 63 | 64 | ## How to obtain support 65 | 66 | Support for the content in this repository is available during CodeJam events, for which this content has been designed. Otherwise, this content is provided "as-is" with no other support. 67 | 68 | 69 | ## Contributing 70 | 71 | If you wish to contribute code, offer fixes or improvements, please send a pull request (PR). Due to legal reasons, contributors will be asked to accept a [Developer Certificate of Origin (DCO)](https://en.wikipedia.org/wiki/Developer_Certificate_of_Origin) on submitting their first PR to this project. This DCO acceptance can be done in the PR itself - look out for the CLA assistant that will guide you through the simple process. SAP uses [the standard DCO text of the Linux Foundation](https://developercertificate.org/). 72 | 73 | ## License 74 | 75 | Copyright (c) 2020 SAP SE or an SAP affiliate company. All rights reserved. This project is licensed under the Apache Software License version 2.0. 76 | -------------------------------------------------------------------------------- /command-prompt-admin.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/command-prompt-admin.png -------------------------------------------------------------------------------- /exercises/01/command-palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/01/command-palette.png -------------------------------------------------------------------------------- /exercises/01/install-code-path.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/01/install-code-path.png -------------------------------------------------------------------------------- /exercises/01/output.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/01/output.png -------------------------------------------------------------------------------- /exercises/01/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 01 - Installing the CAP related tools 2 | 3 | In this exercise you'll install all the tools required to work comfortably with the Node.js flavor of CAP in a local environment. 4 | 5 | Successfully completing this exercise relies on the hardware and software [prerequisites](../../prerequisites.md) published for this CodeJam. These prerequisites describe the requirement for some software preinstalled; this includes VS Code and Node.js. This exercise will focus on tools relating to both of these. (If you haven't already installed Node.js, we recommend version 10 (LTS)). 6 | 7 | ## Steps 8 | 9 | After completing these steps you'll have a working local environment for development of CAP based projects with Node.js, using the CAP "development kit" package `@sap/cds-dk`. 10 | 11 | ### 1. Install the CDS command line tool 12 | 13 | The CDS command line tool is the heart of everything you do when developing CAP services locally. It is Node.js based and comes in the form of an NPM package (which further depends on other packages). The package and its dependencies are in the `@sap` namespace and are available from the [SAP NPM registry](https://blogs.sap.com/2017/05/16/sap-npm-registry-launched-making-the-lives-of-node.js-developers-easier/). 14 | 15 | :point_right: First, delete the reation of the `@sap` namespace to the SAP NPM registry (It's not updated anymore since June 2020): 16 | 17 | ```sh 18 | user@host:~ 19 | => npm config delete "@sap:registry" 20 | ``` 21 | 22 | :point_right: Check that this setting is now saved in your configuration, with: 23 | 24 | ```sh 25 | user@host:~ 26 | => npm config ls 27 | ``` 28 | 29 | You should see output similar to this: 30 | 31 | ```sh 32 | ; cli configs 33 | metrics-registry = "https://registry.npmjs.org/" 34 | scope = "" 35 | user-agent = "npm/6.4.1 node/v10.15.3 linux x64" 36 | 37 | ; userconfig /user/.npmrc 38 | @sap:registry = "https://npm.sap.com" 39 | 40 | ; node bin location = /user/.nvm/versions/node/v10.15.3/bin/node 41 | ; cwd = / 42 | ; HOME = /user 43 | ; "npm config ls -l" to show all defaults. 44 | ``` 45 | 46 | :point_right: Next, explore the information about the `@sap/cds-dk` package, including its dependencies, with: 47 | 48 | ```sh 49 | user@host:~ 50 | => npm info @sap/cds-dk 51 | ``` 52 | 53 | This will show you that it has dependencies on other `@sap` namespaced packages, and also that there is a 'binary' (in other words an executable) called `cds` that's delivered as part of the package. 54 | 55 | :point_right: Now, install the `@sap/cds-dk` package globally: 56 | 57 | ```sh 58 | user@host:~ 59 | => npm install --global @sap/cds-dk 60 | ``` 61 | 62 | This produces a fair amount of output as it works, but should eventually end with a couple of lines similar to this: 63 | 64 | ```sh 65 | + @sap/cds-dk@1.4.2 66 | added 471 packages from 381 contributors in 33.398s 67 | ``` 68 | 69 | Here you can see that the version of the `@sap/cds-dk` package just installed is 1.4.2. It may be that the version of `@sap/cds-dk` that is installed when you do this exercise will be different (newer). 70 | 71 | ### 2. Install the CDS extension for VS Code 72 | 73 | To efficiently and comfortably develop CAP based services with CDS, there is an extension for the [VS Code](https://code.visualstudio.com/) IDE. This is available to download from the [SAP Development Tools](https://tools.hana.ondemand.com/) website. 74 | 75 | Extensions can be installed directly in VS Code from the extension marketplace, or manually from a file. In this case the extension for CDS language support is available in the form of a file. 76 | 77 | :point_right: Go to the [Cloud section of the SAP Development Tools website](https://tools.hana.ondemand.com/#cloud) and find the "CDS Language Support for Visual Studio Code" section. Follow the instructions there to download and subsequently install the extension. 78 | 79 | To install the downloaded extension in VS Code have a look at the screenshot below: 80 | 81 | ![CDS Extension installation in VS Code](vscode-extension-import.png) 82 | 83 | When successfully installed, you should see the extension thus (again, the version number may be different): 84 | 85 | ![CDS Language Support extension installed in VS Code](vscode-extension.png) 86 | 87 | > **For macOS users only:** To be able to open VS Code from the command line (which you will be doing in a subsequent exercise) you need to add the installation path of VS Code to the environment variable PATH. There's an option to do this via the `Command Palette` in VS Code. 88 | 89 | > Open the `Command Palette` with ⇧⌘P. You can also open it via the menu bar: View -> Command Palette. 90 | 91 | > ![Command Palette navigation in the menu bar](command-palette.png) 92 | 93 | > Search for `code` and press Enter. 94 | 95 | > ![search for code in the command palette](install-code-path.png) 96 | 97 | > A success message for the process should then appear at the bottom right of the screen. 98 | 99 | > ![message that the 'code' command was successfully installed](sucessfully-installed.png) 100 | 101 | ### 3. Verify your development environment 102 | 103 | :point_right: Run the following command from the terminal to check whether all required tools are installed (this is an experimental tool, please contact the instructor if you have any questions): 104 | 105 | ```sh 106 | npx check-sap-cloud-readiness -codejam-cap 107 | ``` 108 | 109 | You should see an output similar to this: 110 | ![out](output.png) 111 | 112 | If necessary, double check the [prerequisites](../../prerequisites.md) to install the missing tools. Please contact the instructor of this CodeJam if you need help. 113 | 114 | 115 | ## Summary 116 | 117 | You've now installed the key tools for developing with CAP locally, and are all set to create your first project. 118 | 119 | ## Questions 120 | 121 | 1. What are the benefits of using NPM here? 122 | 123 | 124 | 2. What are the `@sap` namespaced packages upon which `@sap/cds-dk` depends? 125 | 126 | 127 | 3. What is the significance of using the `--global` option when installing the `@sap/cds-dk` package? 128 | 129 | 130 | 4. What is the meaning of the `.vsix` file type for the VS Code extension? Can we dig into that to see what's inside? 131 | 132 | -------------------------------------------------------------------------------- /exercises/01/run.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/SAP-samples/cloud-cap-nodejs-codejam/tree/master/exercises/01 4 | 5 | echo EXERCISE 01 6 | 7 | echo 1. Install the CDS command line tool 8 | npm set @sap:registry=https://npm.sap.com 9 | npm install --global @sap/cds-dk 10 | 11 | echo 2. Install the CDS extension for VS Code \(NOP\) 12 | 13 | echo 3. Verify your development environment \(NOP\) 14 | 15 | -------------------------------------------------------------------------------- /exercises/01/sucessfully-installed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/01/sucessfully-installed.png -------------------------------------------------------------------------------- /exercises/01/vscode-extension-import.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/01/vscode-extension-import.png -------------------------------------------------------------------------------- /exercises/01/vscode-extension.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/01/vscode-extension.png -------------------------------------------------------------------------------- /exercises/02/basic-odata-service.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/02/basic-odata-service.png -------------------------------------------------------------------------------- /exercises/02/default-shell-windows.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/02/default-shell-windows.png -------------------------------------------------------------------------------- /exercises/02/initialized-project-in-vscode.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/02/initialized-project-in-vscode.png -------------------------------------------------------------------------------- /exercises/02/integrated-terminal-in-command-palette.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/02/integrated-terminal-in-command-palette.png -------------------------------------------------------------------------------- /exercises/02/integrated-terminal-in-view.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/02/integrated-terminal-in-view.png -------------------------------------------------------------------------------- /exercises/02/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 02 - Creating a new CAP project 2 | 3 | In this exercise you'll become familiar with the workflow of creating a new CAP project from the command line, and discover how quickly you can get to a basic OData service serving a simple metadata document. 4 | 5 | One of the challenges of building a full stack app using OData has been the production of an OData service - the definition of the metadata and the creation of the backend components to service the OData operations. 6 | 7 | With Core Data & Services (CDS) as the definition language and CAP as the framework providing out-of-the-box services that respond to OData requests, that challenge has disappeared. It's very easy to get a basic OData service up and running in only a few minutes. Being able to rapidly get to a working metadata document has various benefits which we'll discuss at the end of this exercise. 8 | 9 | 10 | ## Steps 11 | 12 | After completing these steps you'll be familiar with how you can use the `cds` command line tool to initialize a project with an OData service. 13 | 14 | 15 | ### 1. Initialize a new CAP project 16 | 17 | > Before you start creating a new project we recommend that you create a dedicated directory for this CodeJam within your home directory instead of any system directories (e.g. DO NOT work in `C:\Windows\System32\`). 18 | 19 | For any new CAP based project you start by indirectly creating a directory containing various basic files. This can be achieved with the CDS command line tool `cds` which you installed in [exercise 01](../01/). 20 | 21 | The `cds` tool should be available in your executable path, having been installed globally as part of the Node.js `@sap/cds-dk` package. 22 | 23 | ```sh 24 | user@host:~ 25 | => cds 26 | 27 | USAGE 28 | 29 | cds [] 30 | cds = cds compile 31 | cds = cds help 32 | 33 | COMMANDS 34 | 35 | i | init jump-start cds-based projects 36 | c | compile process models selectively 37 | d | deploy e.g. to databases or cloud 38 | s | serve run servers locally 39 | w | watch restart server on file changes 40 | m | import add models from external sources 41 | r | repl read-eval-event loop 42 | e | env get/set cds configuration 43 | b | build prepare for deployment 44 | v | version get detailed version information 45 | ? | help get detailed usage information 46 | 47 | Learn more about each with: 48 | cds help or 49 | cds ? 50 | ``` 51 | 52 | Note that with `cds init` a new project can be quickly initialized. 53 | 54 | :point_right: Explore what options are available with `cds init` with the `--help` option: 55 | 56 | ```sh 57 | user@host:~ 58 | => cds init --help 59 | ``` 60 | 61 | :point_right: Use some of these options to initialize a new project directory called `bookshop` thus: 62 | 63 | ```sh 64 | user@host:~ 65 | => cds init bookshop --add hana,mta 66 | ``` 67 | 68 | You should see output that looks similar to this: 69 | 70 | ``` 71 | [cds] - creating new project in current folder 72 | > applying template 'hana'... 73 | > applying template 'mta'... 74 | done. 75 | 76 | Find samples on https://github.com/SAP-samples/cloud-cap-samples 77 | Learn about next steps at https://cap.cloud.sap/docs/get-started 78 | ``` 79 | 80 | ### 2. Open the project in VS Code 81 | 82 | Now that the project has been initialized, it's time to explore it. The VS Code IDE is a comfortable environment in which to do so, so at this point you will open up the newly created `bookshop` directory in it. 83 | 84 | :point_right: Open up the new `bookshop` directory in VS Code. One way to do this (if the installation of VS Code on your operating system put the binary in your executable path) is simply by invoking `code` on the command line, and specifying the directory to open: 85 | 86 | ```sh 87 | user@host:~ 88 | => code bookshop 89 | ``` 90 | 91 | > If this approach is not available to you, simply start VS Code through your operating system's GUI and open the directory manually (with menu path "File → Open"). 92 | 93 | 94 | ### 3. Explore the initialized project structure 95 | 96 | The skeleton project that has been initialized is visible in VS Code. This is what it should look like. 97 | 98 | ![initialized project in VS Code](initialized-project-in-vscode.png) 99 | 100 | :point_right: Examine what files have been populated in the project directory structure. 101 | 102 | Briefly, the directories and contents can be described thus: 103 | 104 | | Directory or File | Description | 105 | | -------------- | -------- | 106 | | `.vscode/` | VS Code specific files for launch configurations (useful for debugging, which we will cover in [exercise 08](../08/)). This directory may or may not be visible in VS Code, depending on version and settings. | 107 | | `app/` | Where any UI components live, in case you're building and serving a full stack app. | 108 | | `db/` | Where the data models (in CDS) are specified. | 109 | | `srv/` | Where the service definitions (in CDS) are specified. | 110 | | `.eslintrc` | Configuration for linting of the JavaScript with [ESLint](https://eslint.org/). | 111 | | `.gitignore` | Specification of what content should be ignored when using `git` for source code control, specifically for CAP-based projects. | 112 | | `mta.yaml` | This is the central descriptor file for the project. It defines all modules (microservices) and backing services (like databases). This information will be used to build the .mtar archive during design time and to deploy & provision the apps and services during deploy time. | 113 | | `package.json` | The standard package descriptor file for Node.js based (NPM) packages and projects. | 114 | | README.md | A small 'Getting Started' guide for this project. | 115 | 116 | 117 | ### 4. Create a simple data model and service definition 118 | 119 | :point_right: Create a new file called `schema.cds` in the `db/` directory of the recently created project, copy the following lines to the file and save it: 120 | 121 | ```cds: 122 | namespace my.bookshop; 123 | 124 | entity Books { 125 | key ID : Integer; 126 | title : String; 127 | stock : Integer; 128 | } 129 | ``` 130 | 131 | > You **may** wish to use VS Code's "auto save" feature, then again you may not. Just in case you do, you can turn it on via the **File -> Auto Save** menu option. 132 | 133 | :point_right: Create a new file called `service.cds` in the `srv/` directory of the recently created project, copy the following lines to the file and save it: 134 | 135 | ```cds: 136 | using my.bookshop as my from '../db/schema'; 137 | 138 | service CatalogService { 139 | entity Books as projection on my.Books; 140 | } 141 | ```` 142 | 143 | You have now created a simple data model as well as a service definition for your project. 144 | 145 | ### 5. Examine the data model and service definition 146 | 147 | The key files in this project as far as the business domain is concerned are the `db/schema.cds` and the `srv/service.cds` files that you just added. 148 | 149 | :point_right: Have a brief look at the content of each of these files to get a basic understanding of what's there. Note the use of the `namespace` and how it is defined in the data model and referenced in the service definition. Note also the how the different parts of each file are syntax highlighted. 150 | 151 | 152 | ### 6. Install the dependencies 153 | 154 | The `package.json` file that was created when you initialized the project directory contains a list of NPM packages upon which the project is dependent. Before we can start the service up, these dependencies must be installed. Now is a good time to do that. 155 | 156 | > VS Code has an integrated terminal which you can and should use for this and subsequent command line activities. 157 | 158 | :point_right: Open the integrated terminal in VS Code. Do this by opening the Command Palette and searching for 'integrated terminal'. You may wish to use the keyboard shortcut for this - note there is a keyboard shortcut for toggling the integrated terminal in and out of view as well. 159 | 160 | ![the integrated terminal options in the Command Palette](integrated-terminal-in-command-palette.png) 161 | 162 | This should open up the terminal at the bottom of VS Code like this: 163 | 164 | ![integrated terminal in view](integrated-terminal-in-view.png) 165 | 166 | > **Windows users:** Please make sure to select `cmd` as your default shell before you continue: 167 | ![default shell](default-shell-windows.png) 168 | 169 | :point_right: In the integrated terminal, making sure first that you're in the project directory itself (where `package.json` is to be found), install the dependencies like this: 170 | 171 | ```sh 172 | user@host:~/bookshop 173 | => npm install 174 | ``` 175 | 176 | You should see output that ends something like this: 177 | 178 | ```sh 179 | npm notice created a lockfile as package-lock.json. You should commit this file. 180 | added 129 packages from 181 contributors and audited 258 packages in 4.762s 181 | found 0 vulnerabilities 182 | ``` 183 | 184 | Great. Now your fledgling service can already be started up! 185 | 186 | ### 7. Start up the service 187 | 188 | :point_right: In the same integrated terminal, use the following command to start up the service: 189 | 190 | 191 | ```sh 192 | user@host:~/bookshop 193 | => npm start 194 | ``` 195 | 196 | You should see output similar to this: 197 | 198 | ``` 199 | > bookshop@1.0.0 start /tmp/codejam/bookshop 200 | > npx cds run 201 | 202 | [cds] - connect to datasource - hana:undefined 203 | [cds] - serving CatalogService at /catalog 204 | [cds] - service definitions loaded from: 205 | 206 | srv/service.cds 207 | db/schema.cds 208 | 209 | [cds] - launched in: 583.297ms 210 | [cds] - server listening on http://localhost:4004 ... 211 | [ terminate with ^C ] 212 | ``` 213 | 214 | The OData service is now running, and available via [http://localhost:4004](http://localhost:4004). 215 | 216 | 217 | ### 8. Explore the OData service 218 | 219 | While we have no data in the OData service (we don't even have a persistence layer yet!) we can ask the OData service for the two well-known documents: the service document and the metadata document. 220 | 221 | :point_right: Open [http://localhost:4004](http://localhost:4004) in your browser, to see something like this: 222 | 223 | ![basic OData service](basic-odata-service.png) 224 | 225 | The [catalog](http://localhost:4004/catalog) link will take you to the service document and the [$metadata](http://localhost:4004/catalog/$metadata) link will take you to the metadata document. 226 | 227 | :point_right: Explore the metadata document and familiarize yourself with the content. Note the entityset definition and the entity type describing the `Books` entity. 228 | 229 | :point_right: There is also a link to the [Books](http://localhost:4004/catalog/Books) entityset. Follow this link to see what the service returns. Check what happens to the Node.js `cds` process when you try to access the entityset. 230 | 231 | 232 | ## Summary 233 | 234 | With a single command, you've initialized a basic OData service project and with another command you've started it up and explored the metadata. 235 | 236 | 237 | ## Questions 238 | 239 | 1. Why is there a focus on "Contracts First" (As all you need to run a service is a service definition) - what advantages does that bring? 240 | 241 | 242 | 2. What is the difference between the data model and the service definition? Why do we need both? 243 | 244 | 245 | 3. What is returned in response to a request for the Books entityset resource right now? 246 | 247 | 248 | 4. What happened to the `cds` process when you accessed the entityset? Can you think of reasons why this happened? 249 | 250 | 251 | 5. What actually happens when you run `npm start`, and why? 252 | 253 | -------------------------------------------------------------------------------- /exercises/02/run.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/SAP-samples/cloud-cap-nodejs-codejam/tree/master/exercises/02 4 | 5 | echo EXERCISE 02 6 | 7 | echo 1. Initialize a new CAP project 8 | cds init bookshop --add hana,mta && cd bookshop 9 | 10 | echo 2. Open the project in VS Code \(NOP\) 11 | 12 | echo 3. Explore the initialized project structure \(NOP\) 13 | 14 | echo 4. Create a simple data model and service definition 15 | cat < db/schema.cds 16 | namespace my.bookshop; 17 | 18 | entity Books { 19 | key ID : Integer; 20 | title : String; 21 | stock : Integer; 22 | } 23 | EOSCHEMA 24 | 25 | cat < srv/service.cds 26 | using my.bookshop as my from '../db/schema'; 27 | 28 | service CatalogService { 29 | entity Books as projection on my.Books; 30 | } 31 | EOSERVICE 32 | 33 | echo 5. Examine the data model and service definition \(NOP\) 34 | 35 | echo 6. Install the dependencies 36 | npm install 37 | 38 | echo 7. Start up the service \(NOP\) 39 | 40 | echo 8. Explore the OData service \(NOP\) 41 | -------------------------------------------------------------------------------- /exercises/03/books-authors-metadata-document.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/03/books-authors-metadata-document.png -------------------------------------------------------------------------------- /exercises/03/cat-service.cds: -------------------------------------------------------------------------------- 1 | using my.bookshop as my from '../db/data-model'; 2 | 3 | service CatalogService { 4 | entity Books as projection on my.Books; 5 | entity Authors as projection on my.Authors; 6 | } 7 | -------------------------------------------------------------------------------- /exercises/03/command-completion.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/03/command-completion.png -------------------------------------------------------------------------------- /exercises/03/data-model.cds: -------------------------------------------------------------------------------- 1 | namespace my.bookshop; 2 | 3 | entity Books { 4 | key ID : Integer; 5 | title : String; 6 | stock : Integer; 7 | author : Association to Authors; 8 | } 9 | 10 | entity Authors { 11 | key ID : Integer; 12 | name : String; 13 | books : Association to many Books on books.author = $self; 14 | } 15 | -------------------------------------------------------------------------------- /exercises/03/navigation-properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/03/navigation-properties.png -------------------------------------------------------------------------------- /exercises/03/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 03 - Enhancing the project & adding persistence 2 | 3 | In this exercise you'll enhance your basic bookshop project by adding to the data model and service definition, and creating a persistence layer. 4 | 5 | 6 | ## Steps 7 | 8 | After completing these steps you'll have a slightly more complex OData service, with a second entity that is related to the first. It will also be backed by an actual persistence layer, provided by SQLite. 9 | 10 | ### 1. Use `cds watch` to have the service restart on changes 11 | 12 | During the course of this CodeJam you'll be making many changes and additions to CDS and JavaScript sources. Rather than restart the service manually each time, you can use the `watch` command with the `cds` command line tool, that will restart the server on file changes. 13 | 14 | :point_right: Do this now, by entering `cds watch` in the integrated terminal (if you still have the service running, terminate it first with Ctrl-C): 15 | 16 | ```sh 17 | user@host:~/bookshop 18 | => cds watch 19 | ``` 20 | 21 | The `watch` command uses the NPM [nodemon](https://www.npmjs.com/package/nodemon) package, which you can see from the output that it produces, which should look something like this: 22 | 23 | ``` 24 | [cds] - running nodemon... 25 | --exec cds run --with-mocks --in-memory? 26 | --ext cds,csn,csv,ts,mjs,cjs,js,json,properties,edmx,xml 27 | 28 | [cds] - connect to datasource - hana:undefined 29 | [cds] - serving CatalogService at /catalog 30 | [cds] - launched in: 555.266ms 31 | [cds] - server listening on http://localhost:4004 ... 32 | [ terminate with ^C ] 33 | ``` 34 | 35 | You can now proceed with saving changes to your project and have the service automatically restarted. Nice! 36 | 37 | ### 2. Add a new Authors entity to the model 38 | 39 | Currently the data model is extremely simple. In this step you'll add a second entity `Authors`. 40 | 41 | :point_right: Open the `db/schema.cds` file in VS Code and add a new entity definition, after the `Books` entity, thus: 42 | 43 | ```cds 44 | entity Authors { 45 | key ID : Integer; 46 | name : String; 47 | } 48 | ``` 49 | 50 | This is deliberately very simple at this point. Don't forget to save the file ... at which point your service should restart automatically thanks to `cds watch`. 51 | 52 | :point_right: Open up (or refresh) the [service metadata document](http://localhost:4004/catalog/$metadata) and check for the Authors entity definition you've just added. 53 | 54 | You're right. It's not there. 55 | 56 | 57 | ### 3. Expose the Authors entity in the service 58 | 59 | While there is now a second entity definition in the data model, it is not exposed in the existing service. In this step, you'll remedy that. 60 | 61 | :point_right: Open up the `srv/service.cds` file and add a second entity to the `CatalogService` definition. 62 | 63 | This is what the contents of `srv/service.cds` should look like after you've added the new entity and removed the annotation: 64 | 65 | ```cds 66 | using my.bookshop as my from '../db/schema'; 67 | 68 | service CatalogService { 69 | entity Books as projection on my.Books; 70 | entity Authors as projection on my.Authors; 71 | } 72 | ``` 73 | 74 | :point_right: After the service restarts, check the metadata document once again. The definition of the Authors entity should now be present in the metadata, and will look something like this: 75 | 76 | ![Books and Authors entities in the metadata document](books-authors-metadata-document.png) 77 | 78 | This is nice, but there's something fundamental that's missing and preventing this data model from being useful. 79 | 80 | 81 | ### 4. Add a relationship between the Books and Authors entities 82 | 83 | The `Books` and `Authors` entities are standalone and currently are not related to each other. This is not ideal, so in this step you'll fix that by adding a relationship in the form of an [association](https://cap.cloud.sap/docs/cds/cdl#associations). 84 | 85 | :point_right: Return to the `db/schema.cds` file and add an association from the `Books` entity to the `Authors` entity, bearing in mind the simplified assumption that a book has a single author. The association should describe a new `author` property in the `Books` entity like this: 86 | 87 | ```cds 88 | entity Books { 89 | key ID : Integer; 90 | title : String; 91 | stock : Integer; 92 | author : Association to Authors; 93 | } 94 | ``` 95 | 96 | Note that as you type, the CDS Language Services extension for VS Code that you installed in [exercise 01](../01/) provides very useful command completion, recognising the entities defined as well as the CDS syntax itself: 97 | 98 | ![command completion](command-completion.png) 99 | 100 | This `Association to Authors` relationship will allow a consumer to navigate from a book to the related author, but not from an author to their books. Let's fix that now by adding a second association. 101 | 102 | :point_right: To the `Authors` entity, add a `books` property thus: 103 | 104 | ```cds 105 | entity Authors { 106 | key ID : Integer; 107 | name : String; 108 | books : Association to many Books on books.author = $self; 109 | } 110 | ``` 111 | 112 | Note that this is a 'to-many' relationship. 113 | 114 | Don't forget to save the file. 115 | 116 | :point_right: After the service has restarted, check the [metadata document](http://localhost:4004/catalog/$metadata) again. There should now be OData navigation properties defined between the two entities, like this: 117 | 118 | ![navigation properties](navigation-properties.png) 119 | 120 | 121 | ### 5. Deploy the service to a persistence layer 122 | 123 | As it stands, the OData service has no storage. We can actually simulate storage with [service provider](https://cap.cloud.sap/docs/guides/service-impl) logic in JavaScript but that's not a path we want to explore right now (we'll look at it in [exercise 08](../08/)). Instead, we'll use a real database in the form of [SQLite](https://sqlite.org) and deploy the data model and service definition to it. 124 | 125 | As we want to use a local SQLite database (SQLite was defined in the [prerequisites](../../prerequisites.md) for this CodeJam), we need to install a client library to allow the CAP engine to communicate with this DB. 126 | 127 | :point_right: Do that now, i.e. install the `sqlite3` package for this purpose: 128 | 129 | ```sh 130 | user@host:~/bookshop 131 | => npm install -D sqlite3 132 | ``` 133 | 134 | > The use of the `-D` (or `--save-dev`) parameter signifies that the `sqlite3` package is a dependency for development purposes only. Have a look at what gets added to `package.json` at this point to see the two different types of package dependencies. 135 | 136 | Now it's time to make that deployment to the persistence layer. 137 | 138 | :point_right: Explore the options for the deploy command like this: 139 | 140 | ```sh 141 | user@host:~/bookshop 142 | => cds deploy --help 143 | 144 | SYNOPSIS 145 | 146 | cds deploy [ ] [ --to ] 147 | 148 | Deploys the given model to a database. If no model is given it looks up 149 | according configuration from package.json or .cdsrc.json in key 150 | cds.requires.db. Same for the database. 151 | 152 | Supported databases: sqlite, hana 153 | 154 | [...] 155 | ``` 156 | 157 | Use this command to deploy the data model and service definition to a new SQLite-based database (databases with SQLite are simply files on the local filesystem). 158 | 159 | :point_right: Deploy to a new SQLite database like this (remember, you need to be inside the `bookshop` project directory when you invoke this): 160 | 161 | ``` 162 | user@host:~/bookshop 163 | => cds deploy --to sqlite:bookshop.db 164 | ``` 165 | 166 | This should complete fairly quietly, something like this: 167 | 168 | ``` 169 | /> successfully deployed to ./bookshop.db 170 | > updated ./package.json 171 | ``` 172 | 173 | ### 6. Explore the new database 174 | 175 | At this point you should have a new file `bookshop.db` in the project directory. 176 | 177 | :point_right: Have a look inside it with the `sqlite3` command line utility; use the `.tables` command to see what has been created: 178 | 179 | ```sh 180 | user@host:~/bookshop 181 | => sqlite3 bookshop.db 182 | SQLite version 3.22.0 2018-01-22 18:45:57 183 | Enter ".help" for usage hints. 184 | sqlite> .tables 185 | CatalogService_Authors my_bookshop_Authors 186 | CatalogService_Books my_bookshop_Books 187 | sqlite> .quit 188 | user@host:~/bookshop 189 | ``` 190 | 191 | > The `sqlite3` command line utility is not directly related to the `sqlite3` NPM package you just installed; it came from the installation of SQLite itself. 192 | 193 | 194 | ### 7. Dig into the link between the CDS definitions and the artefacts in the database 195 | 196 | Looking at the tables in the `bookshop.db` database we see that there are two pairs of names; one pair prefixed with `CatalogService` and the other pair prefixed with `my_bookshop`. If you guessed that the `CatalogService`-prefixed artefacts relate to the service definition and the `my_bookshop`-prefixed artefacts relate to the data model, you are correct. 197 | 198 | In this step you'll look briefly at what these artefacts are and how they are created. 199 | 200 | The `cds compile` command turns CDS definitions into different target outputs. In the case of our project based on SQLite, this output needs to be in the form of Data Definition Language (DDL) commands. 201 | 202 | When running the `cds deploy` command, this compilation is done as part of the process. But you can also see it explicitly. 203 | 204 | :point_right: Do that now, first for the data definitions, with: 205 | 206 | ```sh 207 | user@host:~/bookshop 208 | => cds compile db --to sql 209 | ``` 210 | 211 | This will produce SQL data definition language commands like this: 212 | 213 | ```sql 214 | CREATE TABLE my_bookshop_Authors ( 215 | ID INTEGER, 216 | name NVARCHAR(5000), 217 | PRIMARY KEY(ID) 218 | ); 219 | 220 | CREATE TABLE my_bookshop_Books ( 221 | ID INTEGER, 222 | title NVARCHAR(5000), 223 | stock INTEGER, 224 | author_ID INTEGER, 225 | PRIMARY KEY(ID) 226 | ); 227 | ``` 228 | 229 | :point_right: Now try it for the service definition: 230 | 231 | ```sh 232 | user@host:~/bookshop 233 | => cds compile srv --to sql 234 | ``` 235 | 236 | You should see output like this: 237 | 238 | ```sql 239 | CREATE TABLE my_bookshop_Authors ( 240 | ID INTEGER, 241 | name NVARCHAR(5000), 242 | PRIMARY KEY(ID) 243 | ); 244 | 245 | CREATE TABLE my_bookshop_Books ( 246 | ID INTEGER, 247 | title NVARCHAR(5000), 248 | stock INTEGER, 249 | author_ID INTEGER, 250 | PRIMARY KEY(ID) 251 | ); 252 | 253 | CREATE VIEW CatalogService_Authors AS SELECT 254 | "AUTHORS_$0".ID, 255 | "AUTHORS_$0".name 256 | FROM my_bookshop_Authors AS "AUTHORS_$0"; 257 | 258 | CREATE VIEW CatalogService_Books AS SELECT 259 | "BOOKS_$0".ID, 260 | "BOOKS_$0".title, 261 | "BOOKS_$0".stock, 262 | "BOOKS_$0".author_ID 263 | FROM my_bookshop_Books AS "BOOKS_$0"; 264 | ``` 265 | 266 | Observe that compiling the service definition will automatically produce DDL for the entities in the data model too, as the service refers to them. Observe also that the service artefacts are views, whereas the data model artefacts are tables. 267 | 268 | 269 | ## Summary 270 | 271 | You now have a fully functional, albeit simple, OData service backed by a persistence layer, where the data is stored in a local SQLite database file. 272 | 273 | 274 | ## Questions 275 | 276 | 1. What are other possible targets in the compilation context? 277 | 278 | 279 | 2. What is the thinking behind the use of views at the service definition layer and tables at the data model layer? 280 | 281 | 282 | 3. Why might you use the `cds compile` command at all? 283 | 284 | -------------------------------------------------------------------------------- /exercises/03/run.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/SAP-samples/cloud-cap-nodejs-codejam/tree/master/exercises/03 4 | 5 | echo EXERCISE 03 6 | 7 | cd bookshop 8 | 9 | echo 1. Use cds watch to have the service restart on changes \(NOP\) 10 | 11 | echo 2. Add a new Authors entity to the model 12 | cat < db/schema.cds 13 | namespace my.bookshop; 14 | 15 | entity Books { 16 | key ID : Integer; 17 | title : String; 18 | stock : Integer; 19 | } 20 | entity Authors { 21 | key ID : Integer; 22 | name : String; 23 | } 24 | EOSCHEMA 25 | 26 | echo 3. Expose the Authors entity in the service 27 | cat < srv/service.cds 28 | using my.bookshop as my from '../db/schema'; 29 | 30 | service CatalogService { 31 | entity Books as projection on my.Books; 32 | entity Authors as projection on my.Authors; 33 | } 34 | EOSERVICE 35 | 36 | echo 4. Add a relationship between the Books and Authors entities 37 | cat < db/schema.cds 38 | namespace my.bookshop; 39 | 40 | entity Books { 41 | key ID : Integer; 42 | title : String; 43 | stock : Integer; 44 | author : Association to Authors; 45 | } 46 | entity Authors { 47 | key ID : Integer; 48 | name : String; 49 | books : Association to many Books on books.author = \$self; 50 | } 51 | EORELATIONS 52 | 53 | echo 5. Deploy the service to a persistence layer 54 | npm install -D sqlite3 55 | cds deploy --to sqlite:bookshop.db 56 | 57 | echo 6. Explore the new database \(NOP\) 58 | 59 | echo 7. Dig into the link between the CDS definitions and the artefacts in the database \(NOP\) 60 | 61 | -------------------------------------------------------------------------------- /exercises/04/books-and-authors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/04/books-and-authors.png -------------------------------------------------------------------------------- /exercises/04/csv-files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/04/csv-files.png -------------------------------------------------------------------------------- /exercises/04/my.bookshop-Authors.csv: -------------------------------------------------------------------------------- 1 | ID,NAME 2 | 42,Douglas Adams 3 | 101,Emily Brontë 4 | 107,Charlotte Brontë 5 | 150,Edgar Allen Poe 6 | 170,Richard Carpenter 7 | -------------------------------------------------------------------------------- /exercises/04/my.bookshop-Books.csv: -------------------------------------------------------------------------------- 1 | ID,TITLE,AUTHOR_ID,STOCK 2 | 421,The Hitch Hiker's Guide To The Galaxy,42,1000 3 | 427,"Life, The Universe And Everything",42,95 4 | 201,Wuthering Heights,101,12 5 | 207,Jane Eyre,107,11 6 | 251,The Raven,150,333 7 | 252,Eleonora,150,555 8 | 271,Catweazle,170,22 9 | -------------------------------------------------------------------------------- /exercises/04/raw-option.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/04/raw-option.png -------------------------------------------------------------------------------- /exercises/04/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 04 - Adding data & testing OData operations 2 | 3 | In [exercise 03](../03/) you deployed the service definition (including the referenced data model) to a SQLite-powered persistence layer. While at this stage the OData service is working nicely, we don't yet have data to explore with. 4 | 5 | In this exercise you'll seed the persistence layer with data from CSV files, which is often a great way to kick start exploration, development and testing in the early stages of a project. 6 | 7 | 8 | ## Steps 9 | 10 | After completing these steps you'll have some authors and books data in your OData service and will have explored that data in the browser with OData Query operations. 11 | 12 | 13 | ### 1. Bring sample CSV files in the project 14 | 15 | The SAP Cloud Application Programming Model adopts a "convention over configuration" approach in many areas, one of which is the automatic recognition and loading of data from CSV files during a deployment. In this step you'll create a `csv/` directory within the `db/` directory, add two CSV files (one for each of the entities) and redeploy. The deployment process will spot the CSV files and load the contents into the tables in the persistence layer. 16 | 17 | :point_right: Create a `csv/` directory within the `db/` directory, and copy into it the CSV files (from this repository) [my.bookshop-Books.csv](my.bookshop-Books.csv) and [my.bookshop-Authors.csv](my.bookshop-Authors.csv). Use the "Raw" link from within each of these GitHub resources to get the actual CSV data to download (and don't forget to ensure the `.csv` extension is used for the files that you save). 18 | 19 | ![raw option on the CSV pages](raw-option.png) 20 | 21 | Your directory structure should then look something like this (the screenshot also shows the content of the two CSV files): 22 | 23 | ![the CSV files in the right place](csv-files.png) 24 | 25 | 26 | ### 2. Redeploy to the persistence layer 27 | 28 | The CSV files are discovered and used during a `cds deploy`, so a new deployment is required. While the `cds watch` will restart the service, it won't do a deploy for us, so we'll do that manually now. 29 | 30 | :point_right: Deploy again, this time noting that you don't have to specify the `--to` option: 31 | 32 | 33 | ```sh 34 | user@host:~/bookshop 35 | => cds deploy 36 | ``` 37 | 38 | During deployment this time you should see extra messages: 39 | 40 | ``` 41 | > filling my.bookshop.Authors from db/csv/my.bookshop-Authors.csv 42 | > filling my.bookshop.Books from db/csv/my.bookshop-Books.csv 43 | /> successfully deployed to ./bookshop.db 44 | ``` 45 | 46 | ### 3. Restart the service 47 | 48 | :point_right: Now the data's been loaded, you should fire up `cds watch` again: 49 | 50 | ```sh 51 | user@host:~/bookshop 52 | => cds watch 53 | ``` 54 | 55 | Now the [Books](http://localhost:4004/catalog/Books) and [Authors](http://localhost:4004/catalog/Authors) entitysets in the OData service will show data in response to OData Query and Read operations. 56 | 57 | ![Books and Authors in the OData service](books-and-authors.png) 58 | 59 | 60 | ### 4. Try out some OData Query operations 61 | 62 | The [OData standard](https://www.odata.org/) describes a number of different operations - Create, Read, Update, Delete and Query (otherwise known as 'CRUD+Q'). With your browser you can try out Read and Query operations directly. 63 | 64 | :point_right: Try out a few Read and Query operations on the data in the service like this: 65 | 66 | | Read / Query | URL | 67 | | ----- | --- | 68 | | Show all books | http://localhost:4004/catalog/Books | 69 | | Show all authors | http://localhost:4004/catalog/Authors | 70 | | Retrieve book with ID 421 | http://localhost:4004/catalog/Books(421) | 71 | | Retrieve author with ID 42 and also their books | http://localhost:4004/catalog/Authors(42)?$expand=books | 72 | 73 | 74 | ## Summary 75 | 76 | Your OData service now has sample data that you can access via OData operations. 77 | 78 | 79 | ## Questions 80 | 81 | 1. Does the order of the fields defined in the CSV files have to match the order of the properties defined in the entities in the data model? 82 | 83 | 84 | 2. Where do you think the format of the CSV file names has come from? 85 | 86 | 87 | 3. How come we didn't have to specify the details (in `--to`) during the deployment in this exercise? 88 | -------------------------------------------------------------------------------- /exercises/04/run.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/SAP-samples/cloud-cap-nodejs-codejam/tree/master/exercises/04 4 | 5 | echo EXERCISE 04 6 | 7 | cd bookshop 8 | 9 | echo 1. Bring sample CSV files in the project 10 | mkdir db/csv 11 | cat < db/csv/my.bookshop-Authors.csv 12 | ID,NAME 13 | 42,Douglas Adams 14 | 101,Emily Brontë 15 | 107,Charlote Brontë 16 | 150,Edgar Allen Poe 17 | 170,Richard Carpenter 18 | EOAUTHORS 19 | 20 | cat < db/csv/my.bookshop-Books.csv 21 | ID,TITLE,AUTHOR_ID,STOCK 22 | 421,The Hitch Hiker's Guide To The Galaxy,42,1000 23 | 427,"Life, The Universe And Everything",42,95 24 | 201,Wuthering Heights,101,12 25 | 207,Jane Eyre,107,11 26 | 251,The Raven,150,333 27 | 252,Eleonora,150,555 28 | 271,Catweazle,170,22 29 | EOBOOKS 30 | 31 | echo 2. Redeploy to the persistence layer 32 | cds deploy 33 | 34 | echo 3. Restart the service \(NOP\) 35 | 36 | echo 4. Try out some OData Query operations \(NOP\) 37 | -------------------------------------------------------------------------------- /exercises/05/cat-service.cds: -------------------------------------------------------------------------------- 1 | using my.bookshop as my from '../db/data-model'; 2 | 3 | service CatalogService { 4 | entity Books as projection on my.Books; 5 | entity Authors as projection on my.Authors; 6 | entity Orders as projection on my.Orders; 7 | } 8 | -------------------------------------------------------------------------------- /exercises/05/common-cds.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/05/common-cds.png -------------------------------------------------------------------------------- /exercises/05/data-model.cds: -------------------------------------------------------------------------------- 1 | namespace my.bookshop; 2 | 3 | using { cuid, managed, Country } from '@sap/cds/common'; 4 | 5 | entity Books { 6 | key ID : Integer; 7 | title : String; 8 | stock : Integer; 9 | author : Association to Authors; 10 | } 11 | 12 | entity Authors { 13 | key ID : Integer; 14 | name : String; 15 | books : Association to many Books on books.author = $self; 16 | } 17 | 18 | entity Orders : cuid, managed { 19 | book : Association to Books; 20 | quantity : Integer; 21 | country : Country; 22 | } -------------------------------------------------------------------------------- /exercises/05/import-collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/05/import-collection.png -------------------------------------------------------------------------------- /exercises/05/orders-entity.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/05/orders-entity.png -------------------------------------------------------------------------------- /exercises/05/postman-05.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "f9905494-1978-471e-977e-f428d197014e", 4 | "name": "codejam-cap-nodejs-ex05", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Order 5 copies of Wuthering Heights (no order ID specified)", 10 | "request": { 11 | "method": "POST", 12 | "header": [ 13 | { 14 | "key": "Content-Type", 15 | "value": "application/json" 16 | } 17 | ], 18 | "body": { 19 | "mode": "raw", 20 | "raw": "{\n \"book_ID\": 201,\n \"quantity\": 5\n}" 21 | }, 22 | "url": { 23 | "raw": "http://localhost:4004/catalog/Orders", 24 | "protocol": "http", 25 | "host": [ 26 | "localhost" 27 | ], 28 | "port": "4004", 29 | "path": [ 30 | "catalog", 31 | "Orders" 32 | ] 33 | } 34 | }, 35 | "response": [] 36 | }, 37 | { 38 | "name": "Order 9 copies of Life, The Universe And Everything (specifying an order ID)", 39 | "request": { 40 | "method": "POST", 41 | "header": [ 42 | { 43 | "key": "Content-Type", 44 | "value": "application/json" 45 | } 46 | ], 47 | "body": { 48 | "mode": "raw", 49 | "raw": "{\n \"ID\": \"527ef85a-aef2-464b-89f6-6a3ce64f2e14\",\n \"book_ID\": 427,\n \"quantity\": 9\n}" 50 | }, 51 | "url": { 52 | "raw": "http://localhost:4004/catalog/Orders", 53 | "protocol": "http", 54 | "host": [ 55 | "localhost" 56 | ], 57 | "port": "4004", 58 | "path": [ 59 | "catalog", 60 | "Orders" 61 | ] 62 | } 63 | }, 64 | "response": [] 65 | } 66 | ] 67 | } -------------------------------------------------------------------------------- /exercises/05/postman-collection.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/05/postman-collection.png -------------------------------------------------------------------------------- /exercises/05/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 05 - Adding a further entity, using generic features 2 | 3 | In this exercise you'll add a further entity to the data model and expose it through the service. In defining this entity you'll make use of some [generic features](https://cap.cloud.sap/docs/cds/common) available for all CAP projects. 4 | 5 | 6 | ## Steps 7 | 8 | At the end of these steps you'll have a third entity `Orders`, and will have performed some OData Create operations upon it. 9 | 10 | 11 | ### 1. Add a new entity Orders 12 | 13 | If this is a bookshop service, we need to be able to place orders. So you should now add a third entity to the data model, for those orders. 14 | 15 | :point_right: Open the `db/schema.cds` file and first of all add this third entity (not forgetting to save the file when you're done): 16 | 17 | ```cds 18 | entity Orders { 19 | key ID : UUID; 20 | book : Association to Books; 21 | quantity : Integer; 22 | } 23 | ``` 24 | 25 | We're not quite done with this entity, but for now, you're about to have a first look at the fruits of your labor by adding a new entry to the service definition for this entity. 26 | 27 | :point_right: Add the entry to the `CatalogService` service definition in the `srv/service.cds` file: 28 | 29 | ``` 30 | service CatalogService { 31 | entity Books as projection on my.Books; 32 | entity Authors as projection on my.Authors; 33 | entity Orders as projection on my.Orders; // <-- new 34 | } 35 | ``` 36 | 37 | Observe that the CDS Language Service extension picks up the new `Orders` entity straight away (as long as you've saved the `db/schema.cds` file) and offers it as a suggestion in the code completion feature. 38 | 39 | :point_right: Noting that your service has been automatically restarted (by `cds watch`) already, take a look at the new `Orders` entity: . 40 | 41 | You should see an error both in the response returned, and in the service log output, that looks something like this: 42 | 43 | ``` 44 | SQLITE_ERROR: no such table: CatalogService_Orders 45 | ``` 46 | 47 | That's because we still need to deploy the changes to the persistence layer, to have a new table and view created there for the `Orders` entity. 48 | 49 | :point_right: Do this now (note that the CSV data will be used again to seed the tables): 50 | 51 | ```sh 52 | user@host:~/bookshop 53 | => cds deploy 54 | > filling my.bookshop.Authors from db/csv/my.bookshop-Authors.csv 55 | > filling my.bookshop.Books from db/csv/my.bookshop-Books.csv 56 | /> successfully deployed database to bookshop.db 57 | user@host:~/bookshop 58 | => 59 | ``` 60 | 61 | > If you want to make sure that the new table and view are there now, you can check with `sqlite3 bookshop.db .tables`. 62 | 63 | :point_right: Once you've redeployed, restart `cds watch`: 64 | 65 | ```sh 66 | user@host:~/bookshop 67 | => cds watch 68 | ``` 69 | 70 | The `Orders` entity is now available in the service (but there is [no data](http://localhost:4004/catalog/Orders) as yet). 71 | 72 | 73 | ### 2. Explore generic CDS features 74 | 75 | When a new order comes in we want to capture the date and time. If we were running in an authenticated environment (in this CodeJam we're not, but CAP supports it) we also want to capture the user associated with the creation. Similarly we want to capture modification information. 76 | 77 | We can use some [common CDS definitions](https://cap.cloud.sap/docs/guides/domain-models#use-common-reuse-types) that are available to us, built into `@sap/cds` itself. These definitions can be found in the file `@sap/cds/common.cds` in the `node_modules/` directory. 78 | 79 | :point_right: Use the Explorer view in VS Code to open up the directories under `node_modules/` in the project, to find the `common.cds` file and open it up. In particular, find and examine the `managed` [aspect](https://cap.cloud.sap/docs/cds/common#aspect-managed), as well as the abstract entity `cuid`. 80 | 81 | ![looking at common.cds](common-cds.png) 82 | 83 | 84 | ### 3. Enhance the Orders entity 85 | 86 | You will now enhance the `Orders` entity using some of the common features made available in `@sap/cds/common.cds`: 87 | 88 | - using the canonical universal ID field 89 | - adding creation and modification information 90 | - adding a country property referring to the `Country` type 91 | 92 | :point_right: First, import the common features to the data model file by adding the following line to `db/schema.cds`, on the line below the `namespace` declaration: 93 | 94 | ```cds 95 | using { cuid, managed, Country } from '@sap/cds/common'; 96 | ``` 97 | 98 | These features are now available to use in our entity definitions. 99 | 100 | :point_right: Now remove the explicit key property definition (`ID`), and instead, add the `cuid` aspect as shown: 101 | 102 | ``` 103 | entity Orders : cuid { 104 | book : Association to Books; 105 | quantity : Integer; 106 | } 107 | ``` 108 | 109 | The use of `cuid` in this position will cause a key property of type `UUID` to be added implicitly to this entity definition. 110 | 111 | :point_right: Now also add the `managed` aspect to the entity thus: 112 | 113 | ``` 114 | entity Orders : cuid, managed { 115 | book : Association to Books; 116 | quantity : Integer; 117 | } 118 | ``` 119 | 120 | This will cause the implicit inclusion of four properties in this entity (these are the `createdAt`, `createdBy`, `modifiedAt` and `modifiedBy` properties from the `managed` type definition in `@sap/cds/common.cds` we examined earlier in this exercise). 121 | 122 | :point_right: Finally add a new explicit property `country`, described by the `Country` type which you imported from `@sap/cds/common`: 123 | 124 | ``` 125 | entity Orders : cuid, managed { 126 | book : Association to Books; 127 | quantity : Integer; 128 | country : Country; 129 | } 130 | ``` 131 | 132 | Note the difference in capitalization here. The property name is `country` which is described by the type `Country`. 133 | 134 | 135 | ### 4. Restart the service manually and check the output 136 | 137 | While the `cds watch` is useful, it supresses various messages to keep the noise down. But there's something in those suppressed messages that you should pay attention to. 138 | 139 | :point_right: Terminate any running `cds watch` process, and run `cds deploy && npm start` manually: 140 | 141 | ```sh 142 | user@host:~/bookshop 143 | => cds deploy && npm start 144 | > filling my.bookshop.Authors from db/csv/my.bookshop-Authors.csv 145 | > filling my.bookshop.Books from db/csv/my.bookshop-Books.csv 146 | /> successfully deployed to ./bookshop 147 | 148 | > bookshop@1.0.0 start /tmp/codejam/bookshop 149 | > npx cds run 150 | 151 | [cds] - connect to datasource - sqlite:bookshop 152 | [cds] - serving CatalogService at /catalog 153 | [cds] - service definitions loaded from: 154 | 155 | srv/service.cds 156 | db/schema.cds 157 | node_modules/@sap/cds/common.cds 158 | 159 | [cds] - launched in: 875.646ms 160 | [cds] - server listening on http://localhost:4004 ... 161 | [ terminate with ^C ] 162 | ``` 163 | 164 | > If your operating system command line doesn't support `&&` then just run the two commands one after the other. 165 | 166 | Notice the extra line in the output of the "service definitions loaded from" message. It shows us that not only are definitions being loaded from what we've defined explicitly (i.e. our `srv/service.cds` and `db/schema.cds` files) but also, implicitly, from `node_modules/@sap/cds/common.cds` because of our reference to it in `db/schema.cds` in the `using` statement. 167 | 168 | 169 | 170 | ### 5. Examine what the Orders entity looks like now 171 | 172 | Following the enhancements, it's worth taking a look at what the `Orders` entity looks like now. 173 | 174 | Open the [metadata document](http://localhost:4004/catalog/$metadata) and find the specific definition of the `Orders` entity within the XML. It should look like this: 175 | 176 | ![Orders entity definition](orders-entity.png) 177 | 178 | Note the type of the `ID` property, the properties resulting from the use of the `managed` aspect, and the navigation property between the `Orders` entity and a new `Countries` entity. 179 | 180 | 181 | ### 6. Create some entries in the Orders entity 182 | 183 | Put the new entity through its paces by performing some OData Create operations to insert orders. An OData Create operation is carried out with an HTTP POST request. If you're confident on the command line and have `curl` installed, you can do this with those tools. Otherwise, you can use Postman (which you will have installed as part of the software [prerequisites](../../prerequisites.md)). 184 | 185 | An OData Create operation (request and response) to insert a new order looks in raw form like this: 186 | 187 | > This specific request/response pair is just for illustration - you do not have to enter it yourself 188 | 189 | Request: 190 | ``` 191 | POST /catalog/Orders HTTP/1.1 192 | Host: localhost:4004 193 | Content-Type: application/json 194 | Content-Length: 29 195 | 196 | {"book_ID":421, "quantity":5} 197 | ``` 198 | 199 | Response: 200 | ``` 201 | HTTP/1.1 201 Created 202 | X-Powered-By: Express 203 | OData-Version: 4.0 204 | content-type: application/json;odata.metadata=minimal 205 | Location: Orders(d9a2ffd5-ecc4-47aa-a91f-e88f70b7adf9) 206 | Date: Mon, 25 Mar 2019 13:47:38 GMT 207 | Connection: keep-alive 208 | Content-Length: 306 209 | 210 | {"@odata.context":"$metadata#Orders/$entity","@odata.metadataEtag":"W/\"s2St6s/UTUxSfYEFAcOmOIuoSKQn7qxgEm65c/QqjAs=\"","ID":"d9a2ffd5-ecc4-47aa-a91f-e88f70b7adf9","modifiedAt":null,"createdAt":"2019-03-25T13:47:38Z","createdBy":"anonymous","modifiedBy":null,"quantity":5,"book_ID":421,"country_code":null} 211 | ``` 212 | 213 | If you want to create the Orders entities using the command line with `curl`, here's what you can do. Otherwise, skip to the [Using Postman](#postman) section. 214 | 215 | **Using `curl` on the command line** 216 | 217 | :point_right: Order 5 copies of Wuthering Heights (no order ID specified): 218 | 219 | ```sh 220 | curl \ 221 | -d '{"book_ID":201,"quantity":5}' \ 222 | -H 'Content-Type: application/json' \ 223 | http://localhost:4004/catalog/Orders 224 | ``` 225 | 226 | For Windows users, this is the equivalent command (basically you have to use double quotes throughout, and therefore some must be escaped with `\`, and the line continuation character is `^` rather than `\`): 227 | 228 | ```sh 229 | curl ^ 230 | -d "{\"book_ID\":201,\"quantity\":5}" ^ 231 | -H "Content-Type: application/json" ^ 232 | http://localhost:4004/catalog/Orders 233 | ``` 234 | 235 | :point_right: Order 9 copies of Life, The Universe And Everything (specifying an order ID): 236 | 237 | ```sh 238 | curl \ 239 | -d '{"ID": "527ef85a-aef2-464b-89f6-6a3ce64f2e14", "book_ID":427,"quantity":9}' \ 240 | -H 'Content-Type: application/json' \ 241 | http://localhost:4004/catalog/Orders 242 | ``` 243 | 244 | For Windows users: 245 | ```sh 246 | curl ^ 247 | -d "{\"ID\": \"527ef85a-aef2-464b-89f6-6a3ce64f2e14\", \"book_ID\":427,\"quantity\":9}" ^ 248 | -H "Content-Type: application/json" ^ 249 | http://localhost:4004/catalog/Orders 250 | ``` 251 | 252 | **Using Postman** 253 | 254 | Instead of using `curl` you can use Postman. There are some OData Create operations for this Orders entity prepared for you in a form that can be imported into Postman. Do that now. 255 | 256 | :point_right: Launch Postman and import a collection using the "Import From Link" feature in this dialogue box: 257 | 258 | ![importing a collection into Postman](import-collection.png) 259 | 260 | For the URL, use the link to this [postman-05.json](https://raw.githubusercontent.com/SAP/cloud-cap-nodejs-codejam/master/exercises/05/postman-05.json) resource. 261 | 262 | :point_right: Use the two requests in the 'exercise 05' folder in this imported collection to order books, noting how one request specifies an order ID and the other does not. 263 | 264 | ![Postman request collection](postman-collection.png) 265 | 266 | 267 | ### 7. Examine the data in the Orders entityset 268 | 269 | Once you've made a few OData Create operations, have a look at the results in the `Orders` entityset. 270 | 271 | :point_right: Open up the [Orders entityset](http://localhost:4004/catalog/Orders) and confirm that orders have been created, noting in particular the values in the `createdAt` properties, as well as the values for the `ID` properties where you didn't specify values when making the requests. 272 | 273 | 274 | ## Summary 275 | 276 | At this point you have a meaningful OData service with data and against which you are now confidently performing various read and write OData operations. 277 | 278 | 279 | 280 | ## Questions 281 | 282 | 1. We added a field `country` described by the type `Country`. What exactly is this type, and what does it bring about in the resulting service's metadata? 283 | 284 | 285 | 2. Are there any issues with the way we have set up the service definition right now? 286 | 287 | -------------------------------------------------------------------------------- /exercises/05/run.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/SAP-samples/cloud-cap-nodejs-codejam/tree/master/exercises/05 4 | 5 | echo EXERCISE 05 6 | 7 | cd bookshop 8 | 9 | 10 | echo 1. Add a new entity Orders 11 | 12 | cat < db/schema.cds 13 | namespace my.bookshop; 14 | 15 | entity Books { 16 | key ID : Integer; 17 | title : String; 18 | stock : Integer; 19 | author : Association to Authors; 20 | } 21 | entity Authors { 22 | key ID : Integer; 23 | name : String; 24 | books : Association to many Books on books.author = \$self; 25 | } 26 | entity Orders { 27 | key ID : UUID; 28 | book : Association to Books; 29 | quantity : Integer; 30 | } 31 | EOORDERS 32 | 33 | cat < srv/service.cds 34 | using my.bookshop as my from '../db/schema'; 35 | 36 | service CatalogService { 37 | entity Books as projection on my.Books; 38 | entity Authors as projection on my.Authors; 39 | entity Orders as projection on my.Orders; 40 | } 41 | EOORDERSSERVICE 42 | 43 | cds deploy 44 | 45 | 46 | echo 2. Explore generic CDS features \(NOP\) 47 | 48 | 49 | echo 3. Enhance the Orders entity 50 | 51 | cat < db/schema.cds 52 | namespace my.bookshop; 53 | using { cuid, managed, Country } from '@sap/cds/common'; 54 | 55 | entity Books { 56 | key ID : Integer; 57 | title : String; 58 | stock : Integer; 59 | author : Association to Authors; 60 | } 61 | entity Authors { 62 | key ID : Integer; 63 | name : String; 64 | books : Association to many Books on books.author = \$self; 65 | } 66 | entity Orders : cuid, managed { 67 | key ID : UUID; 68 | book : Association to Books; 69 | quantity : Integer; 70 | country : Country; 71 | } 72 | EOORDERSMOD 73 | 74 | 75 | echo 4. Restart the service manually and check the output 76 | cds deploy 77 | 78 | 79 | echo 5. Examine what the Orders entity looks like now \(NOP\) 80 | 81 | 82 | echo 6. Create some entries in the Orders entity 83 | 84 | cds run > run.log 2>&1 & 85 | CDSPID=$! 86 | sleep 2 87 | 88 | curl \ 89 | -d '{"book_ID":201,"quantity":5}' \ 90 | -H 'Content-Type: application/json' \ 91 | http://localhost:4004/catalog/Orders 92 | 93 | curl \ 94 | -d '{"ID": "527ef85a-aef2-464b-89f6-6a3ce64f2e14", "book_ID":427,"quantity":9}' \ 95 | -H 'Content-Type: application/json' \ 96 | http://localhost:4004/catalog/Orders 97 | 98 | kill $CDSPID 99 | 100 | 101 | echo 7. Examine the data in the Orders entityset \(NOP\) 102 | -------------------------------------------------------------------------------- /exercises/06/postman-06.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "a7fe5ef7-5bd1-480b-8fa3-9f7fff10d6f9", 4 | "name": "codejam-cap-nodejs-ex06", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "(A) Before @readonly annotations", 10 | "item": [ 11 | { 12 | "name": "1) Add author \"Iain M Banks\"", 13 | "request": { 14 | "method": "POST", 15 | "header": [ 16 | { 17 | "key": "Content-Type", 18 | "value": "application/json" 19 | } 20 | ], 21 | "body": { 22 | "mode": "raw", 23 | "raw": "{\n \"ID\": 162,\n \"name\": \"Iain M Banks\"\n}" 24 | }, 25 | "url": { 26 | "raw": "http://localhost:4004/catalog/Authors", 27 | "protocol": "http", 28 | "host": [ 29 | "localhost" 30 | ], 31 | "port": "4004", 32 | "path": [ 33 | "catalog", 34 | "Authors" 35 | ] 36 | } 37 | }, 38 | "response": [] 39 | }, 40 | { 41 | "name": "2) Add book \"Consider Phlebas\"", 42 | "request": { 43 | "method": "POST", 44 | "header": [ 45 | { 46 | "key": "Content-Type", 47 | "value": "application/json" 48 | } 49 | ], 50 | "body": { 51 | "mode": "raw", 52 | "raw": "{\n \"ID\": 44138,\n \"title\": \"Consider Phlebas\",\n \"stock\": 541,\n \"author_ID\": 162\n}" 53 | }, 54 | "url": { 55 | "raw": "http://localhost:4004/catalog/Books", 56 | "protocol": "http", 57 | "host": [ 58 | "localhost" 59 | ], 60 | "port": "4004", 61 | "path": [ 62 | "catalog", 63 | "Books" 64 | ] 65 | } 66 | }, 67 | "response": [] 68 | } 69 | ] 70 | }, 71 | { 72 | "name": "(B) After @readonly annotations", 73 | "item": [ 74 | { 75 | "name": "1) Add book \"The Player of Games\"", 76 | "request": { 77 | "method": "POST", 78 | "header": [ 79 | { 80 | "key": "Content-Type", 81 | "value": "application/json" 82 | } 83 | ], 84 | "body": { 85 | "mode": "raw", 86 | "raw": "{\n \"ID\": 47110,\n \"title\": \"The Player of Games\",\n \"stock\": 405,\n \"author_ID\": 162\n}" 87 | }, 88 | "url": { 89 | "raw": "http://localhost:4004/catalog/Books", 90 | "protocol": "http", 91 | "host": [ 92 | "localhost" 93 | ], 94 | "port": "4004", 95 | "path": [ 96 | "catalog", 97 | "Books" 98 | ] 99 | } 100 | }, 101 | "response": [] 102 | }, 103 | { 104 | "name": "2) Remove book \"The Raven\"", 105 | "request": { 106 | "method": "DELETE", 107 | "header": [], 108 | "body": { 109 | "mode": "raw", 110 | "raw": "" 111 | }, 112 | "url": { 113 | "raw": "http://localhost:4004/catalog/Books(251)", 114 | "protocol": "http", 115 | "host": [ 116 | "localhost" 117 | ], 118 | "port": "4004", 119 | "path": [ 120 | "catalog", 121 | "Books(251)" 122 | ] 123 | } 124 | }, 125 | "response": [] 126 | } 127 | ] 128 | } 129 | ] 130 | } -------------------------------------------------------------------------------- /exercises/06/postman-collection-06.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/06/postman-collection-06.png -------------------------------------------------------------------------------- /exercises/06/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 06 - Enhancing the service with annotations 2 | 3 | In this exercise you'll enhance the service definition with annotations that restrict the OData operations that are allowed. 4 | 5 | 6 | ## Steps 7 | 8 | At the end of these steps, your OData service will have different levels of access for each of the entities. 9 | 10 | 11 | ### 1. Import a collection of HTTP requests into Postman 12 | 13 | :point_right: In the same way that you did in [exercise 05](../05/), import a collection of HTTP requests into your Postman client via the URL to [postman-06.json](https://raw.githubusercontent.com/SAP/cloud-cap-nodejs-codejam/master/exercises/06/postman-06.json) resource. 14 | 15 | This contains a number of different requests ready for you to try. 16 | 17 | > Note: If you still want to use the command line, you'll find the invocations in the appropriate steps below. 18 | 19 | ![Postman collection](postman-collection-06.png) 20 | 21 | 22 | ### 2. Test the existing write access to Books and Authors 23 | 24 | Right now the `Books` and `Authors` entities are exposed in the `CatalogService` service. In the subsequent steps in this exercise we'll be tightening the restrictions down to read-only. Before we do, let's check to see that, at least currently, we have write access. We'll do that by making a couple of OData Create operations, one to create a new author, and the other to add a book by that author. 25 | 26 | :point_right: In the Postman collection you imported, try out the requests in the folder "**(A) Before @readonly annotations**", running them in the order they're presented (the creation of the new book is for the new author, which needs to exist first). 27 | 28 | If you want to use `curl` on the command line instead of Postman, use the following invocations. 29 | 30 | First, add the author "Iain M Banks": 31 | 32 | ```sh 33 | curl \ 34 | -d '{"ID": 162, "name": "Iain M Banks"}' \ 35 | -H 'Content-Type: application/json' \ 36 | http://localhost:4004/catalog/Authors 37 | ``` 38 | 39 | For Windows users: 40 | ```sh 41 | curl ^ 42 | -d "{\"ID\": 162, \"name\": \"Iain M Banks\"}" ^ 43 | -H "Content-Type: application/json" ^ 44 | http://localhost:4004/catalog/Authors 45 | ``` 46 | 47 | Now add the book "Consider Phlebas": 48 | 49 | ```sh 50 | curl \ 51 | -d '{"ID": 44138, "title": "Consider Phlebas", "stock": 541, "author_ID": 162 }' \ 52 | -H 'Content-Type: application/json' \ 53 | http://localhost:4004/catalog/Books 54 | ``` 55 | 56 | For Windows users: 57 | ```sh 58 | curl ^ 59 | -d "{\"ID\": 44138, \"title\": \"Consider Phlebas\", \"stock\": 541, \"author_ID\": 162 }" ^ 60 | -H "Content-Type: application/json" ^ 61 | http://localhost:4004/catalog/Books 62 | ``` 63 | 64 | Check that the creation requests are successful, and that you can see the new author and book in an OData Query operation: . 65 | 66 | 67 | ### 3. Restrict access to the Books and Authors entities 68 | 69 | Now, we want to only allow read-only operations on the master data. This can be achieved with OData annotations that are encapsulated into a convenient `@readonly` [shortcut](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/227cbf1a3ec24075a3aaaf6202f88be5.html). 70 | 71 | :point_right: Add this `@readonly` shortcut to each of the `Books` and `Authors` specifications in the `CatalogService` thus: 72 | 73 | ```cds 74 | service CatalogService { 75 | @readonly entity Books as projection on my.Books; 76 | @readonly entity Authors as projection on my.Authors; 77 | entity Orders as projection on my.Orders; 78 | } 79 | ``` 80 | 81 | What does this do, precisely? Let's find out. 82 | 83 | :point_right: After saving the file, start up `cds watch` to get back into the auto restart mode. The, examine the OData service's [metadata](http://localhost:4004/catalog/$metadata). You should find annotations that look like this: 84 | 85 | ![readonly annotations](readonly-annotations.png) 86 | 87 | Are these annotations just recommendations that can be overridden? Let's find out. 88 | 89 | 90 | ### 4. Attempt to modify the Books and Authors entitysets 91 | 92 | We can think of the annotations that we saw in the metadata document as guidelines for a UI, but we want to ensure that the restrictions are really in effect in the service itself. Let's try to create another entry in the `Books` entityset. 93 | 94 | :point_right: In the same Postman collection you imported, try out the first request in the folder "**(B) After @readonly annotations**". If you want to use `curl` on the command line instead of Postman, use the following invocation to add the book "The Player of Games": 95 | 96 | ```sh 97 | curl \ 98 | -d '{"ID": 47110, "title": "The Player of Games", "stock": 405, "author_ID": 162 }' \ 99 | -H 'Content-Type: application/json' \ 100 | http://localhost:4004/catalog/Books 101 | ``` 102 | 103 | For Windows users: 104 | ```sh 105 | curl ^ 106 | -d "{\"ID\": 47110, \"title\": \"The Player of Games\", \"stock\": 405, \"author_ID\": 162 }" ^ 107 | -H "Content-Type: application/json" ^ 108 | http://localhost:4004/catalog/Books 109 | ``` 110 | 111 | The request is an OData Create request for a new book. You should see that this request is rejected with HTTP status code 405 "Method Not Allowed", with an error like this supplied in the response body: 112 | 113 | ```json 114 | { 115 | "error": { 116 | "code": "405", 117 | "message": "Method Not Allowed" 118 | } 119 | } 120 | ``` 121 | 122 | You should also see a line in the terminal (where you invoked `cds serve all`) like this: 123 | 124 | ``` 125 | [2019-05-29T15:16:39.694Z | WARNING | 1939700]: Method Not Allowed 126 | ``` 127 | 128 | :point_right: Next, try out the second request in that same folder - it's an OData Delete operation, to remove the book "The Raven". If you want to use `curl` on the command line instead of Postman, use this invocation: 129 | 130 | ```sh 131 | curl \ 132 | -X DELETE \ 133 | 'http://localhost:4004/catalog/Books(251)' 134 | ``` 135 | 136 | For Windows users: 137 | ```sh 138 | curl ^ 139 | -X DELETE ^ 140 | "http://localhost:4004/catalog/Books(251)" 141 | ``` 142 | 143 | It should also fail in a similar way. 144 | 145 | _TIP: If you end up destroying your test data, you can easily restore it by redeploying (`cds deploy`), as the test data will be re-seeded from the CSV files._ 146 | 147 | 148 | ### 5. Restrict access to the Orders entityset 149 | 150 | In a similar way to how we restricted access to the `Books` and `Authors` entitysets to read-only operations, we will now restrict access to the `Orders` entityset so that orders can only be created, and not viewed, amended or removed. 151 | 152 | As you might have guessed, this is achieved via the `@insertonly` annotation shortcut. 153 | 154 | :point_right: Before making this edit, switch back (if you haven't already) to using `cds watch` so that restarts will be automatic after changes. 155 | 156 | :point_right: In the `CatalogService` service definition in `srv/service.cds`, annotate the `Orders` entity with `@insertonly` so it looks like this: 157 | 158 | ```cds 159 | service CatalogService { 160 | @readonly entity Books as projection on my.Books; 161 | @readonly entity Authors as projection on my.Authors; 162 | @insertonly entity Orders as projection on my.Orders; 163 | } 164 | ``` 165 | 166 | :point_right: Now create a couple of orders using the Postman collection from [exercise 05](../05/) - there should be a couple of POST requests against the `Orders` entityset (refer to the step in exercise 05 for the command line invocations if you wish). 167 | 168 | ![Postman request collection](../05/postman-collection.png) 169 | 170 | Note at this point that the requests are successful: HTTP status code 201 is returned for each request, along with the newly created entity in the response payload, like this example: 171 | 172 | ```json 173 | { 174 | "@odata.context": "$metadata#Orders/$entity", 175 | "@odata.metadataEtag": "W/\"qItYMyHC4RMSWG6mehaOHDxo+o/HzUCPMchqSx7hd1k=\"", 176 | "ID": "527ef85a-aef2-464b-89f6-6a3ce64f2e14", 177 | "modifiedAt": null, 178 | "createdAt": "2019-03-26T06:51:52Z", 179 | "createdBy": "anonymous", 180 | "modifiedBy": null, 181 | "quantity": 9, 182 | "book_ID": 427, 183 | "country_code": null 184 | } 185 | ``` 186 | 187 | Further, you can see the request logged in the terminal, with no sign of any errors: 188 | 189 | ``` 190 | POST /catalog/Orders 191 | ``` 192 | 193 | This confirms we can insert new orders. But can we see what they are? 194 | 195 | :point_right: Try to perform an OData Query operation on the `Orders` entityset, simply by requesting this URL: [http://localhost:4004/catalog/Orders](http://localhost:4004/catalog/Orders). 196 | 197 | The operation should be denied, and you'll receive something like this in the body of the response in your browser: 198 | 199 | ```xml 200 | 201 | 405 202 | Method Not Allowed 203 | 204 | ``` 205 | 206 | 207 | ## Summary 208 | 209 | In this exercise you used shortcut annotations to restrict access to the entities expose in the service definition. The annotations provide not only information to be used by consumers (such as frontends) but also control access at the HTTP level. 210 | 211 | 212 | ## Questions 213 | 214 | 1. Did you notice anything special about your request to in step 2? 215 | 216 | 217 | 2. How might the annotations relating to the read-only restrictions be useful in a UI context? 218 | 219 | 220 | 3. What was the format of the OData Delete operation - did we need to supply a payload? 221 | 222 | -------------------------------------------------------------------------------- /exercises/06/readonly-annotations.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/06/readonly-annotations.png -------------------------------------------------------------------------------- /exercises/06/run.bash: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # https://github.com/SAP-samples/cloud-cap-nodejs-codejam/tree/master/exercises/06 4 | 5 | echo EXERCISE 06 6 | 7 | cd bookshop 8 | 9 | 10 | echo 1. Import a collection of HTTP requests into Postman \(NOP\) 11 | 12 | 13 | echo 2. Test the existing write access to Books and Authors 14 | 15 | cat < srv/service.cds 16 | using my.bookshop as my from '../db/schema'; 17 | 18 | service CatalogService { 19 | entity Books as projection on my.Books; 20 | entity Authors as projection on my.Authors; 21 | entity Orders as projection on my.Orders; 22 | } 23 | EOSERVICE 24 | 25 | 26 | cds run > run.log 2>&1 & 27 | CDSPID=$! 28 | sleep 2 29 | 30 | curl -X DELETE 'http://localhost:4004/catalog/Books(44138)' 31 | curl -X DELETE 'http://localhost:4004/catalog/Authors(162)' 32 | 33 | curl \ 34 | -d '{"ID": 162, "name": "Iain M Banks"}' \ 35 | -H 'Content-Type: application/json' \ 36 | http://localhost:4004/catalog/Authors 37 | 38 | curl \ 39 | -d '{"ID": 44138, "title": "Consider Phlebas", "stock": 541, "author_ID": 162 }' \ 40 | -H 'Content-Type: application/json' \ 41 | http://localhost:4004/catalog/Books 42 | 43 | kill $CDSPID 44 | 45 | 46 | echo 3. Restrict access to the Books and Authors entities 47 | cat < srv/service.cds 48 | using my.bookshop as my from '../db/schema'; 49 | 50 | service CatalogService { 51 | @readonly entity Books as projection on my.Books; 52 | @readonly entity Authors as projection on my.Authors; 53 | entity Orders as projection on my.Orders; 54 | } 55 | EOSERVICEREADONLY 56 | 57 | 58 | echo 4. Attempt to modify the Books and Authors entitysets 59 | 60 | cds run > run.log 2>&1 & 61 | CDSPID=$! 62 | sleep 2 63 | 64 | curl \ 65 | -d '{"ID": 47110, "title": "The Player of Games", "stock": 405, "author_ID": 162 }' \ 66 | -H 'Content-Type: application/json' \ 67 | http://localhost:4004/catalog/Books 68 | 69 | curl \ 70 | -X DELETE \ 71 | 'http://localhost:4004/catalog/Books(251)' 72 | 73 | kill $CDSPID 74 | 75 | 76 | echo 5. Restrict access to the Orders entityset 77 | 78 | cat < srv/service.cds 79 | using my.bookshop as my from '../db/schema'; 80 | 81 | service CatalogService { 82 | @readonly entity Books as projection on my.Books; 83 | @readonly entity Authors as projection on my.Authors; 84 | @insertonly entity Orders as projection on my.Orders; 85 | } 86 | EOSERVICEINSERTONLY 87 | 88 | cds run > run.log 2>&1 & 89 | CDSPID=$! 90 | sleep 2 91 | 92 | curl \ 93 | -d '{"book_ID":201,"quantity":5}' \ 94 | -H 'Content-Type: application/json' \ 95 | http://localhost:4004/catalog/Orders 96 | 97 | curl \ 98 | -d '{"ID": "527ef85a-aef2-464b-89f6-6a3ce64f2e14", "book_ID":427,"quantity":9}' \ 99 | -H 'Content-Type: application/json' \ 100 | http://localhost:4004/catalog/Orders 101 | 102 | kill $CDSPID 103 | 104 | 105 | -------------------------------------------------------------------------------- /exercises/07/collection-runner.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/07/collection-runner.png -------------------------------------------------------------------------------- /exercises/07/orderinfo-entityset.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/07/orderinfo-entityset.png -------------------------------------------------------------------------------- /exercises/07/postman-07.json: -------------------------------------------------------------------------------- 1 | { 2 | "info": { 3 | "_postman_id": "6531f05c-510e-4233-ba3e-18c85eb99be9", 4 | "name": "codejam-cap-nodejs-ex07", 5 | "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" 6 | }, 7 | "item": [ 8 | { 9 | "name": "Order 2 copies of Wuthering Heights", 10 | "request": { 11 | "method": "POST", 12 | "header": [ 13 | { 14 | "key": "Content-Type", 15 | "value": "application/json" 16 | } 17 | ], 18 | "body": { 19 | "mode": "raw", 20 | "raw": "{\n \"book_ID\": 201,\n \"quantity\": 2\n}" 21 | }, 22 | "url": { 23 | "raw": "http://localhost:4004/catalog/Orders", 24 | "protocol": "http", 25 | "host": [ 26 | "localhost" 27 | ], 28 | "port": "4004", 29 | "path": [ 30 | "catalog", 31 | "Orders" 32 | ] 33 | } 34 | }, 35 | "response": [] 36 | }, 37 | { 38 | "name": "Order 7 copies of Eleonora", 39 | "request": { 40 | "method": "POST", 41 | "header": [ 42 | { 43 | "key": "Content-Type", 44 | "value": "application/json" 45 | } 46 | ], 47 | "body": { 48 | "mode": "raw", 49 | "raw": "{\n \"book_ID\": 252,\n \"quantity\": 7\n}" 50 | }, 51 | "url": { 52 | "raw": "http://localhost:4004/catalog/Orders", 53 | "protocol": "http", 54 | "host": [ 55 | "localhost" 56 | ], 57 | "port": "4004", 58 | "path": [ 59 | "catalog", 60 | "Orders" 61 | ] 62 | } 63 | }, 64 | "response": [] 65 | }, 66 | { 67 | "name": "Order 42 copies of The Hitch Hiker's Guide To The Galaxy", 68 | "request": { 69 | "method": "POST", 70 | "header": [ 71 | { 72 | "key": "Content-Type", 73 | "value": "application/json" 74 | } 75 | ], 76 | "body": { 77 | "mode": "raw", 78 | "raw": "{\n \"book_ID\": 421,\n \"quantity\": 42\n}" 79 | }, 80 | "url": { 81 | "raw": "http://localhost:4004/catalog/Orders", 82 | "protocol": "http", 83 | "host": [ 84 | "localhost" 85 | ], 86 | "port": "4004", 87 | "path": [ 88 | "catalog", 89 | "Orders" 90 | ] 91 | } 92 | }, 93 | "response": [] 94 | } 95 | ] 96 | } -------------------------------------------------------------------------------- /exercises/07/postman-collection-07.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/07/postman-collection-07.png -------------------------------------------------------------------------------- /exercises/07/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 07 - Defining a second service 2 | 3 | In this exercise you'll enhance the service definition by introducing a second service that sits on top of the same underlying data model. You'll see that you can define as many services on the same data model as you need, either in the same service definition file, or in separate files. In this exercise you'll define the second service in the same file. 4 | 5 | The scenario is that a service is required to provide an analysis frontend with basic statistics on book orders, and nothing else - so there's no need (or desire) to expose the rest of the data model to this frontend. 6 | 7 | 8 | ## Steps 9 | 10 | At the end of these steps, you'll have two OData services both exposing different views on the same underlying data model. 11 | 12 | 13 | ### 1. Add a new service definition 14 | 15 | Service definitions can live alongside each other in the same CDS file. 16 | 17 | :point_right: In `srv/service.cds`, add a second service definition thus: 18 | 19 | ```cds 20 | service Stats { 21 | @readonly entity OrderInfo as projection on my.Orders excluding { 22 | createdAt, 23 | createdBy, 24 | modifiedAt, 25 | modifiedBy, 26 | book, 27 | country 28 | } 29 | } 30 | ``` 31 | 32 | Here the `Stats` service exposes the Orders entity in a read-only fashion as in the `CatalogService`, but uses the `excluding` clause to omit specific properties. These properties are not of interest to the analysis UI so are explicitly left out. Note that it also exposes the information as an entity called `OrderInfo`. 33 | 34 | :point_right: Effect a deployment to the persistence layer so that the relevant artefact will be created: 35 | 36 | ```sh 37 | user@host:~/bookshop 38 | => cds deploy 39 | ``` 40 | 41 | 42 | :point_right: Start the service up again via `cds watch` and check the root document at [http://localhost:4004/](http://localhost:4004/). You should see something like this: 43 | 44 | ![two services](two-services.png) 45 | 46 | 47 | ### 2. Create multiple orders 48 | 49 | Now let's create a number of orders, and see what the `OrderInfo` entityset shows us. We can do this quickly using another Postman collection, and using Postman's "Collection Runner" feature. 50 | 51 | Note: If you want to do this using `curl`, jump to the [using `curl` on the command line](#commandline) section. 52 | 53 | **Using Postman** 54 | 55 | :point_right: Import another collection into Postman from the URL to this resource: [postman-07.json](https://raw.githubusercontent.com/SAP/cloud-cap-nodejs-codejam/master/exercises/07/postman-07.json). 56 | 57 | This screenshot shows what the collection looks like (it contains multiple POST requests to create orders for various books) and also shows the extra options which allows all the requests in the collection to be executed in one go: 58 | 59 | ![Postman collection](postman-collection-07.png) 60 | 61 | :point_right: After importing this collection, click the arrow to the right of the collection name to expand the options as shown, and select the small blue "Run" button which will open up the "Collection Runner" window: 62 | 63 | ![Collection Runner window](collection-runner.png) 64 | 65 | :point_right: Use the large blue "Run ..." button to execute all the requests - a results window should appear. 66 | 67 | **Using `curl` on the command line** 68 | 69 | Order 2 copies of Wuthering Heights: 70 | 71 | ```sh 72 | curl \ 73 | -d '{"book_ID":201,"quantity":2}' \ 74 | -H 'Content-Type: application/json' \ 75 | http://localhost:4004/catalog/Orders 76 | ``` 77 | 78 | For Windows users: 79 | ```sh 80 | curl ^ 81 | -d "{\"book_ID\": 201, \"quantity\": 2}" ^ 82 | -H "Content-Type: application/json" ^ 83 | http://localhost:4004/catalog/Orders 84 | ``` 85 | 86 | Order 7 copies of Eleonora: 87 | 88 | ```sh 89 | curl \ 90 | -d '{"book_ID":252,"quantity":7}' \ 91 | -H 'Content-Type: application/json' \ 92 | http://localhost:4004/catalog/Orders 93 | ``` 94 | 95 | For Windows users: 96 | ```sh 97 | curl ^ 98 | -d "{\"book_ID\": 252, \"quantity\": 7}" ^ 99 | -H "Content-Type: application/json" ^ 100 | http://localhost:4004/catalog/Orders 101 | ``` 102 | 103 | Order 42 copies of The Hitch Hiker's Guide To The Galaxy (obviously!): 104 | 105 | ```sh 106 | curl \ 107 | -d '{"book_ID":421,"quantity":42}' \ 108 | -H 'Content-Type: application/json' \ 109 | http://localhost:4004/catalog/Orders 110 | ``` 111 | 112 | For Windows users: 113 | ```sh 114 | curl ^ 115 | -d "{\"book_ID\": 421, \"quantity\": 42}" ^ 116 | -H "Content-Type: application/json" ^ 117 | http://localhost:4004/catalog/Orders 118 | ``` 119 | 120 | Now it's time to take a look at what the service will show us for these orders. We know we can't look at the `Orders` entityset as it has a `@insertonly` annotation shortcut based restriction, so we turn to our new service `Stats`. 121 | 122 | :point_right: Look at the [http://localhost:4004/stats/OrderInfo](http://localhost:4004/stats/OrderInfo) entityset in the `Stats` service. You should see something like this: 123 | 124 | ![OrderInfo entityset](orderinfo-entityset.png) 125 | 126 | It shows us only the quantity statistics for the orders, just what we want for our simple analysis frontend. 127 | 128 | 129 | ## Summary 130 | 131 | It's easy to explore building different views on the same underlying data model, views that are focused and appropriate for different consumers, whether they are user interfaces or API clients. 132 | 133 | 134 | ## Questions 135 | 136 | 1. Why might it be better to specify properties to _exclude_, rather than properties to _include_? 137 | 138 | 139 | 2. What did the order creation HTTP requests look like - which service was used, and why? 140 | 141 | 142 | 3. What was the artefact created in the persistence layer in Step 1? 143 | 144 | -------------------------------------------------------------------------------- /exercises/07/two-services.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/07/two-services.png -------------------------------------------------------------------------------- /exercises/08/breakpoint-set.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/08/breakpoint-set.png -------------------------------------------------------------------------------- /exercises/08/debug-buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/08/debug-buttons.png -------------------------------------------------------------------------------- /exercises/08/debug-console.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/08/debug-console.png -------------------------------------------------------------------------------- /exercises/08/discount.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/08/discount.png -------------------------------------------------------------------------------- /exercises/08/handlers-dir.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/08/handlers-dir.png -------------------------------------------------------------------------------- /exercises/08/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 08 - Adding custom logic, and debugging 2 | 3 | In this exercise you'll learn how to add custom processing of specific OData operations on your services. It's done by adding [custom implementation logic](https://cap.cloud.sap/docs/guides/service-impl) (in JavaScript) via well-defined hooks into the service API. 4 | 5 | Along the way you'll also learn how to use debugging features in VS Code, with the [launch configuration](https://code.visualstudio.com/docs/editor/debugging#_launch-configurations) provided by the `cds init` command that you used in an earlier exercise. 6 | 7 | With custom implementation logic you can turn an out-of-the-box service into more or less whatever you need, in a clean and minimal way. 8 | 9 | 10 | ## Steps 11 | 12 | At the end of these steps you will have modified the `CatalogService` service in such a way that books with a high stock value will be discounted (and for the sake of simplicity with this simple data model, the discount will be shown in the book's title). 13 | 14 | 15 | ### 1. Create the outline of a custom logic handler 16 | 17 | Custom logic for a given service definition is provided in a JavaScript file that shares the same base name as that service definition file. For example, for a service definition file `my-service.cds` the custom logic should be placed in `my-service.js`. 18 | 19 | This custom logic file is normally placed in the same directory as the service definition file (i.e. side-by-side with it). 20 | 21 | :point_right: Create a new file `service.js` in the `srv/` directory. You should end up with something like this: 22 | 23 | ![handlers directory](handlers-dir.png) 24 | 25 | 26 | ### 2. Add some basic custom logic code 27 | 28 | :point_right: In the new file `service.js`, add the following code: 29 | 30 | ```javascript 31 | module.exports = srv => { 32 | 33 | console.log('Service name:', srv.name) 34 | 35 | } 36 | ``` 37 | 38 | You can see that this custom logic handler file is in the form of a module, which exports a single function. That function (defined using [ES6 arrow functions](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions)) has a single parameter `srv` to receive the server object on invocation. 39 | 40 | 41 | ### 3. Run the service 42 | 43 | Following the automatic service restart, you should see a few interesting new lines in the output: 44 | 45 | ``` 46 | [cds] - connect to datasource - sqlite:bookshop 47 | [cds] - serving CatalogService at /catalog - with impl: srv/service.js 48 | [cds] - serving Stats at /stats - with impl: srv/service.js 49 | Service name: CatalogService 50 | Service name: Stats 51 | [cds] - launched in: 928.482ms 52 | [cds] - server listening on http://localhost:4004 ... 53 | [ terminate with ^C ] 54 | ``` 55 | 56 | The two lines containing "serving \ at \ ..." that we've seen before now have extra information showing that there's a JavaScript implementation that complements the service definition. 57 | 58 | Note that as the relationship between the service definition and the handler is at file level, the new `service.js` file is deemed a handler for both services (`CatalogService` and `Stats`) in that service definition file. In fact, we can see that two lines from the call to `console.log` confirm that - the function defined in the module is called twice - once for each service (the first time `srv.name` is "CatalogService", and the second time it's "Stats"). 59 | 60 | 61 | ### 4. Set a breakpoint and launch in debug mode 62 | 63 | The project already comes with some configuration that works with VS Code for debugging - if you're curious, have a look in the `.vscode/` directory in the root of the project. 64 | 65 | It means that you can easily set a breakpoint and launch the service in debug mode using standard VS Code features. 66 | 67 | :point_right: Before proceeding with the main part of this step, make sure the service is not running - go to the integrated terminal and stop it with Ctrl-C. 68 | 69 | :point_right: Now, set a breakpoint on the `console.log` line you added in the custom logic handler, by clicking in the margin to the left of the line numbers (or hitting F9 when on the line), to set a red mark as shown: 70 | 71 | ![breakpoint set](breakpoint-set.png) 72 | 73 | :point_right: Start the service in debug mode by using menu option "Debug -> Start Debugging", or simply hit F5. VS Code should switch to the debug perspective on the left hand side, and the service should start running, pausing at the `console.log` line as shown: 74 | 75 | ![running in debug mode](running-debug.png) 76 | 77 | At this point you can explore a little bit. 78 | 79 | :point_right: Switch to the Debug Console (next to the integrated terminal) and examine the `srv` object, which reflects a rich API. Try examining the values of the following, by typing them into the Debug Console input area. 80 | 81 | ![Debug Console](debug-console.png) 82 | 83 | ```javascript 84 | srv.name 85 | ``` 86 | 87 | ```javascript 88 | Object.keys(srv.entities) 89 | ``` 90 | 91 | ```javascript 92 | srv.path 93 | ``` 94 | 95 | :point_right: Use the debug control buttons to continue: 96 | 97 | ![debug control buttons](debug-buttons.png) 98 | 99 | 100 | :point_right: When you've finished exploring, use the "Stop" debug control button to terminate the service. 101 | 102 | 103 | ### 5. Add custom logic 104 | 105 | At this point we're confident enough to start adding custom logic, by registering custom handlers. The custom logic should cause a discount message ("5% off!") to appear with the titles of books that are highly stocked (and therefore are those which we want to discount in order to get rid of). 106 | 107 | :point_right: Add the following code directly after the call to `console.log` in the `service.js` file. As you do, notice in the code the comments that the custom logic is implemented in two different ways, using two different programming styles - you only need one of them. Comment out (or delete) one of them, leaving the one you prefer: 108 | 109 | ```js 110 | if (srv.name === 'CatalogService') { 111 | 112 | srv.after ('READ', 'Books', xs => { 113 | 114 | // CHOOSE ONLY ONE OF THESE ... 115 | // AND LET US KNOW YOUR PREFERENCE AND WHY! :-) 116 | 117 | // option 1 start 118 | xs.map(x => x.stock > 500 && (x.title = `(5% off!) ${x.title}`)) 119 | // option 1 end 120 | 121 | // option 2 start 122 | let newBooks = []; 123 | xs.forEach(x => { 124 | if (x.stock > 500) { 125 | x.title = '(5% off!) ' + x.title 126 | } 127 | newBooks.push(x) 128 | }) 129 | return newBooks 130 | // option 2 end 131 | 132 | }) 133 | 134 | } 135 | ``` 136 | 137 | 138 | :point_right: Restart the service (you can choose to do it normally or in debug mode so you can explore with breakpoints in this code) and check that the [titles for certain books](http://localhost:4004/catalog/Books) have been modified to show a discount, like this: 139 | 140 | ![discount showing](discount.png) 141 | 142 | 143 | ## Summary 144 | 145 | You have added custom logic and learned how to debug a service in VS Code. The options available for adding custom logic are rich and plentiful - we recommend you look further into the [documentation](https://cap.cloud.sap/docs/guides/service-impl) for more information. 146 | 147 | 148 | ## Questions 149 | 150 | 1. What other hooks do you think might be useful in customizing a service? 151 | 152 | 153 | 2. How many times is the function (that is supplied to the `after` hook) called? 154 | 155 | -------------------------------------------------------------------------------- /exercises/08/running-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/08/running-debug.png -------------------------------------------------------------------------------- /exercises/09/app-directory.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/09/app-directory.png -------------------------------------------------------------------------------- /exercises/09/browse-books-app.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/09/browse-books-app.png -------------------------------------------------------------------------------- /exercises/09/empty-fiori-launchpad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/09/empty-fiori-launchpad.png -------------------------------------------------------------------------------- /exercises/09/empty-table.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/09/empty-table.png -------------------------------------------------------------------------------- /exercises/09/launchpad-with-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/09/launchpad-with-tile.png -------------------------------------------------------------------------------- /exercises/09/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 09 - Introducing an app at the UI layer 2 | 3 | In this exercise you'll add a UI layer, by adding annotations that can drive aspects of a user interface (UI), and introducing a Fiori Elements based app. 4 | 5 | 6 | ## Steps 7 | 8 | Following these steps, you'll build a simple Fiori app that sits in a local Fiori launchpad environment, and that serves up book details from the `CatalogService` OData service, helped by annotations that you'll be specifying. 9 | 10 | 11 | ### 1. Introduce a basic HTML page to be served for the UI 12 | 13 | Following the "convention over configuation" theme, the Node.js flavored CAP model will also automatically serve static resources (such as UI artefacts) from a directory called `app/`. 14 | 15 | If there isn't anything that can be sensibly served in the `app/` directory it will serve the "Welcome to cds.services" landing page we've seen already: 16 | 17 | ![the "Welcome to cds.services" landing page](../07/two-services.png) 18 | 19 | :point_right: Create the `webapp/` directory as a child of the existing `app/` directory, and create an `index.html` file within it, containing the following: 20 | 21 | ```html 22 | 23 | 24 | 25 | 26 | 27 | 28 | Bookshop 29 | 30 | 31 | 32 | 33 | 34 | ``` 35 | 36 | :point_right: Restart `cds watch` and go to the URL [http://localhost:4004/webapp](http://localhost:4004/webapp). Here, while the the page itself looks empty, there is the page title "Bookshop" in the browser tab, that shows us that the HTML we entered has been loaded: 37 | 38 | ![title in browser tab](title-in-browser-tab.png) 39 | 40 | 41 | ### 2. Add a Fiori sandbox environment to the UI index page 42 | 43 | Let's now add a bit more to the contents of the `index.html` file. To create a sandbox Fiori launchpad we'll need the UI5 runtime as well as artefacts from the `test-resources` area of the toolkit. 44 | 45 | > While you have `cds watch` running, you may notice that it's not looking out for changes to HTML files, but that doesn't actually matter, as with HTML changes, the server doesn't need to be restarted. So you can make these following changes and simply switch over to the browser to refresh. 46 | 47 | :point_right: Add these `script` elements between the `title` element and the end of the `head` element in `index.html`: 48 | 49 | ```html 50 | 57 | 58 | 60 | 61 | 68 | 69 | 74 | ``` 75 | 76 | Here's a brief summary of what each of these `script` elements are for, in order of appearance in the file: 77 | 78 | 1. Basic configuration for the Fiori launchpad sandbox (otherwise known as the "universal shell" or "ushell") 79 | 1. Loading of the actual Fiori launchpad sandbox itself 80 | 1. Loading and bootstrapping of SAPUI5 81 | 1. Some JavaScript to declare a function to run when the initialization of SAPUI5 is complete; the function creates a launchpad and places it into the Document Object Model 82 | 83 | Reloading the browser tab should now show the beginnings of something recognizable as a Fiori launchpad, like this: 84 | 85 | ![an empty Fiori launchpad](empty-fiori-launchpad.png) 86 | 87 | ### 3. Introduce a basic UI app to the Fiori launchpad 88 | 89 | Now we have the launchpad as a container for our app, let's introduce it gradually. 90 | 91 | The first thing to do is to add an entry to the sandbox launchpad configuration to define a tile and the app to which it should be connected. 92 | 93 | :point_right: Do this by adding this "browse-books" section to the `applications` property in the "sap-ushell-config" - remember that this is inside the first `script` element in the file (context is shown): 94 | 95 | ```html 96 | 110 | ``` 111 | 112 | Reloading the index page in the browser should show something like this: 113 | 114 | ![Fiori launchpad with tile](launchpad-with-tile.png) 115 | 116 | 117 | ### 4. Create the app artefacts 118 | 119 | As we can see from the configuration we've just added, we're suggesting the app is a Component-based app (where the component name is "bookshop") and is to be found at (relative) URL `/webapp`. Let's flesh that out in terms of directories and files now. 120 | 121 | 122 | 123 | :point_right: In the new `webapp/` directory, create a simple `Component.js` file (note the capitalization of the filename) with the following content: 124 | 125 | ```js 126 | sap.ui.define( 127 | ['sap/fe/core/AppComponent'], 128 | ac => ac.extend('bookshop.Component', { 129 | metadata: { 130 | manifest: 'json' 131 | } 132 | }) 133 | ) 134 | ``` 135 | 136 | This is a modern UI5 component definition that points to a JSON configuration file (a manifest). 137 | 138 | :point_right: Create the corresponding manifest file `manifest.json` in the same directory as `Component.js`, with the following content: 139 | 140 | ```json 141 | { 142 | "_version": "1.8.0", 143 | "sap.app": { 144 | "id": "bookshop", 145 | "type": "application", 146 | "title": "Browse Books", 147 | "description": "Sample Application", 148 | "i18n": "i18n/i18n.properties", 149 | "dataSources": { 150 | "CatalogService": { 151 | "uri": "/catalog/", 152 | "type": "OData", 153 | "settings": { 154 | "odataVersion": "4.0" 155 | } 156 | } 157 | } 158 | }, 159 | "sap.ui5": { 160 | "dependencies": { 161 | "libs": {} 162 | }, 163 | "models": { 164 | "i18n": { 165 | "type": "sap.ui.model.resource.ResourceModel", 166 | "uri": "i18n/i18n.properties" 167 | }, 168 | "": { 169 | "dataSource": "CatalogService", 170 | "settings": { 171 | "synchronizationMode": "None", 172 | "operationMode": "Server", 173 | "autoExpandSelect": true, 174 | "earlyRequests": true, 175 | "groupProperties": { 176 | "default": { 177 | "submit": "Auto" 178 | } 179 | } 180 | } 181 | } 182 | }, 183 | "routing": { 184 | "routes": [{ 185 | "pattern": "", 186 | "name": "BooksList", 187 | "target": "BooksList" 188 | }, 189 | { 190 | "pattern": "Books({key})", 191 | "name": "BooksDetails", 192 | "target": "BooksDetails" 193 | } 194 | ], 195 | "targets": { 196 | "BooksList": { 197 | "type": "Component", 198 | "id": "BooksList", 199 | "name": "sap.fe.templates.ListReport", 200 | "options": { 201 | "settings": { 202 | "entitySet": "Books", 203 | "navigation": { 204 | "Books": { 205 | "detail": { 206 | "route": "BooksDetails" 207 | } 208 | } 209 | } 210 | } 211 | } 212 | }, 213 | "BooksDetails": { 214 | "type": "Component", 215 | "id": "BooksDetails", 216 | "name": "sap.fe.templates.ObjectPage", 217 | "options": { 218 | "settings": { 219 | "entitySet": "Books" 220 | } 221 | } 222 | } 223 | } 224 | } 225 | } 226 | } 227 | 228 | ``` 229 | 230 | Now you can open the "Browse Books" app and see the beginnings of a list report. 231 | 232 | ![empty table](empty-table.png) 233 | 234 | 235 | ### 5. Create a CDS index file with annotations 236 | 237 | This is the point where you can introduce an `index.cds` file which controls which services are exposed, and also which can contain annotations to drive the Fiori elements based app. 238 | 239 | :point_right: Create a file `index.cds` in the `srv/` directory, and initially add this single line, which brings in the service definitions defined in `service.cds`: 240 | 241 | ```cds 242 | using from './service'; 243 | ``` 244 | 245 | Now let's look at important content that will help us join together in our minds the two complementary worlds of CAP and Fiori. This content is to be added next to `index.cds` and controls what gets served to Fiori frontends, via annotations that form a rich layer of metadata over the top of the service. 246 | 247 | :point_right: Below the initial `using from ...` line, add the following content: 248 | 249 | ```cds 250 | annotate CatalogService.Books with @( 251 | UI: { 252 | Identification: [ {Value: title} ], 253 | SelectionFields: [ title ], 254 | LineItem: [ 255 | {Value: ID}, 256 | {Value: title}, 257 | {Value: author.name}, 258 | {Value: author_ID}, 259 | {Value: stock} 260 | ], 261 | HeaderInfo: { 262 | TypeName: '{i18n>Book}', 263 | TypeNamePlural: '{i18n>Books}', 264 | Title: {Value: title}, 265 | Description: {Value: author.name} 266 | } 267 | } 268 | ); 269 | 270 | annotate CatalogService.Books with { 271 | ID @title:'{i18n>ID}' @UI.HiddenFilter; 272 | title @title:'{i18n>Title}'; 273 | author @title:'{i18n>AuthorID}'; 274 | stock @title:'{i18n>Stock}'; 275 | } 276 | 277 | annotate CatalogService.Authors with { 278 | ID @title:'{i18n>ID}' @UI.HiddenFilter; 279 | name @title:'{i18n>AuthorName}'; 280 | } 281 | ``` 282 | 283 | > You may see some warnings that there are no texts for the internationalization (i18n) identifiers. We'll fix this shortly, you can ignore the warnings for now. 284 | 285 | 286 | ### 6. Test the app 287 | 288 | The app should be ready to invoke. Reload the Fiori launchpad and select the tile. It should open up into a nice List Report style Fiori Elements app - all driven from the service's annotations: 289 | 290 | ![the Browse Books app](browse-books-app.png) 291 | 292 | Well done! 293 | 294 | 295 | ### 7. Add base internationalization texts 296 | 297 | Just to round things off, add some i18n texts - they're referred to in various annotation sections, and it will make the app look a little more polished. 298 | 299 | :point_right: Create a directory called `i18n/` as a direct child of the `srv/` directory, and create a file `i18n.properties` inside it, with the following content: 300 | ``` 301 | ID=ID 302 | Title=Title 303 | Stock=Stock Available 304 | AuthorID=Author ID 305 | AuthorName=Author Name 306 | Book=Book 307 | Books=Books 308 | ``` 309 | 310 | :point_right: After the `cds watch` mechanism has restarted the server, refresh the app, and you should see the static texts as specified in the `i18n.properties` file, such as "Author Name" rather than "AuthorName". 311 | 312 | 313 | ## Summary 314 | 315 | While this was a little intense as far as creation of artefacts was concerned, we hope you agree that for little effort, and based on a great foundation, a lot can be achieved! 316 | 317 | ## Questions 318 | 319 | 1. Why do we put the internationalization file in the `srv/` directory (rather than the `app/` directory)? 320 | 321 | 322 | -------------------------------------------------------------------------------- /exercises/09/title-in-browser-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/SAP-archive/cloud-cap-nodejs-codejam/97bbde4b4a4eac2649306c3f3f3c7f2d6e2ee6d1/exercises/09/title-in-browser-tab.png -------------------------------------------------------------------------------- /exercises/10/readme.md: -------------------------------------------------------------------------------- 1 | # Exercise 10 - Deploy the app to Cloud Foundry 2 | 3 | 4 | In this exercise you'll make your project cloud-ready. In a cloud deployment, all modules will run as independent (but linked) applications. The modules, and the relationships between them, are described in the `mta.yaml` file in the project's root. There's one already there that's been generated because of the use of `--add mta` when the project was initialized in [exercise 02](../exercises/02/). In this exercise you'll make some modifications and additions to that file, and add a couple more resources too. 5 | 6 | 7 | ## Steps 8 | 9 | At the end of these steps, your project will be cloud ready, built and deployed to SAP Cloud Platform Cloud Foundry Environment. 10 | 11 | ### 1. Sign in to Cloud Foundry 12 | 13 | :point_right: Run the following command from your command line to log in to the referenced Cloud Foundry endpoint. When prompted use your SAP Cloud Platform credentials. 14 | 15 | ``` 16 | user@host:~/bookshop 17 | => cf login -a https://api.cf.eu10.hana.ondemand.com 18 | ``` 19 | 20 | ### 2. Tailor the HDI container declaration to the trial landscape 21 | 22 | We need to ensure that the correct HDI container will be used in the trial landscape. To do this, we need to make sure that the appropriate parameter is specified in the corresponding resource in `mta.yaml`. 23 | 24 | :point_right: Edit the `mta.yaml` file, and to the `parameters` section (which should be at the end of the file), add a line "`service: hanatrial`". Here's what the section should look like after the edit: 25 | 26 | ```yaml 27 | ############## RESOURCES ################################## 28 | resources: 29 | ##### Services extracted from CAP configuration #### 30 | ##### 'service-plan' can be configured via 'cds.requires..vcap.plan' 31 | - name: bookshop-db 32 | type: com.sap.xs.hdi-container 33 | 34 | parameters: 35 | service: hanatrial 36 | properties: 37 | hdi-service-name: ${service-name} # required for Java case 38 | ############################################################ 39 | ``` 40 | 41 | > Take care to get the whitespace and indentation right, and remember that there are no tabs allowed in YAML files! 42 | 43 | This step is only necessary when you want to deploy the project to the trial landscape. 44 | 45 | 46 | ### 3. Add a module definition to `mta.yaml` for the UI 47 | 48 | The `mta.yaml` file contains module definitions for the service and the database, but not (yet) for the Fiori-based UI. You'll do that now in this step. 49 | 50 | :point_right: Edit `mta.yaml` and add the following module definition, directly below the `modules:` line, and before the server module definition (`bookshop-srv`) which starts with a "#### SERVER MODULE ####" style comment line: 51 | 52 | ```yaml 53 | modules: 54 | ############## UI MODULE ########################## 55 | - name: bookshop-ui 56 | type: nodejs 57 | path: app 58 | parameters: 59 | memory: 256M 60 | disk-quota: 256M 61 | requires: 62 | - name: srv-binding 63 | group: destinations 64 | properties: 65 | forwardAuthToken: true 66 | strictSSL: true 67 | name: srv-binding 68 | url: ~{srv-url} 69 | 70 | ############## SERVER MODULE ########################## 71 | - name: bookshop-srv 72 | type: nodejs 73 | [...] 74 | ``` 75 | 76 | > The `bookshop-ui` module is shown here in context so you can see where to insert it - make sure you only add the lines for this module, and again, make sure you get the whitespace right. 77 | 78 | 79 | ### 4. Add a module descriptor file for the srv module 80 | 81 | You might have noticed that there is no descriptor for the `srv` module defined. For local development, such a descriptor is not needed as CAP knows how to parse those files. For the deployment to Cloud Foundry, on the other hand, a descriptor is required, to define the module dependencies and start commands. 82 | 83 | :point_right: Create a new `package.json` file in the `srv/` directory, and add the following content representing the server module, to make it cloud-ready: 84 | 85 | ```json 86 | { 87 | "name": "bookshop-srv", 88 | "version": "1.0.0", 89 | "dependencies": { 90 | "@sap/cds": "^3.18.4", 91 | "express": "^4.17.1", 92 | "@sap/hana-client": "^2.4.167" 93 | }, 94 | "engines": { 95 | "node": "^10" 96 | }, 97 | "scripts": { 98 | "start": "cds serve gen/csn.json" 99 | }, 100 | "cds": { 101 | "requires": { 102 | "db": { 103 | "kind": "hana", 104 | "model": "gen/csn.json" 105 | } 106 | } 107 | } 108 | } 109 | ``` 110 | 111 | ### 5. Add the app router configuration 112 | 113 | Similar to the `srv` module, we need to add a descriptor file for the `app` module as well. We will embed the UI source files into an app router, to be able to connect to the `srv` module and to forward requests to it. 114 | 115 | :point_right: Create a new `package.json` file in the `app/` directory with the following content, to start this module as an independent app router application within Cloud Foundry: 116 | 117 | ```json 118 | { 119 | "name": "bookshop-ui", 120 | "dependencies": { 121 | "@sap/approuter": "^6.6.0" 122 | }, 123 | "engines": { 124 | "node": "^10" 125 | }, 126 | "scripts": { 127 | "start": "node node_modules/@sap/approuter/approuter.js" 128 | } 129 | } 130 | ``` 131 | 132 | :point_right: Add a new file `xs-app.json` to the same directory (`app/`), with the following content, to configure the app router: 133 | 134 | ```json 135 | { 136 | "welcomeFile": "webapp/", 137 | "authenticationMethod": "none", 138 | "routes": [{ 139 | "source": "^/webapp/(.*)$", 140 | "target": "$1", 141 | "localDir": "webapp/" 142 | }, { 143 | "source": "^(.*)$", 144 | "destination": "srv-binding" 145 | }] 146 | } 147 | 148 | ``` 149 | 150 | This file not only defines the welcome page, but also defines which requests are forwarded to which Cloud Foundry application. 151 | 152 | 153 | ### 6. Specify HANA as a database in the configuration 154 | 155 | We're about to deploy to HANA in the form of an HDI container on the trial landscape of SAP Cloud Platform Cloud Foundry. This means we need to ensure we have the right configuration for the database, so that things get built correctly. This can be done in the `package.json` file. 156 | 157 | :point_right: In the root level `package.json` file, find the `cds -> requires -> db` node and add another section for `[production]`, like this: 158 | 159 | ```cds 160 | "[production]": { 161 | "kind": "hana" 162 | }, 163 | ``` 164 | 165 | ' 166 | so it then looks like this: 167 | 168 | ```cds 169 | "cds": { 170 | "requires": { 171 | "db": { 172 | "kind": "sqlite", 173 | "model": [ 174 | "db/", 175 | "srv/", 176 | "app/" 177 | ], 178 | "[production]": { 179 | "kind": "hana" 180 | }, 181 | "credentials": { 182 | "database": "db.db" 183 | } 184 | } 185 | } 186 | }, 187 | ``` 188 | 189 | The "hana" value of "kind" will now be used in place of "sqlite" when the "production" profile is used. See the [Runtime Configuration for Node.js](https://cap.cloud.sap/docs/advanced/config) section of the CAP documentation for more details. 190 | 191 | 192 | ### 7. Add npm scripts to trigger the deployment 193 | 194 | So far, the `package.json` file in your project root only defines scripts for local project execution. 195 | 196 | 197 | :point_right: Add the following script definition to the `"scripts"` section of the **project's root `package.json`** for build step processes: 198 | 199 | ```json 200 | "build:mta": "cds build/all && mbt build -p=cf" 201 | ``` 202 | 203 | You might have noticed, that the `mbt` command isn't a typical shell command. This is actually a command from another Node.js package. `mbt` allows you to package your project into a deployable archive, which you'll need in order to get the app onto the SAP Cloud Platform. 204 | 205 | :point_right: Install this package locally in your project to allow its usage in the npm scripts, like this: 206 | 207 | ``` 208 | user@host:~/bookshop 209 | => npm install mbt 210 | ``` 211 | 212 | ### 8. Build the project 213 | We're almost there. To make our project ready for deployment, we need to package it into a single archive which can be used a delivery unit. 214 | 215 | :point_right: Trigger the build process with the following command, specifying "production" for the `NODE_ENV` environment variable: 216 | 217 | ``` 218 | NODE_ENV=production npm run build:mta 219 | ``` 220 | 221 | If you're running Windows then you'll need to set the environment variable first with `set` and then run the command, like this: 222 | 223 | ``` 224 | set NODE_ENV production 225 | npm run build:mta 226 | ``` 227 | 228 | > If you want to run the two commands (`cds build/all` and `mbt build -p=cf`) manually, one after the other, to see what they do, note that you'll have to use `npx` to run the `mbt` command, like this: `npx mbt build -p=cf`. In fact, the latest version of `mbt` has the value `cf` as the default for the `-p` ('platform') flag, so you don't actually need to specify this explicitly. 229 | 230 | 231 | ### 9. Deploy the archive 232 | 233 | Now you should see a new directory `mta_archives/` which contains an archive file named with the `ID` and `version` we defined in the `mta.yaml` descriptor. One command is all it takes to deploy your project to the cloud. 234 | 235 | :point_right: Execute the following command to trigger the deployment process: 236 | 237 | ``` 238 | user@host:~/bookshop 239 | => cf deploy mta_archives/bookshop_1.0.0.mtar 240 | ``` 241 | 242 | 243 | ### 10. Check the apps and services in Cloud Foundry 244 | 245 | You can and should check the status of what you've deployed to your trial Cloud Foundry environment on the SAP Cloud Platform. 246 | 247 | Use the following commands do to this: 248 | 249 | ``` 250 | cf apps 251 | cf services 252 | ``` 253 | 254 | ## Summary 255 | 256 | You have learned the basic commands to interact with the Cloud Foundry Command Line Interface to check the state of the deployed applications. You also added the necessary scripts to your project to automate the build and deploy steps via the command line. 257 | 258 | ## Questions 259 | 260 | 1. Can you guess what the second subcommand of the `build:mta` script does? Are you able to run this command (`mbt build -p=cf`) straight from the terminal? 261 | 262 | 263 | 2. When you run the commands to check the apps and services, what do you see? Are all apps in the "Running" state? 264 | 265 | 266 | -------------------------------------------------------------------------------- /prerequisites.md: -------------------------------------------------------------------------------- 1 | # Prerequisites 2 | 3 | There are hardware and software prerequisites for participating in this CodeJam. There are also some optional, recommended items that you might like to have too. 4 | 5 | Administration rights are an essential requirement due to the installation of some local tools. 6 | 7 | Please ensure you complete all the prerequisites **before the day of the CodeJam**. 8 | 9 | ## Hardware 10 | 11 | Each participant should have their own laptop with enough admin privileges to be able to install software. You should also be able to access a command line shell (e.g. `cmd.exe` on Windows). The laptop should also be able to connect to the host organization's guest network for Internet access, and have enough power for the whole day. 12 | 13 | ## SAP Cloud Platform 14 | 15 | Each attendee must have an SAP Cloud Platform trial account, and specifically a Cloud Foundry environment with an organization and a space defined. See the following tutorials for more details: 16 | 17 | - [Get a Free Trial Account on SAP Cloud Platform](https://developers.sap.com/tutorials/hcp-create-trial-account.html) 18 | - [Log in via the Cloud Foundry Command Line Interface](https://developers.sap.com/tutorials/cp-cf-download-cli.html) 19 | 20 | ## Software 21 | 22 | There are some mandatory and optional requirements with respect to software. The installation instructions are dependent on the operating system. Please ensure you follow instructions according to your operating system in the subsections below, to have the mandatory software installed on your laptops **before** the day of the CodeJam. 23 | 24 | ### Windows - Mandatory 25 | 26 | Some of these mandatory software requirements are to be installed manually (directly), others via the [Chocolatey](https://chocolatey.org/) package manager. 27 | 28 | First, install the following tools manually: 29 | 30 | - Chrome (latest version): 31 | - Visual Studio Code (also known as VS Code): 32 | - Postman : 33 | 34 | Next, install Chocolatey. Therefore open a command prompt as an **administrator**. 35 | 36 | ![Open command prompt as administrator](command-prompt-admin.png) 37 | 38 | Execute the following command in this recently opened command prompt to install Chocolatey: 39 | 40 | ```bash 41 | @"%SystemRoot%\System32\WindowsPowerShell\v1.0\powershell.exe" -NoProfile -InputFormat None -ExecutionPolicy Bypass -Command "iex ((New-Object System.Net.WebClient).DownloadString('https://chocolatey.org/install.ps1'))" && SET "PATH=%PATH%;%ALLUSERSPROFILE%\chocolatey\bin" 42 | ``` 43 | 44 | 45 | Use Chocolatey to install Node.js Long Term Support (LTS) version, SQLite, make, curl (command-line client for URLs), jq (lightweight and flexible command-line JSON processor), Cloud Foundry command line interface (CLI) and the Microsoft Build Tools: 46 | ```bash 47 | choco install -y nodejs-lts sqlite make curl jq cloudfoundry-cli 48 | ``` 49 | 50 | Install Windows Build Tools: 51 | ```bash 52 | npm install --global windows-build-tools 53 | ``` 54 | 55 | Next, use the Cloud Foundry CLI to install a plugin to deploy your MultiTarget Application (MTA) to Cloud Foundry: 56 | ```bash 57 | cf install-plugin multiapps 58 | ``` 59 | 60 | Once you're done installing, please ensure you can successfully start the executables `sqlite3`, `make`, `cf` and `node` from the command line. 61 | 62 | 63 | ### macOS/Linux - Mandatory 64 | 65 | Please install the following tools manually: 66 | 67 | - Chrome (latest version): https://www.google.com/chrome/ 68 | - Visual Studio Code (also known as VS Code): https://code.visualstudio.com/download 69 | - Node.js (latest LTS version 10): https://nodejs.org/en/download/ 70 | - Postman : https://www.getpostman.com/downloads/ 71 | - The Cloud Foundry command line tool: https://github.com/cloudfoundry/cli/releases 72 | 73 | Furthermore, use the Cloud Foundry CLI to install the `multiapps`plugin. This plugin enables the Cloud Foundry CLI to deploy your Multi-Target Application (MTA) to Cloud Foundry. To do so, execute the following command in the Terminal. 74 | 75 | ```bash 76 | cf install-plugin multiapps 77 | ``` 78 | 79 | ### Optional items 80 | 81 | Some of the exercises require you to make HTTP requests, and for this you can use Postman (a mandatory software requirement above). 82 | 83 | Alternatively you can also use `curl`, a command line HTTP client. Instructions for the HTTP requests in each exercise are given for both Postman and `curl`. To install `curl` visit [https://curl.haxx.se/](https://curl.haxx.se/) and follow the [download instructions](https://curl.haxx.se/download.html) (Windows users can install `curl` with Chocolatey too: `choco install curl`). 84 | 85 | 86 | Further to the software prerequisites described above, we also recommend a couple of Chrome extensions, both of which make viewing HTTP responses in JSON and XML more pleasant: 87 | 88 | - The [JSON Formatter extension](https://chrome.google.com/webstore/detail/json-formatter/bcjindcccaagfpapjjmafapmmgkkhgoa?hl=en) 89 | - The [XML Tree extension](https://chrome.google.com/webstore/detail/xml-tree/gbammbheopgpmaagmckhpjbfgdfkpadb) 90 | 91 | ## Attendees 92 | 93 | Some familiarity with JavaScript is strongly recommended. Existing familiarity with Core Data Services (CDS) concepts is an advantage, as is experience with working with command line tools. 94 | 95 | Attendees wishing to prepare themselves for the day can take advantage of the tutorial [Create a Basic Node.js App](https://developers.sap.com/tutorials/cp-node-create-basic-app.html) in the SAP Developer Center. 96 | --------------------------------------------------------------------------------