Skip to Content
Technical Articles
Author's profile photo Matthias Schmalz

Designing UI5 Apps for SAP Launchpad Service – Part 1

Content Navigation

Part 1: A simple UI5 app that runs in the SAP Launchpad Service (Current post)

Part 2: Multiple apps which consume a reuse library (Planned)

Part 3: Splitting bigger projects  (Planned)

Introduction

With this guide, we want to give you technical insights into UI5 apps in the SAP BTP that run in the launchpad service.
You will learn how to structure complex UI5 applications to avoid common pitfalls.
You should already be familiar with:

This guide will not go into details about the following:
– Templating tools for UI5 projects and MTA
– Developing service backends e.g. with CAP
– Configuration of launchpad sites
– OData and SAP Fiori elements

All discussed samples will soon be published in a Github repository.

Related Blogs

A Simple UI5 app that runs in the SAP Launchpad Service

This first post will focus on a simple deployment which only contains one standalone UI5 app, that fetches data from a backend. This app can run in the launchpad or standalone.

Deployed Content

In the beginning let’s have a look at all the parts which form a deployment of an app.
There are mainly 3 service instances that you can find in your Cloud Foundry space after deploying it:

  • An HTML5 repository app-host containing an HTML5 app. The HTML5 repository takes care of storing and serving all the static content of your app, i.e. all js, html and other files that form the runnable source code of your app. An app-host is a container for an atomic deployment and can contain multiple apps.
    In addition to the app sources it contains:

    • A manifest.json as part of the UI5 app, which describes the app and specifies, e.g. the app ID
    • An xs-app.json which defines routes of your app e.g. to connect to your backend
  • An XSUAA instance that controls security-relevant settings and could define authorization scopes for the application backend. It also controls the authentication to your app.
  • A destination service instance with destinations to the app-host and the XSUAA. This is needed to connect the service instances to the launchpad service

MTA Project Structure

The deployment of this is described in the mta.yaml of the project. It is structured as follows:

 

  • 3 resources that represent the created service instances. They are all created by this MTA and are therefore managed services.
  • A module for the UI5 app. Most build steps are defined in the package.json. As a result it produces a zip file.
  • A module that deploys the app to the app-host. It declares a build dependency to the UI5 app to include the produced zip and requires the app-host resource as the deploy target.
  • A module that creates the destinations. It requires the destination service resource as content target and the app-host and XSUAA instance to create destinations for them.
    The configuration defines the destinations and adds some additional properties.

The configuration of the destination resource enables it to use the managed app router and already creates a hardcoded destination for the simple OData service (northwind):

- name: btp-samples-simple-app-dest-srv
  type: org.cloudfoundry.managed-service
  parameters:
    config:
      HTML5Runtime_enabled: true
      init_data:
        instance:
          destinations:
          - Name: northwind
            Authentication: NoAuthentication
            ProxyType: Internet
            Type: HTTP
            URL: https://services.odata.org/V3/Northwind/Northwind.svc/
          existing_destinations_policy: update
      version: 1.0.0
    service: destination
    service-name: btp-samples-simple-app-dest-srv
    service-plan: lite
The XSUAA resource includes the xs-security.json, which could define authorization scopes and roles:
- name: btp-samples-simple-app-uaa
  type: org.cloudfoundry.managed-service
  parameters:
    path: ./xs-security.json
    service: xsuaa
    service-plan: application
    service-name: btp-samples-simple-app-xsuaa-srv

 

The destination content module requires all 3 instances. It specifies to create service keys which will provide the credentials.

- name: btp-samples-simple-app-dest-content
  type: com.sap.application.content
  requires:
  - name: btp-samples-simple-app-dest-srv
    parameters:
      content-target: true
  - name: btp-samples-simple-app-repo-host
    parameters:
      service-key:
        name: btp-samples-simple-app-repo-host-key
  - name: btp-samples-simple-app-uaa
    parameters:
      service-key:
        name: btp-samples-simple-app-uaa-key

Then it defines the destinations to be created. Here the values of the ServiceInstanceName properties are the instance names and not the MTA resource names as it is the case above.
It adds some additional properties. First it specifies the same service name for XSUAA and app-host. This way they are bound together.
Second, it defines the authentication method for the app-host for further routings to use OAuth2UserTokenExchange via the XSUAA:

  parameters:
    content:
      instance:
        destinations:
        - Name: btp-samples-simple-app_repo_host
          ServiceInstanceName: btp-samples-simple-app-html5-srv
          ServiceKeyName: btp-samples-simple-app-repo-host-key
          sap.cloud.service: btp.samples.simple.app
        - Name: btp-samples-simple-app_uaa
          ServiceInstanceName: btp-samples-simple-app-xsuaa-srv
          ServiceKeyName: btp-samples-simple-app-uaa-key
          sap.cloud.service: btp.samples.simple.app
          Authentication: OAuth2UserTokenExchange
        existing_destinations_policy: ignore

 

