Technology Blogs by Members
Explore a vibrant mix of technical expertise, industry insights, and tech buzz in member blogs covering SAP products, technology, and events. Get in the mix!
cancel
Showing results for 
Search instead for 
Did you mean: 
MioYasutake
Active Contributor

*Update*


I have published a new blog where I explain how to deploy a CAP based Fiori app to a central launchpad (without using Approuter).

Introduction


 

Recently, I've been learning about CAP (Cloud Application Programming Model).
With CAP you can add UI annotations to OData service and preview how the Fiori app will look like from local URL.


 

I had the following questions regarding CAP based Fiori elements apps.

  1. How can I consume CAP based OData service from Fiori elements app?

  2. Can I use Fiori tools?

  3. Can I use MTA style project and bundle UI and db modules together?


In this blog, I'm going to share how I have achieved these requirements.
The source code is available at GitHub.

I'm new to Cloud Foundry Development, so comments and suggestions are appreciated.

 

Developing Environment



 

Steps



  1. Create a CAP project

  2. Add OData v2 proxy

  3. Add UI module using Fiori tools

  4. Add Approuter

  5. Add Deployer

  6. Add Launchpad module

  7. Add XSUAA configuration

  8. Generate mta.yaml

  9. Build & Deploy


 

1. Create a CAP project


 

1.1. Generate a new CAP project.
cds init bookshop

 

1.2. Create db/schema.cds. Here, I'm using well-known bookshop model.
using { Currency, managed, sap } from '@sap/cds/common';

namespace sap.capire.bookshop;

entity Books : managed {
key ID : Integer @title : 'ID';
title : localized String(111) @title : 'Title';
descr : localized String(1111)@title : 'Description';
author : Association to Authors @title : 'Author';
stock : Integer @title : 'Stock';
price : Decimal(9, 2) @title : 'Price';
currency : Currency @title : 'Currency';
}

entity Authors : managed {
key ID : Integer @title : 'Author ID';
name : String(111) @title : 'Author Name';
dateOfBirth : Date @title : 'Date of Birth';
dateOfDeath : Date @title : 'Date of Death';
placeOfBirth : String @title : 'Place of Birth';
placeOfDeath : String @title : 'Place of Death';
books : Association to many Books on books.author = $self;
}


 

1.3. Create srv/cat-service.cds.
using { sap.capire.bookshop as my } from '../db/schema';

service CatalogService {
entity Books as projection on my.Books
entity Autors as projection on my.Authors
}

 

1.4. Just blow service definition, add UI annotations to srv/cat-service.cds.
annotate CatalogService.Books with @(
UI: {
HeaderInfo: {
TypeName: 'Book',
TypeNamePlural: 'Books',
Title: { Value: ID },
Description: { Value: title }
},
SelectionFields: [ ID, title, author.name ],
LineItem: [
{ Value: ID },
{ Value: title },
{ Value: author.name },
{ Value: price },
{ Value: currency_code }
],
Facets: [
{
$Type: 'UI.CollectionFacet',
Label: 'Book Info',
Facets: [
{$Type: 'UI.ReferenceFacet', Target: '@UI.FieldGroup#Main', Label: 'Main Facet'}
]
}
],
FieldGroup#Main: {
Data: [
{ Value: ID },
{ Value: title },
{ Value: author_ID },
{ Value: price },
{ Value: currency_code }
]
}
}
);

 

1.5. Add db/data/sap.capire.bookshop-Authors.csv as initial data.

 

1.6. Similarly, add db/data/sap.capire.bookshop-Books.csv.

 

1.7. Execute cds watch and see the Fiori preview.




 

2. Add OData v2 proxy


 

As we are going to use Fiori Tools, the OData service needs to be adapted to v2 (Fiori tools currently do not fully support OData v4).
For this, we use @sap/cds-odata-v2-adapter-proxy

 

