Skip to content

Prototyping with SAP CAP

Deep dive workshop to learn more about:

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

SAP BAS Dev Spaces

Create CAP DEV Space

Choose Full Stack Cloud Application application and press

Create CAP Dev Space

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.

Get started with SAP Business Application Studio

⓪ 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

Start from template

Choose CAP Project

Choose CAP Project

Enter CAP Project Details

CAP Project Details

Project Storyboard

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

  1. Option (BAS Admin) - Add destination with Service Center
    • Having the role BAS Admin, the data model can be created using the Storyboard. Story Board – Create a data model
    • The Service Center opens up and then add a new SAP System for your udina-<customer>-trial subaccount.
BAS Service Center - Add Northwind Destination
  1. 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 ConfigurationValue
    NameNorthwind
    TypeHTTP
    DescriptionoData V2 Demo Service
    URLhttps://services.odata.org/V2/Northwind/Northwind.svc/
    Proxy TypeInternet
    AuthenticationNoAuthentication
    Additional PropertiesValue
    HTML5.DynamicDestinationtrue
    WebIDEAdditionalDatafull_url
    WebIDEEnabledtrue
    WebIDEUsageodata_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.

Add External Data Model to CAP Project
Add Northwind Data Model 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

Use CAP Notebook

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

Use CAP Notebook

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

Use CAP Notebook

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:

PropertyValue
Template Selection
Template TypeSAP Fiori
TemplateList Report Page
Data Source and Service Selection
Data sourceUse a local CAP Project
CAP project folder path/home/user/projects/workshop/
OData serviceCatalogService
Entity Selection
Main entityBooks
Navigation entityNone
Automatically add table columnsYes
Project Attributes
Module namebookmanager
Application titleBook Manager
Application namespaceudina
Add deployment configuration to MTA projectYes
Add FLP configurationNo
Configure advanced optionsNo
Deployment Configuration
Please choose the targetCloud Foundry
Destination nameNorthwind - ...

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

Feature Showcase for CAP

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


uml diagram

Using XSUAA-Based Authentication

Configure your app for XSUAA-based authentication:

sh
cds add xsuaa --for production

The following files have been generated:

FileDescription
manifest.ymlCloud Foundry App Manifest
mta.yamlMTA Development Descriptor
xs-security.jsonApplication 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:

FileDescription
app/default-env.jsonLocal environment destination injections
app/package.jsonNode.js Approuter metadata
app/xs-app.jsonApplication 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:

FileDescription
mta_archives/archive.mtarMTA 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:

NameDescription
workshopThe application router
workshop-srvThe 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

Powering SAP BTP 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 ResourceSimilar toIs Described in an Entity Data Model by
CollectionTableEntity Set - A navigation property on an entity type that identifies a collection of entities
EntryStructureEntity Type - Note: Entity Types may be part of a type hierarchy
Property of an entryFieldPrimitive or Complex Entity Type Property
Complex TypeDeep StructureComplex Type
LinkRelationA Navigation Property defined on an Entity Type
Service OperationBAPIFunction 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.
OData URI Convention

Example Calls

MethodTaskURI Segment
GETMetadata/$metadata
GETRequest resources/Books
GETRequest individual resource/Books(201)
GETQueries/Books?$select=title,stock&$expand=author&$top=3
GETInvoke function/Books(201)/ns.rateAuthor()
POSTCreate resource/Books { ID: 999, title: "abc", stock: 3 }

Query String Options

QueryDescription
$selectSubset of properties to be returned
$orderbySpecifies an expression for determining what values are used to order the collection of Entries
$topIdentifies a subset of the Entries in the Collection of Entries
$skipSkips N entries in the result of the Collection of Entries
$filterFilters 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
$expandReturns linked navigation properties inlined with the results
$formatReturned data format [Atom,Xml,Json,Xlsx(ABAP)...IANA-defined-content-type]
$count=trueResult includes a count of the number of Entries in the Collection