The app-content module collects the zip of the UI5 app module at build time.
Later it deploys them to the app-host.

- name: btp-samples-simple-app-app-content
  type: com.sap.application.content
  path: .
  requires:
  - name: btp-samples-simple-app-repo-host
    parameters:
      content-target: true
  build-parameters:
    build-result: resources
    requires:
    - name: btpsamplessimpleapp
      artifacts:
      - btpsamplessimpleapp.zip
      target-path: resources/

 

The module of the app itself configure the build via node.js commands.

- name: btpsamplessimpleapp
  type: html5
  path: simple.app
  build-parameters:
    build-result: dist
    builder: custom
    commands:
    - npm install
    - npm run build:cf
    supported-platforms: []

The UI5 App

Build

The UI5 app is built with UI5 Tooling. The build is controlled via the package.json and the ui5-deploy.yaml and produces a zip for the content module packager.

Routings

There is an xs-app.json file that defines routings to backend services.
Here the simple OData service path is forwarded via the northwind destination, which is created by the MTA, without any authentication.
Usually you would connect it to a BTP cloud service and define an additional authentication flow like OAuth2UserTokenExchange.

{
  "welcomeFile": "/index.html",
  "authenticationMethod": "route",
  "routes": [
    {
      "source": "^/northwind/(.*)$",
      "target": "/$1",
      "authenticationType": "none",
      "destination": "northwind"
    },
    {
      "source": "^/resources/(.*)$",
      "target": "/resources/$1",
      "authenticationType": "none",
      "destination": "ui5"
    },
    {
      "source": "^/test-resources/(.*)$",
      "target": "/test-resources/$1",
      "authenticationType": "none",
      "destination": "ui5"
    },
    {
      "source": "^(.*)$",
      "target": "$1",
      "service": "html5-apps-repo-rt",
      "authenticationType": "xsuaa"
    }
  ]
}

You can test the app locally via the UI5 Tooling. It provides a local middleware which is configured in ui5.yaml.
Here similar routings are configured e.g. the same OData service is connected. See Use Custom Middlewares  for more details.
To start the local test just run the start script in the package.json.

App Descriptor

It has a manifest.json file that defines a unique sap.app/id and application sap.app/type for it.
Having unique Ids for all apps in your subaccount is important to avoid conflicts.

   "sap.app": {
        "id": "btp.samples.simple.app",
        "type": "application",
        "i18n": "i18n/i18n.properties",
        "title": "{{appTitle}}",
        "description": "{{appDescription}}",
        "applicationVersion": {
            "version": "1.0.0"
        }
}

An entry under sap.app/crossNavigation/inbound defines the tile and navigation for the launchpad service. It is important to use unique values here to avoid conflicts with other apps which are deployed in the same subaccount.

        "crossNavigation": {
            "inbounds": {
                "btp-samples-simple-app-inbound": {
                    "signature": {
                        "parameters": {},
                        "additionalParameters": "allowed"
                    },
                    "semanticObject": "simpleApp",
                    "action": "display",
                    "title": "{{flpTitle}}",
                    "subTitle": "{{flpSubtitle}}",
                    "icon": ""
                }
            }
        }

The OData service URL is specified under sap.app/dataSources. It points to the routed source defined in xs-app.json. Please note that this is a relative path, which is resolved relative to the app. The reason for this is explained below.

"dataSources": {
    "mainService": {
        "uri": "northwind",
        "type": "OData"
    }
}

It also specifies sap.cloud/service with the same service name as specified for the destinations in the mta.yaml. Note that this has a different meaning than the sap.app/id (although it has the same value in this sample).

"sap.cloud": {
    "public": true,
    "service": "btp.samples.simple.app"
}

Running the Deployed App

Deploying the App

To deploy the app you have to build the MTA and then deploy it to your Cloud Foundry space.
SAP Business Application Studio simplifies this for you, because the build tool and the deployer are already set up.
The deployment itself will run quite fast because only service instances and content are deployed. There is no CF application at all.

Running Standalone