2.1. install @Sap/cds-odata-v2-adapter-proxy to your project.
npm install @sap/cds-odata-v2-adapter-proxy -s

 

2.2. Create srv/server.js.
"use strict";

const cds = require("@sap/cds");
const proxy = require("@sap/cds-odata-v2-adapter-proxy");

cds.on("bootstrap", app => app.use(proxy()));

module.exports = cds.server;

 

Now, OData v2 service can be accessed with "/v2" prefix.

path to v4 service: http://localhost:4004/catalog/Books

path to v2 service: http://localhost:4004/v2/catalog/Books

 

3. Add UI module using Fiori tools


 

Next, let's add UI module using Fiori tools.

 

3.1. Generate Fiori Project


 

1. Press Ctrl + Shift + P and launch Application Generator.


 

2. Select SAP Fiori elements application.


 

3. Select List Report Object Page V2.


 

4. Select Connect to an OData Source as Data source, and specify your local CAP OData URL:  http://localhost:4004/v2/catalog/


 

5. Select "Books" as Main Entity.


 

6. Type below information.























Module Name bookshopapp
Title Bookshop App
Namespace demo
Description Bookshop App
Project Folder Your CAP project's root folder


Once you press Finish, Fiori elements app will be generated.

 

7. Move to bookshopapp folder you've just created and run the app.
npm start

 

You can see your Fiori app running locally at http://localhost:8080/test/flpSandbox.html#masterDetail-display.


 

3.2. Make adjustments for Cloud Foundry


 

We have to make some adjustments so that this Fiori app can be deployed as part of MTA.

 

1. Create bookshopapp/webapp/xs-app.json.
{
"welcomeFile": "flpSandbox.html",
"routes": [{
"source": "^(.*)",
"target": "$1",
"authenticationType": "xsuaa",
"service": "html5-apps-repo-rt"
}]
}

 

2. Add ui5-task-zipper as devDependencies to bookshopapp module.
npm install ui5-task-zipper --save-dev

 

3. Configure bookshopapp/package.json.

Add ui5-task-zipper to ui5>dependencies.
	"ui5": {
"dependencies": [
"@sap/ux-ui5-tooling",
"ui5-task-zipper"
]
}

 

4. Replace bookshopapp/ui5.yaml with the following code. What I've done are:

  • Remove "ui5Theme"

  • Add resources section

  • Add builder section


Resulting bookshopapp/ui5.yaml will look like below.
specVersion: '1.0'
metadata:
name: 'bookshopapp'
type: application
resources:
configuration:
paths:
webapp: webapp
server:
...
builder:
customTasks:
- name: "ui5-task-zipper"
afterTask: "uglify"
configuration:
archiveName: "uimodule"
additionalFiles:
- webapp/xs-app.json

 

3.3. Add Configuration for Launchpad


 

1. Add crossNavigation settings to bookshopapp/webapp/manifest.json

