diff --git a/README.md b/README.md index 759623c..c6dd198 100644 --- a/README.md +++ b/README.md @@ -33,7 +33,7 @@ Here's an overview of the exercises in this CodeJam. - [Exercise 07 - Defining a second service](exercises/07/) - [Exercise 08 - Adding custom logic, and debugging](exercises/08/) - [Exercise 09 - Introducing an app at the UI layer](exercises/09/) -- [Exercise 10 - (Bonus) Deploy a simple project to the Cloud](exercises/10/) +- [Exercise 10 - Deploy a simple project to the Cloud](exercises/10/) ### Feedback @@ -60,5 +60,3 @@ Support for the content in this repository is available during CodeJam events, f Copyright (c) 2019 SAP SE or an SAP affiliate company. All rights reserved. This file is licensed under the SAP Sample Code License except as noted otherwise in the [LICENSE](LICENSE) file. - - diff --git a/exercises/01/readme.md b/exercises/01/readme.md index bd6b923..f5a20bc 100644 --- a/exercises/01/readme.md +++ b/exercises/01/readme.md @@ -81,6 +81,12 @@ When successfully installed, you should see the extension thus: ![CDS Language Support extension installed in VS Code](vscode-extension.png) +### 3. Install the MultiApp Build Tool (mbt) +This tool allows you in package your project into a deployable archieve. +``` +npm install -g mbt +``` + ## Summary You've now installed the two key tools for developing with CAP locally, and are all set to create your first project. @@ -94,4 +100,3 @@ You've now installed the two key tools for developing with CAP locally, and are 1. What is the significance of using the `--global` option when installing the `@sap/cds` package? 1. What is the meaning of the `.vsix` file type for the VS Code extension? Can we dig into that to see what's inside? - diff --git a/exercises/02/basic-odata-service.png b/exercises/02/basic-odata-service.png index 1e4b96d..54f4b20 100644 Binary files a/exercises/02/basic-odata-service.png and b/exercises/02/basic-odata-service.png differ diff --git a/exercises/02/initialized-project-in-vscode.png b/exercises/02/initialized-project-in-vscode.png index df71215..927ea38 100644 Binary files a/exercises/02/initialized-project-in-vscode.png and b/exercises/02/initialized-project-in-vscode.png differ diff --git a/exercises/02/integrated-terminal-in-view.png b/exercises/02/integrated-terminal-in-view.png index 43659c8..43b7ae0 100644 Binary files a/exercises/02/integrated-terminal-in-view.png and b/exercises/02/integrated-terminal-in-view.png differ diff --git a/exercises/02/readme.md b/exercises/02/readme.md index 78017f0..61400ce 100644 --- a/exercises/02/readme.md +++ b/exercises/02/readme.md @@ -65,31 +65,30 @@ user@host:~ => cds init --help ``` -Amongst other things, you should see a `--modules` option to specify a list of modules to be created when the project is initialized, and also a `--verbose` option. +Amongst other things, you should see a `--modules` option to specify a list of modules to be created when the project is initialized, and also a `--verbose` option. The options `--mta`, `--db-technology` and `--insecure` are related to deployment to Cloud Foundry and its access management. -:point_right: Use both of these options to initialize a new project directory thus: +:point_right: Use all of these options to initialize a new project directory thus: ```sh user@host:~ -=> cds init --modules db,srv --verbose bookshop +=> cds init --modules db,srv --mta --insecure --db-technology hana --verbose bookshop ``` You should see output that looks similar to this: -```sh -Creating new project in directory bookshop. - -Copying templates for type db to bookshop/db ... -Copying templates for type srv to bookshop/srv ... -Updating npm dependencies in bookshop/package.json ... +``` +[Warn] Srv type nodejs only supports odata version v4 and not v2. Will use v4. +Initializing project in folder bookshop. +Copying templates for type db to db ... +Creating mta file /Users/d056949/SoftwareDev/sandbox/bookshop/mta.yaml ... +Copying templates for type srv to {{moduleFolder}} ... +Creating mta file /Users/d056949/SoftwareDev/sandbox/bookshop/mta.yaml ... +Updating npm dependencies in /Users/d056949/SoftwareDev/sandbox/bookshop/package.json ... Running npm install... -npm WARN bookshop@1.0.0 license should be a valid SPDX license expression - -added 76 packages from 109 contributors and audited 166 packages in 3.261s +added 81 packages from 111 contributors and audited 176 packages in 2.976s found 0 vulnerabilities - -Project creation was successful. +Finished successfully. ``` @@ -123,6 +122,7 @@ Briefly, the directories and contents can be described thus: | `db` | Where the data models (in CDS) are specified. A skeleton CDS project that has been initialized with the `--modules db` option will have a basic data model file in the form of `data-model.cds` with a small sample definition, like here | | `node_modules` | This is the normal place where NPM packages (modules) are to be found in a Node.js based project | | `srv` | Where the service definitions (in CDS) are specified. A skeleton CDS project that has been initialized with the `--modules srv` option will have a basic service definition file in the form of `cat-service.cds` with a small service definition, like here | +| `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 (a) to build the .mtar archive during design time and to deploy and provision the apps and services during deploy time. | Besides the directories there are also a number of files, including the project's `package.json` (present in any Node.js based project) and a readme file. @@ -158,14 +158,15 @@ Note: You can also specify simply `cds serve all` to have `cds` look for appropr You should see output similar to this: ``` -[cds] - server listening at http://localhost:4004 +[cds] - connect to datasource - hana:db,srv [cds] - serving CatalogService at /catalog [cds] - service definitions loaded from: srv/cat-service.cds db/data-model.cds -[cds] - launched in: 448.633ms +[cds] - server listens at http://localhost:4004 ... (terminate with ^C) +[cds] - launched in: 566.451ms ``` The OData service is now running, and available on [http://localhost:4004](http://localhost:4004). @@ -183,7 +184,7 @@ The [catalog](http://localhost:4004/catalog) link will take you to the service d :point_right: Explore the metadata document and familiarize yourself with the content. Note the entityset definition and the entity type describing the `Books` entity. Note also the annotations describing particular service capabilities. -: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. +: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 process when you try to access the entityset. ## Summary @@ -200,3 +201,5 @@ With a single command, you've initialized a basic OData service project and with 1. What are the annotations in the metadata document describing, and where do they come from? 1. What does the Books entityset resource contain right now? + +1. What happened to the node process when you accessed the entityset? Can you think of reasons why this happened? diff --git a/exercises/03/readme.md b/exercises/03/readme.md index 13ba2bc..7e3b6c5 100644 --- a/exercises/03/readme.md +++ b/exercises/03/readme.md @@ -105,7 +105,50 @@ Don't forget to save the file. As it stands, the OData service has no storage. We can actually simulate storage with [service provider](https://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/b9c34890348b4f2184e07a6731bce50b.html) 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. -_Note: From cds 3.10 onwards, the `sqlite3` package is automatically installed in the project for you as a development dependency so it's all ready for you to start using._ +:point_right: Update the database definition in `package.json` to include a SQLite DB for local testing. This will fix the issue you encountered before when the node process crashed. Currently, you'll see a section that describes the persistence layer configuration: + +```json +"cds": { + "requires": { + "db": { + "kind": "hana", + "model": [ + "db", + "srv" + ] + } + }, + "odata": { + "version": "v4" + } +} +``` +To prepare the app for a multiple databases, change the content to: +```json +"cds": { + "requires": { + "db": { + "kind": "sqlite", + "model": ["db", "srv"], + "credentials": { + "database": "bookshop.db" + }, + "[production]": { + "kind": "hana" + } + } + }, + "odata": { + "version": "v4" + } +} +``` + +:point_right: As we want to use a local SQLite database, we need to install a client to communicate with this DB. Install the `sqlite3` package for this job. +``` +npm install -D sqlite3 +``` + :point_right: Explore the `cds deploy` command like this: @@ -128,34 +171,10 @@ Use this command to deploy the data model and service definition to a new SQLite ``` user@host:~/bookshop -=> cds deploy --to sqlite:bookshop.db -``` - -This should complete fairly quietly, and give a message like this: - -``` -- updated package.json -``` - -Note: If you're wondering what is been updated in `package.json` relating to the use of SQLite, have a look. You'll see a section that describes the persistence layer configuration: - -```json - "cds": { - "requires": { - "db": { - "kind": "sqlite", - "model": [ - "db", - "srv" - ], - "credentials": { - "database": "bookshop.db" - } - } - } - } +=> cds deploy ``` +This should complete fairly quietly. ### 5. Explore the new database diff --git a/exercises/04/csv-files.png b/exercises/04/csv-files.png index ab2ccb8..8fbdef9 100644 Binary files a/exercises/04/csv-files.png and b/exercises/04/csv-files.png differ diff --git a/exercises/04/my.bookshop-Authors.csv b/exercises/04/my.bookshop-Authors.csv index c4b02d2..9560d13 100644 --- a/exercises/04/my.bookshop-Authors.csv +++ b/exercises/04/my.bookshop-Authors.csv @@ -1,4 +1,4 @@ -ID,name +ID,NAME 42,Douglas Adams 101,Emily Brontë 107,Charlote Brontë diff --git a/exercises/04/my.bookshop-Books.csv b/exercises/04/my.bookshop-Books.csv index 0fe3856..1c09a07 100644 --- a/exercises/04/my.bookshop-Books.csv +++ b/exercises/04/my.bookshop-Books.csv @@ -1,4 +1,4 @@ -ID,title,author_ID,stock +ID,TITLE,AUTHOR_ID,STOCK 421,The Hitch Hiker's Guide To The Galaxy,42,1000 427,"Life, The Universe And Everything",42,95 201,Wuthering Heights,101,12 diff --git a/exercises/04/readme.md b/exercises/04/readme.md index 0d7e6ff..8c77405 100644 --- a/exercises/04/readme.md +++ b/exercises/04/readme.md @@ -39,7 +39,56 @@ During deployment this time you should see an extra message: ``` -### 3. Restart the service +### 3. Add config files for HANA +The `.hdiconfig` file specifies the configuration of the used HDI container. . + +:point_right: Change the following property of the `db/src/.hdiconfig` file to adapt the version to the HANA version used in the SAP Cloud Platform Cloud Foundry trial. +``` +"plugin_version": "12.1.0", +``` + +While SQLite automatically imports the CSV files, we need to provide more information for HANA to import those files. These import instructions are encoded in a `.hdbtabledata` file and will be read during the deployment process to the Cloud Foundry environment. + + :point_right: Create a new `db/csv/Data.hdbtabledata` file with the following content to specify which data should be imported into the HANA tables. +``` +{ + "format_version": 1, + "imports": [ + { + "target_table": "MY_BOOKSHOP_AUTHORS", + "source_data": { + "data_type": "CSV", + "file_name": "my.bookshop-Authors.csv", + "has_header": true + }, + "import_settings": { + "import_columns": [ + "ID", + "NAME" + ] + } + }, + { + "target_table": "MY_BOOKSHOP_BOOKS", + "source_data": { + "data_type": "CSV", + "file_name": "my.bookshop-Books.csv", + "has_header": true + }, + "import_settings": { + "import_columns": [ + "ID", + "TITLE", + "AUTHOR_ID", + "STOCK" + ] + } + } + ] +} +``` + +### 4. Restart the service :point_right: Terminate and then restart the service thus: @@ -53,7 +102,7 @@ Now the [Books](http://localhost:4004/catalog/Books) and [Authors](http://localh ![Books and Authors in the OData service](books-and-authors.png) -### 4. Try out some OData query operations +### 5. Try out some OData query operations 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. diff --git a/exercises/05/readme.md b/exercises/05/readme.md index 30e5538..08c4cfe 100644 --- a/exercises/05/readme.md +++ b/exercises/05/readme.md @@ -52,14 +52,16 @@ user@host:~/bookshop user@host:~/bookshop => cds serve all -[cds] - server listening at http://localhost:4004 +[cds] - connect to datasource - sqlite:bookshop.db [cds] - serving CatalogService at /catalog [cds] - service definitions loaded from: srv/cat-service.cds db/data-model.cds + node_modules/@sap/cds/common.cds -[cds] - launched in: 834.496ms +[cds] - server listens at http://localhost:4004 ... (terminate with ^C) +[cds] - launched in: 821.718ms ``` The `Orders` entity is now available in the service (but there is [no data](http://localhost:4004/catalog/Orders) as yet). @@ -138,7 +140,7 @@ user@host:~/bookshop => cds deploy && cds serve all > initializing from csv files at ./db/csv... -[cds] - server listening at http://localhost:4004 +[cds] - connect to datasource - sqlite:bookshop.db [cds] - serving CatalogService at /catalog [cds] - service definitions loaded from: @@ -146,7 +148,8 @@ user@host:~/bookshop db/data-model.cds node_modules/@sap/cds/common.cds -[cds] - launched in: 629.661ms +[cds] - server listens at http://localhost:4004 ... (terminate with ^C) +[cds] - launched in: 824.746ms ``` _Note: If you're running Windows, you can use a semicolon in place of the `&&`. It's not quite the same but will work just fine in most cases._ diff --git a/exercises/06/readme.md b/exercises/06/readme.md index 7f722e5..14ab6fe 100644 --- a/exercises/06/readme.md +++ b/exercises/06/readme.md @@ -106,7 +106,7 @@ The request is an OData Create request for a new book. You should see that this You should also see a line in the terminal (where you invoked `cds serve all`) like this: ``` -[2019-03-26T06:39:23.025Z | WARNING | 1369756]: An error occurred: Method Not Allowed +[2019-05-29T15:16:39.694Z | WARNING | 1939700]: Method Not Allowed ``` :point_right: Next, try out the second request in that same folder - it's an OData Delete operation, to remove a book. diff --git a/exercises/07/two-services.png b/exercises/07/two-services.png index 3de9ec1..e0c8c9d 100644 Binary files a/exercises/07/two-services.png and b/exercises/07/two-services.png differ diff --git a/exercises/08/readme.md b/exercises/08/readme.md index 105692d..1d6a125 100644 --- a/exercises/08/readme.md +++ b/exercises/08/readme.md @@ -53,18 +53,19 @@ You should see a few interesting lines in the output, highlighted here: user@host:~/bookshop => cds serve all -[cds] - server listening at http://localhost:4004 -[cds] - serving CatalogService at /catalog - impl: cat-service.js <-- -[cds] - serving Stats at /stats - impl: cat-service.js <-- -Service name: CatalogService <-- -Service name: Stats <-- +[cds] - connect to datasource - sqlite:bookshop.db +Service name: CatalogService +Service name: Stats +[cds] - serving CatalogService at /catalog - impl: cat-service.js +[cds] - serving Stats at /stats - impl: cat-service.js [cds] - service definitions loaded from: - db/data-model.cds srv/cat-service.cds + db/data-model.cds node_modules/@sap/cds/common.cds -[cds] - launched in: 720.034ms +[cds] - server listens at http://localhost:4004 ... (terminate with ^C) +[cds] - launched in: 1125.468ms ``` The first two ("serving \ at \ ...") that we've seen before now have extra information showing that there's a JavaScript implementation that complements the service definition. @@ -135,6 +136,38 @@ At this point we're confident enough to start adding custom logic, by [registeri ![discount showing](discount.png) +### 6. Add a independent project descriptor +You might have noticed that there is no module descriptor for the server module defined. For the local development, such a descriptor is not needed as CDS knows how to parse those files. For the deployment to Cloud Foundry, on the other hand, such a file is required to define the module dependencies and start commands. + +:point_right: Add a new `package.json` file with the following content to the `svr` module to make it cloud-ready. +```json +{ + "name": "project-srv", + "version": "1.0.0", + "dependencies": { + "@sap/cds": "^3.10.0", + "express": "^4.16.4", + "hdb": "^0.17.0" + }, + "engines": { + "node": "^10" + }, + "scripts": { + "postinstall": "npm dedupe", + "start": "cds serve gen/csn.json" + }, + "cds": { + "requires": { + "db": { + "kind": "hana", + "model": "gen/csn.json" + } + } + } +} + +``` + ## Summary 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://help.sap.com/viewer/65de2977205c403bbc107264b8eccf4b/Cloud/en-US/94c7b69cc4584a1a9dfd9cb2da295d5e.html) for more information. @@ -147,4 +180,3 @@ You have added custom logic and learned how to debug a service in VS Code. The o 1. What is the command used in the launch configuration for starting the service in debug mode - is it `cds serve all`? 1. How many times is the function (that is supplied to the `after` hook) called? - diff --git a/exercises/09/browse-books-app.png b/exercises/09/browse-books-app.png index af9d8d9..099de71 100644 Binary files a/exercises/09/browse-books-app.png and b/exercises/09/browse-books-app.png differ diff --git a/exercises/09/empty-fiori-launchpad.png b/exercises/09/empty-fiori-launchpad.png index 313e295..3815fa3 100644 Binary files a/exercises/09/empty-fiori-launchpad.png and b/exercises/09/empty-fiori-launchpad.png differ diff --git a/exercises/09/launchpad-with-tile.png b/exercises/09/launchpad-with-tile.png index 619bcfa..464e461 100644 Binary files a/exercises/09/launchpad-with-tile.png and b/exercises/09/launchpad-with-tile.png differ diff --git a/exercises/09/readme.md b/exercises/09/readme.md index b33acef..217aa57 100644 --- a/exercises/09/readme.md +++ b/exercises/09/readme.md @@ -16,7 +16,10 @@ Following the "convention over configuation" theme, the Node.js flavored CAP mod If there is an `app/` directory with content, it will serve that instead. -:point_right: Create an `app/` directory, at the same level as the `db/` and `srv/` directories, and create an `index.html` file within it, containing the following: +:point_right: Create an `app/` directory, at the same level as the `db/` and `srv/` directories. This directory that will contain the app files. + + +:point_right: Create the `webapp/` directory as a child of the `app/` and create an `index.html` file within it, containing the following. ```html @@ -33,7 +36,7 @@ If there is an `app/` directory with content, it will serve that instead. ``` -:point_right: Restart the service (with `cds serve all`) and go to the root URL, i.e. [http://localhost:4004](http://localhost:4004). This time, you are not shown the "Welcome to cds.services" landing page - instead, the page is empty, except for the page title in the browser tab, that shows us that the HTML we entered has been loaded: +:point_right: Restart the service (with `cds serve all`) and go to the URL, i.e. [http://localhost:4004/webapp](http://localhost:4004/webapp). Here, you are not shown the "Welcome to cds.services" landing page - instead, the page is empty, except for the page title in the browser tab, that shows us that the HTML we entered has been loaded: ![title in browser tab](title-in-browser-tab.png) @@ -43,7 +46,6 @@ If there is an `app/` directory with content, it will serve that instead. To create a sandbox Fiori launchpad we'll need the UI5 runtime as well as artefacts from the `test-resources` area of the toolkit. :point_right: Add these `script` elements between the `title` element and the end of the `head` element: - ```html ``` @@ -73,7 +76,7 @@ To create a sandbox Fiori launchpad we'll need the UI5 runtime as well as artefa Here's a brief summary of what each of these `script` elements are for, in order of appearance in the file: 1. Basic configuration for the Fiori launchpad sandbox (otherwise known as the "universal shell" or "ushell") -1. Loading of the actual Fiori launchpad sandbox itself +1. Loading of the actual Fiori launchpad sandbox itself (Note the new Fiori 3 theme, do you like it?) 1. Loading and bootstrapping of SAPUI5 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,7 +85,29 @@ Reloading the browser tab should now show the beginnings of something recognizab ![an empty Fiori launchpad](empty-fiori-launchpad.png) -### 3. Introduce a basic UI app to the Fiori launchpad +### 3. Add a new module to the project descriptor +:point_right: Add this new module to the project descriptor file `mta.yaml` next to the other modules as follows: +``` +- name: bookshop-ui + type: nodejs + path: app + parameters: + memory: 256M + disk-quota: 256M + requires: + - name: srv_api + group: destinations + properties: + forwardAuthToken: true + strictSSL: false + name: srv_api + url: ~{url} +``` + +This snippet does not only describe the runtime environment of the new module, it also injects the URL of the deployed `svr` module during deploy time. + + +### 4. Introduce a basic UI app to the Fiori launchpad Now we have the launchpad as a container for our app, let's introduce it gradually. @@ -93,14 +118,14 @@ The first thing to do is to add an entry to the sandbox launchpad configuration ```javascript