After the deployment the app can be run standalone from its index.html via the managed approuter. You can find it in the SAP BTP cockpit on the HTML5 Applications page under your subaccount. Its URL will look like this:

https://<tenant subdomain>.launchpad.cfapps.<landscape host>/<destination instance guid>.<service name>.<sap.app/Id>-/index.html
Service name and sap.app/id have any dots removed. Example for a URL:
https://xyz.launchpad.cfapps.eu10.hana.ondemand.com/8b1f65d4-0445-446f-a4b5-90627416789e.btp-samples-simple-app.btpsamplessimpleapp-1.0.0/index.html

All routings in xs-app.json are relative to this. For example, the OData service is under
https://xyz.launchpad.cfapps.eu10.hana.ondemand.com/8b1f65d4-0445-446f-a4b5-90627416789e.btp-samples-simple-app.btpsamplessimpleapp-1.0.0/northwind/
There is no way to install a routing directly in the app router root. Therefore, it is important that you always use paths that are relative to the app location.

The index.html which is located next to the app defines the resource mapping for it and points to the same folder:

<script id="sap-ui-bootstrap"
    src="https://ui5.sap.com/resources/sap-ui-cachebuster/sap-ui-core.js"
    data-sap-ui-resourceroots='{
        "btp.samples.simple.app": "./"
    }'
    data-sap-ui-libraries="sap.m" >
</script>

Inside the manifest.json the data source path is automatically resolved relative to the app.

Beware that there are other locations, where this automatic resolution does not happen, for example when you do an AJAX request via coding or specify an URL in a control property like the src of an image. Our app contains an image control in the header area that shows a logo from a local file to demonstrate this.
Here the browser would resolve an URL relative to the document location which is the index.html. Using simple hardcoded URLs only works, as long as the app is located next to the index.html.
Why this can cause issues and how to solve this will be explained below.

The index.html loads UI5 via a public CDN URL which has cachebuster enabled. This ensures the best startup performance, because the resources will be fetched from the closest location and cached in the browser cache.

Running in the Launchpad Service

In order to run the app in the Launchpad Service, you have to configure it first and assign it to a user role. You can find more details on this here: Integrate Your SAPUI5 App into Your Launchpad Site

After everything is set up the app could be launched directly via an URL with this pattern:
https://<tenant subdomain>.launchpad.cfapps.<landscape host>/site?siteId=<site ID>#<semantic object>-<action>, for example:
https://xyz.launchpad.cfapps.eu10.hana.ondemand.com/site?siteId=df526ffe-2a32-464e-8fc5-5d5db908334e#simpleApp-display

As you can see, the URL points to a completely different path than where the app can be found. Also the startup HTML page does not contain the resource mapping as in the standalone case.
In order to launch the app, the platform provides all needed URLs. This information is retrieved via a request which looks like
/comsapfdc/fdcCache_<subaccountid (- replaced by _)>/~<cache token>~/apps/<sap.app/id>/ui5AppInfo.json
You can find this in the network trace when starting the app.
In this data you can find a similar URL as when running the app standalone, but it has an additional cache token, which is the date of the last change.

{
	"name": "btp.samples.simple.app",
	"url": "/8b1f65d4-0445-446f-a4b5-90627416789e.btp-samples-simple-app.btpsamplessimpleapp/~211021122629+0000~/",
	"manifest": true,
	"asyncHints": {
		"libs": [{
			"name": "sap.f",
			"lazy": false
		}, {
			"name": "sap.m",
			"lazy": false
		}, {
			"name": "sap.ui.fl",
			"lazy": false
		}],
		"components": []
	},
	"messages": [],
	"version": "1.0.0"
}

As mentioned before, some hardcoded URLs would be resolved relative to the document location. In this case this would not result in the correct URL for fetching the logo image.
Therefore the URL is calculated in the Master.controller.js as follows:

sap.ui.require.toUrl("btp/samples/simple/app/images/logo_ui5.png")

This way your app is independent of the location from where it is launched.

Next Steps

Now you are ready to plan your first simple app project.
However, usually you need more apps and reuse components. Watch out for the next part coming soon.

Assigned Tags

      1 Comment
      You must be Logged on to comment or reply to a post.
      Author's profile photo Fabien HENIQUE
      Fabien HENIQUE

      Hello Matthias,

      This really well explained and I’m already waiting for next parts 🙂

      I only have one thing I’m trying to figure out, do you know how to use a fixed version of UI5? I put a fixed version in index.html and deploy-yaml, but UI5 version is always the latest one.