This configuration is necessary for executing the app from Fiori Launchpad.
    "sap.app": {
"id": "demo.bookshopapp",
"type": "application",
"i18n": "i18n/i18n.properties",
"applicationVersion": {
"version": "1.0.0"
},
"title": "{{appTitle}}",
"description": "{{appDescription}}",
"crossNavigation": {
"inbounds": {
"intent1": {
"signature": {
"parameters": {},
"additionalParameters": "allowed"
},
"semanticObject": "data",
"action": "display",
"title": "{{appTitle}}",
"icon": "sap-icon://search"
}
}
},

 

4. Add Approuter


 

We are adding Approuter module to route incoming requests and authenticate users.

 

4.1. Create approuter folder at the project root and add package.json inside it.
{
"name": "bookshop-approuter",
"version": "0.0.1",
"engines": {
"node": "12.x.x"
},
"scripts": {
"start": "node node_modules/@sap/approuter/approuter.js"
},
"dependencies": {
"@sap/approuter": "^7.1.0"
}
}

 

4.2. Add approuter/xs-app.json.

Approuter redirects requests containing path /v2/catalog to srv-api destination, which will be defined later in mta.yaml.
At this point, authenticationType is "none", because we don't restrict access to the CAP service.
If you require only users with certain authorization to access the service, you would set authenticationType as "xsuaa".  How to achieve user authentication is very well explained in Jhodel Cailan's blog.
{
"welcomeFile": "/cp.portal",
"authenticationMethod": "route",
"logout": {
"logoutEndpoint": "/do/logout"
},
"routes": [
{
"source": "^/v2/catalog/(.*)$",
"authenticationType": "none",
"destination": "srv-api",
"csrfProtection": false
}
]
}

 

5. Add Deployer


 

Deployer module will upload html5 app to HTML5 Application Repository.

 

5.1. Create deployer folder at the project root and add package.json inside it.
{
"name": "bookshop-deployer",
"engines": {
"node": "12.x.x"
},
"dependencies": {
"@sap/html5-app-deployer": "2.1.0"
},
"scripts": {
"start": "node node_modules/@sap/html5-app-deployer/index.js"
}
}

 

6. Add Launchpad module


 

This module is necessary for the app to be able to run in CF Launchpad.

 

6.1. Create launchpad folder at the project root and add package.json inside it.
{
"name": "launchpad-site-content",
"description": "Portal site content deployer package",
"engines": {
"node": "12.X"
},
"dependencies": {
"@sap/portal-cf-content-deployer": "3.32.0-20200312112659"
},
"scripts": {
"start": "node node_modules/@sap/portal-cf-content-deployer/src/index.js"
}
}

 

6.2. Add launchpad/portal-site/CommonDataModel.json

Here, you configure Launchpad Tile and Group.
{
"_version": "3.0.0",
"identification": {
"id": "c9aae627-9601-4a11-83c3-41b94a3c8026-1576776549699",
"entityType": "bundle"
},
"payload": {
"catalogs": [
{
"_version": "3.0.0",
"identification": {
"id": "defaultCatalogId",
"title": "{{catalogTitle}}",
"entityType": "catalog",
"i18n": "i18n/i18n.properties"
},
"payload": {
"viz": [
{
"id": "demo.bookshopapp",
"vizId": "data-display"
}
]
}
}
],
"groups": [{
"_version": "3.0.0",
"identification": {
"id": "defaultGroupId",
"title": "{{groupTitle}}",
"entityType": "group",
"i18n": "i18n/i18n.properties"
},
"payload": {
"viz": [
{
"id": "demo.bookshopapp-1",
"appId": "demo.bookshopapp",
"vizId": "data-display"
}
]
}
}],
...
}

 

6.3. Add launchpad/portal-site/i18n/i18n.properties.
catalogTitle=Default Catalog
groupTitle=Default Group

 

7. Add XSUAA configuration


 

We have specified in bookshopapp/webapp/xs-app.json that HTML5 Application Repository requires user authentication as below, so we need XSUAA service instance bound to Approuter.
"authenticationType": "xsuaa"

 

7.1. Add xs-security.json to the project's root.
{
"xsappname": "bookshop",
"tenant-mode": "dedicated",
"scopes": [
{
"name": "uaa.user",
"description": "UAA"
}
],
"role-templates": [
{
"name": "Token_Exchange",
"description": "UAA",
"scope-references": [
"uaa.user"
]
}
]
}

 

8. Generate mta.yaml


 

This is the final step before deploy. We are connecting everything together in mta.yaml.

 

8.1. Add below configuration to package.json, which is at the project root.

It indicates that HANA DB will be used in production, while SQLite DB will be used for development.
    "cds": {
"requires": {
"db": {
"kind": "hana"
}
}
}

 

8.2. Generate mta.yaml.
cds add mta

Next,  we need to make some changes to mta.yaml.

 

8.3. Modify bookshop-db resource

As I'm using trial account, I need to change the parameter "service" from hana to hanatrial.
# ------------------------------------------------------------
- name: bookshop-db
# ------------------------------------------------------------
type: com.sap.xs.hdi-container
parameters:
service: hanatrial
service-plan: hdi-shared
properties:
hdi-service-name: ${service-name}

 

8.4. Add Approuter module.
 - name: bookshop-app-router
type: approuter.nodejs
path: approuter
parameters:
disk-quota: 512M
memory: 512M
requires:
- name: bookshop_uaa
- name: bookshop_html5_repo_runtime
- name: bookshop_portal
- name: srv-api
group: destinations
properties:
name: srv-api
url: "~{srv-url}"
forwardAuthToken: true

 

8.5. Add UI and Deployer modules.
 - name: bookshop_ui_deployer
type: com.sap.application.content
path: deployer
requires:
- name: bookshop_html5_repo_host
parameters:
content-target: true
build-parameters:
build-result: resources
requires:
- name: bookshopapp
artifacts:
- dist/uimodule.zip
target-path: resources/
- name: bookshopapp
type: html5
path: bookshopapp
build-parameters:
builder: custom
commands:
- npm install
- npm run build
supported-platforms: []

 

8.6. Add Launchpad deployer module.
 - name: bookshop_launchpad_deployer
type: com.sap.portal.content
path: launchpad
deployed-after:
- bookshop_ui_deployer
requires:
- name: bookshop_portal
- name: bookshop_html5_repo_host
- name: bookshop_uaa

 

8.7. Add the following resources.
 - name: bookshop_uaa
type: org.cloudfoundry.managed-service
parameters:
path: ./xs-security.json
service-plan: application
service: xsuaa
- name: bookshop_html5_repo_runtime
type: org.cloudfoundry.managed-service
parameters:
service-plan: app-runtime
service: html5-apps-repo
- name: bookshop_html5_repo_host
type: org.cloudfoundry.managed-service
parameters:
service-plan: app-host
service: html5-apps-repo
config:
sizeLimit: 1
- name: bookshop_portal
type: org.cloudfoundry.managed-service
parameters:
service-plan: standard
service: portal

 

In the end, mta.yaml will look like this.

 

8.8. After you've generated mta.yaml, add cds configuration for local development.
  "cds": {
"requires": {
"db": {
"kind": "hana"
}
},
"[development]": {
"requires": {
"db": {
"kind": "sql"
}
}
}
}

 

9. Build & Deploy


 

Finally, we deploy the MTA project to Cloud Foundry.

 

9.1. Run below command to build the project.
mbt build

 

The first time I ran build, I received below error.

The error was caued by @Sap/cds version which was ^3. After I upgraded it to ^4 and executed npm install, the error was gone. I also found that without @Sap/hana-client, bookshop-srv could not be deployed.

Below is package.json after upgrade.
    "dependencies": {
"@sap/cds": "^4",
"@sap/cds-odata-v2-adapter-proxy": "^1.4.43",
"express": "^4",
"@sap/hana-client": "^2.4.177"
}

 

9.2. Run below command to deploy the project to Cloud Foundry.

Make sure you have logged in to the target CF subaccount.
cf deploy mta_archives/bookshop_1.0.0.mtar

 

If everything goes well, you'll see bookshop-app-router running in your space.


 

When you click Approuter URL, Fiori launchpad screen opens.


 

Press the Tile and... Fiori app is running!


 

Conclusion


 

In this blog I have explained how to develop a Fiori elements app which consumes CAP service in the same project.
One drawback of putting everything together in one MTA project is that it takes a lot of time to build & deploy. So it may be better to split it into UI project and CAP project.
In that case, you can register CAP service as CF destination and consume the destination from your UI app.

 

References


 

About development in Cloud Foundry



About Cloud Application Programming Model


15 Comments
Labels in this area