Appearance
Prototyping with SAP CAP
Deep dive workshop to learn more about:
- Prototyping with SAP CAP
- How to use uniform APIs to consume local or remote services
- Out-of-the-box support for SAP Fiori elements UX
- Events and Messaging using SAP Event Mesh
- Essential steps to deploy a CAP application to SAP BTP
FullScreen mode
There's built-in support for fullscreen mode. Press F on your keyboard to view the presentation in fullscreen mode. Once in fullscreen mode, press the ESC key to exit.
UDINA Trial Sub Account
In connection with the UNIORG SAP BTP workshop, UNIORG offers a time-limited trial sub-account that can be used for an initial introduction to development with SAP BTP.
The sub-account can be connected to the SAP Cloud Connector to securely access on-premise systems from the SAP BTP Cloud.
Plan Restrictions
- Developer Access
- 5 named S-User
How to Generate a new S-User ID - Authorizations
- SAP Business Application Studio Developer
- Cloud Connectivity and Destination Admin
- SAP BTP SubAccount Viewer
- 5 named S-User
- Cloud Foundry Quota Plan
- Runtime Memory: 512 MByte
- Routes: 4
- Service Instances: 8
- App Instances: 4
- No paid service plans
- Business Application Studio – Free Plan
- A user can only have up to 2 dev spaces.
- A user can only have 1 dev space in the RUNNING state at a time.
- The maximum size limit of a dev space is 4 GB.
Setup your DEV Space
Launch My DEV Spaces
Create CAP DEV Space
Choose Full Stack Cloud Application application and press
Grow As You Go
This section contains best practices for speeding up development by jump-starting projects with zero setup, eliminating boilerplate code, and parallelizing work.
⓪ Jumpstarting Project
The following steps show creating new projects with a SAP BAS Template, but it is also possible by using the CDS Development Kit.
Start with template wizzard
Choose CAP Project
Enter CAP Project Details
Project Storyboard
Start CDS service in terminal
bash
cds watch
Add helper script to package.json
json
"scripts": {
"start": "cds-serve",
"watch": "cds watch"
}
Add NPM Scripts view
Press ctrlshiftp and select View: Open View
-> NPM Scripts
.
CAP Notebook Helper
A CAP Notebook is a Custom Notebook in Visual Studio Code that serves you as a guide on how to create, navigate, and monitor CAP projects.
Download CAP Notebook and Drag & Drop it to your WORKSPACE
① Use External Data Model
- Option (BAS Admin) - Add destination with Service Center
- Having the role BAS Admin, the data model can be created using the Storyboard.
- The Service Center opens up and then add a new SAP System for your
udina-<customer>-trial
subaccount.
Option (Destination Admin) - Create destination with SAP BTP Cockpit
If you are assigned to the role BAS Admin, but you are allowed to create BTP Destinations, you can manually add a relevant service destination.
Open the BTP Cockpit section Connectivity -> Destinations and create a New Destination with the following settings:
Destination Configuration Value Name Northwind Type HTTP Description oData V2 Demo Service URL https://services.odata.org/V2/Northwind/Northwind.svc/ Proxy Type Internet Authentication NoAuthentication Additional Properties Value HTML5.DynamicDestination true WebIDEAdditionalData full_url WebIDEEnabled true WebIDEUsage odata_gen Full Path is mandatory
BAS currently only supports service destinations with full path
Add External Data Model
Use Service Action Add External Data Model to CAP Project to add service to CAP project.
Check, if BAS created the relevant package.json
service section:
json
"cds": {
"requires": {
"Northwind": {
"kind": "odata-v2",
"model": "srv/external/Northwind",
"[production]": {
"credentials": {
"destination": "Northwind"
}
}
}
}
}
Use real destination for development by adding default-env.json
json
{
"VCAP_SERVICES": {},
"destinations": [
{
"name": "Northwind",
"url": "https://services.odata.org/V2/Northwind/Northwind.svc/"
}
]
}
Deprecated
The usage of default-env.json
is generally deprecated and cds bind should be used instead, but it needs a deployed service. To overcome this chicken-egg problem (testing before deployment), we will use this approach for demo purposes.
Remove production switch from package.json
Just delete the marked lines from existing code and use context menu Format Document
to fix text indent:
json
"cds": {
"requires": {
"Northwind": {
"kind": "odata-v2",
"model": "srv/external/Northwind",
"[production]": {
"credentials": {
"destination": "Northwind"
}
}
}
}
}
Restart CDS service in terminal to load destination environment
bash
cds watch
Test external service
bash
https://localhost:4004/northwind-sample/Customers
Add missing dependencies for external oData V2 support
Missing dependency error @sap-cloud-sdk/http-client
Since CAP is a glue framework and by default uses oData v4, you need to add missing dependency @sap-cloud-sdk/http-client
to add oData v2 support for external service consumption.
bash
npm i @sap-cloud-sdk/http-client
Re-Test external service
https://localhost:4004/northwind-sample/Customers
② Mashing up with Remote Services
Combining local and remote services using Mashing.
Enhance cat-service.cds
cds
using my.bookshop as my from '../db/data-model';
using Northwind from './external/Northwind.cds';
service CatalogService {
@readonly entity Books as projection on my.Books;
@readonly entity Customers as projection on Northwind.Customers;
}
Create new cat-service.js
service implementation
js
const cds = require('@sap/cds');
module.exports = cds.service.impl(async function () {
const Northwind = await cds.connect.to('Northwind');
this.on('READ', 'Customers', req => {
//console.log("read external customers", req.query);
return Northwind.run(req.query);
});
});
③ REST Client
The REST Client for Visual Studio Code allows you to send HTTP request and view the response in Visual Studio Code directly (similarly to Postman).
Create a ./test
folder and add a file service.http
with content.
http
GET http://localhost:4004/catalog/Books
###
GET http://localhost:4004/catalog/Customers
### Example POST service call with CSRF token
@host = https://my-sap-system:443
@path = /sap/opu/odata/sap/api_purchasereq_process_srv/
# @name fetchToken
HEAD {{host}}{{path}}
Authorization: Basic {{$dotenv USERNAME}} {{$dotenv PASSWORD}}
X-CSRF-Token: Fetch
###
# @name createPurchaseRequisition
POST {{host}}{{path}}A_PurchaseRequisitionHeader
Authorization: Basic {{$dotenv USERNAME}} {{$dotenv PASSWORD}}
X-CSRF-Token: {{fetchToken.response.headers.x-csrf-token}}
Content-Type: application/json
{
data: {}
}
Externalize sensitive data to ./test/.env
and exclude it from git inside .gitignore
sh
USERNAME=<Username>
PASSWORD=<Password>
④ Enhance Bookshop Data Model
Enriching the book shop similarly to tutorial Build a Business Application Using CAP for Node.js, which focuses on local CAP development with VS Code.
Enhance ./db/data-model.cds
cds
namespace my.bookshop;
using {
Country,
managed
} from '@sap/cds/common';
entity Books {
key ID : Integer;
title : localized String;
author : Association to Authors;
stock : Integer;
}
entity Authors {
key ID : Integer;
name : String;
books : Association to many Books
on books.author = $self;
}
entity Orders : managed {
key ID : UUID;
book : Association to Books;
country : Country;
amount : Integer;
}
Enhance ./srv/cat-service.cds
cds
using my.bookshop as my from '../db/data-model';
using Northwind from './external/Northwind.cds';
service CatalogService {
entity Books as select from my.Books;
entity Authors as select from my.Authors;
entity Orders as select from my.Orders;
@readonly
entity Customers as projection on Northwind.Customers;
}
Add initial data ./db/data/my.bookshop-Authors.csv
Use CAP DATA MODELS AND SERVICES view to create sample,
csv
ID,name
101,Emily Brontë
107,Charlote Brontë
150,Edgar Allen Poe
170,Richard Carpenter
Replace initial data ./db/data/my.bookshop-Books.csv
with
csv
ID,title,author_ID,stock
201,Wuthering Heights,101,12
207,Jane Eyre,107,11
251,The Raven,150,333
252,Eleonora,150,555
271,Catweazle,170,22
Test expand
https://localhost:4004/catalog/Books?$expand=author
Localize data
Copy ./db/data/my.bookshop-Books.csv
to ./db/data/my.bookshop-Books_texts.csv
csv
ID,locale,title
201,de,DE Wuthering Height
201,en,EN Wuthering Height
Test locales
https://localhost:4004/catalog/Books?$expand=texts
Add Virtual Element
Virtual Elements allow to implement calculated chracteristics. This keyword indicates that this element isn’t added to persistent artifacts.
Add Books virtual element stockState
inside data-model.cds
cds
entity Books {
key ID : Integer;
title : localized String;
author : Association to Authors;
stock : Integer;
virtual stockState : String enum {
Error = 'Error';
Information = 'Information';
None = 'None';
Success = 'Success';
Warning = 'Warning';
};
}
Add custom logic ./srv/cat-service.js
ts
this.after('READ', 'Books', each => {
// Add some discount for overstocked books
if (each.stock > 111) each.title += ' -- 11% discount!'
// Calculate Virtual stock state to highlight critical stocks
each.stockState = (each.stock < 20) ? 'Warning' : 'None'
})
⑤ Create SAP Fiori elements UX
Use ctrlshiftp and select Fiori: Open Application Generator
Add the following information:
Property | Value |
---|---|
Template Selection | |
Template Type | SAP Fiori |
Template | List Report Page |
Data Source and Service Selection | |
Data source | Use a local CAP Project |
CAP project folder path | /home/user/projects/workshop/ |
OData service | CatalogService |
Entity Selection | |
Main entity | Books |
Navigation entity | None |
Automatically add table columns | Yes |
Project Attributes | |
Module name | bookmanager |
Application title | Book Manager |
Application namespace | udina |
Add deployment configuration to MTA project | Yes |
Add FLP configuration | No |
Configure advanced options | No |
Deployment Configuration | |
Please choose the target | Cloud Foundry |
Destination name | Northwind - ... |
Fix SAPUI5 namespace inside ./app/bookmanager/webapp/index.html
html
<script
id="sap-ui-bootstrap"
src="https://ui5.sap.com/1.115.0/resources/sap-ui-core.js" // [!code focus]
...
Start Application
Launch the Web Application Bookmanager
Edit Page Map
- Use context menu
Shop Page Map
on bookmanager app. - Set
BooksList
-> Table -> Initial Load:Enabled
- Investigate changes inside
manifest.json
- See Flexible Column Layout example
Add DRAFT Mode to ./srv/cat-service.cds
Books
cds
...
service CatalogService {
@odata.draft.enabled
entity Books as select from my.Books;
...
}
Add ValueHelp annotation to ./app/bookmanager/annotations.cds
cds
annotate service.Books with {
author @Common: {
Text : author.name,
TextArrangement : #TextFirst,
ValueListWithFixedValues: false,
ValueList : {
CollectionPath: 'Authors',
Parameters : [
{
$Type: 'Common.ValueListParameterInOut',
LocalDataProperty: author_ID,
ValueListProperty: 'ID'
},
{
$Type: 'Common.ValueListParameterDisplayOnly',
ValueListProperty: 'name'
}
]
}
};
};
Start CRUD Application
Kill process if eaddrinuse
sh
lwctl -c basic-tools kill -9 $(lwctl -c basic-tools ps aux | grep node | awk '{print $2}')
⑥ Deploy to SAP BTP
Following the SAP CAP Cookbook Deploy to Cloud Foundry, the CAP command-line tool helps to prepare the project for the deployment to SAP BTP.
How SAP CAP project folders deploy to SAP BTP
Using XSUAA-Based Authentication
Configure your app for XSUAA-based authentication:
sh
cds add xsuaa --for production
The following files have been generated:
File | Description |
---|---|
manifest.yml | Cloud Foundry App Manifest |
mta.yaml | MTA Development Descriptor |
xs-security.json | Application Security Descriptor |
Using MTA-Based Deployment
We’ll be using the Cloud MTA Build Tool to execute the deployment:
sh
cds add mta
The mta.yaml
has been updated by adding a module for the CAP service, that is referencing the XSUAA resource.
Restrict the memory usage of your CAP service (defaults to 1024M) by adding a memory parameter to the CAP module:
yml
modules
- name: workshop-srv
type: nodejs
path: gen/srv
parameters:
buildpack: nodejs_buildpack
memory: 256M
Using a local SQLite database
Instead of using a production-ready cloud database, our prototyping will use the sqlite3 in-memory database instead.
Move sqlite from devDependencies to dependencies (prod):
sh
npm i sqlite3 -P
Edit mta.yaml to auto build the prototype database
yml
build-parameters:
before-all:
- builder: custom
commands:
- npx -p @sap/cds-dk cds build --production
- npx -p @sap/cds-dk cds deploy --to sqlite:gen/srv/srv/db.sqlite --no-save
Add database dependency to package.json
json
{
"[production]": {
"db": {
"kind": "sqlite",
"credentials": {
"database": "srv/db.sqlite"
}
}
}
}
Using Custom App Router as Gateway
For scenarios without SAP Fiori Launchpad, the app router needs to be deployed along with your application:
sh
cds add approuter --for production
The following files have been generated:
File | Description |
---|---|
app/default-env.json | Local environment destination injections |
app/package.json | Node.js Approuter metadata |
app/xs-app.json | Application Routing Configuration |
The mta.yaml
has been updated by adding the approuter module.
Add helper scripts to package.json
json
"scripts": {
"start": "cds-serve",
"watch": "cds watch",
"build": "rimraf resources mta_archives && mbt build --mtar archive",
"deploy": "cf deploy mta_archives/archive.mtar --retries 1",
"target:test": "cf target -o udina-<customer>-trial -s services"
}
Build Services
Make sure to install formerly added service dependencies to node_modules
:
sh
npm install
and trigger the build archive process using the build
script.
The following file has been generated:
File | Description |
---|---|
mta_archives/archive.mtar | MTA Archive |
Login to Cloud Foundry
Inside BAS (or VS code) press ctrlshiftp and select CF: Login to Cloud Foundry
.
Alternatively use the terminal:
sh
cf login -a https://api.cf.eu10.hana.ondemand.com
Enter the relevant input like email, password and deployment target.
Make sure to use the right deployment target
Check that you are using the cf deployment target udina-<customer>-trial
to avoid deploying into wrong SAP BTP Sub Account!
Deploy Services
Trigger the deploy
script.
Test Services
Open the SAP BTP Cockpit for the Sub Account udina-<customer>-trial
and open the Cloud Foundry Space services.
The deployment created two applications:
Name | Description |
---|---|
workshop | The application router |
workshop-srv | The CAP service |
Test the services.
Try to open Northwind Customers entity set, that is returning an error:
Error
Error during request to remote service: Failed to load destination.
⑦ Create SAPUI5 freestyle Applicatiom
- Use ctrlshiftp and select
Fiori: Open Application Generator
to start the Template Wizard. - Choose Application Type:
SAPUI5 freestyle
- Select
SAPUI5 Application
- Choose
Next
- Select Data Source:
Use a Local CAP Project
- Choose your CAP project:
workshop
- Chosse OData service:
CatalogService
- Choose
Next
- Choose View name:
View
- Choose
Next
- Choose Module name:
freestyleui5
- Choose Application title:
Freestyle UI5
- Make sure, option
Add deployment configuration to MTA project
is checked - Choose
Next
- Choose Target:
Cloud Foundry
- Choose Destination name:
None
- Choose
Next
Investigate changes
Investigate changes inside mta.yaml
and see new entries:
- modules
- workshop-app-content
- freestyleui5
- resources
- workshop-repo-host
- workshop-destination-service
Implement UI5 view
Replace <content/>
with marked lines inside View.view.xml
xml
<mvc:View controllerName="freestyleui5.controller.View" displayBlock="true"
xmlns="sap.m" xmlns:mvc="sap.ui.core.mvc">
<Page id="page" title="{i18n>title}">
<List id="list" items="{/Books}">
<StandardListItem title="{title}" info="{stock}"/>
</List>
</Page>
</mvc:View>
Exclude app sources from Approuter
Change mta.yaml
to skip app
yml
- name: workshop
type: approuter.nodejs
path: app/
build-parameters:
ignore: ["freestyleui5/"]
Deploy Updates
- Build MTA
- Deploy MTA
Investigate HTML5 Application Repository
bash
cf html5-info
cf html5-list
Bound HTML5 repository runtime to Approuter
Add missing dependencies inside mta.yaml
yml
requires:
- name: workshop-repo-runtime
resources:
- name: workshop-repo-runtime
type: org.cloudfoundry.managed-service
parameters:
service: html5-apps-repo
service-plan: app-runtime
Bound Destination resource to CAP
Add missing dependencies inside mta.yaml
yml
requires:
- name: workshop-destination-service
Add missing HTML5 route to Approuter
Edit app/xs-app.json
:
json
{
"source": "^/freestyleui5(/.*)",
"target": "/freestyleui5/$1",
"service": "html5-apps-repo-rt",
"authenticationType": "xsuaa"
},
Deploy Updates
- Build MTA
- Deploy MTA
Test services
- Test Freestyle UI5 application
- Test Northwind /catalog/Customers service using destination
Quick fix freestyleui5 app relative service path
Add route to app/freestyleui5/xs-app.json
json
{
"source": "^/catalog/(.*)$",
"destination": "srv-api",
"authenticationType": "xsuaa",
"csrfProtection": true
},
Deploy Updates
- Build MTA
- Deploy MTA
⑧ To be defined
⑨ Undeploy your project
Use the generated script undeploy or manually call:
cf undeploy workshop --delete-services --delete-service-keys --delete-service-brokers
OData briefly explained
There are various ways to learn about OData and starting with the native HTTP request may be the most direct one. Read Learning more or the Full Documentation for further details.
Entity Data Model (EDM)
OData Resource | Similar to | Is Described in an Entity Data Model by |
---|---|---|
Collection | Table | Entity Set - A navigation property on an entity type that identifies a collection of entities |
Entry | Structure | Entity Type - Note: Entity Types may be part of a type hierarchy |
Property of an entry | Field | Primitive or Complex Entity Type Property |
Complex Type | Deep Structure | Complex Type |
Link | Relation | A Navigation Property defined on an Entity Type |
Service Operation | BAPI | Function Import |
URI Convention
A URI used by an OData service has up to three significant parts:
- the service root URI
- resource path
- query string options.
Example Calls
Method | Task | URI Segment |
---|---|---|
GET | Metadata | /$metadata |
GET | Request resources | /Books |
GET | Request individual resource | /Books(201) |
GET | Queries | /Books?$select=title,stock&$expand=author&$top=3 |
GET | Invoke function | /Books(201)/ns.rateAuthor() |
POST | Create resource | /Books { ID: 999, title: "abc", stock: 3 } |
Query String Options
Query | Description |
---|---|
$select | Subset of properties to be returned |
$orderby | Specifies an expression for determining what values are used to order the collection of Entries |
$top | Identifies a subset of the Entries in the Collection of Entries |
$skip | Skips N entries in the result of the Collection of Entries |
$filter | Filters a subset of the Entries in the Collection of Entries Operators: Eq,Ne,Gt,Ge,Lt,Le,And,Or,Not Functions: substringof,endswith,startswith,lnegth,indexof,tolower,toupper,trim,concat |
$expand | Returns linked navigation properties inlined with the results |
$format | Returned data format [Atom,Xml,Json,Xlsx(ABAP)...IANA-defined-content-type] |
$count=true | Result includes a count of the number of Entries in the Collection |