Skip to Content
Technical Articles

Deploying a CAP based Fiori app to a central Launchpad

Introduction

In my previous blog, I wrote about how to create a CAP based Fiori elements app and deploy it to Cloud Foundry. There has been several changes since then:

  • Fiori elements supports OData V4 (List Report and Object Page)
  • Fiori tools has data source option “Local CAP Node.js Project”
  • SAP Cloud Platform Launchpad service (central Launchpad) has been released

In my view, the third one is the most significant change because you no longer need Approuter, Deployer and Launchpad modules in your MTA project.

To know more about the Launchpad service and how to develop applications for central Launchpad, I recommend reading the following blog posts.

In this blog, I’m going to demonstrate how to develop a CAP based Fiori app and deploy it to a central Launchpad.

The source code is available at GitHub.

Prerequisites

  • Launchpad service subscription -> tutorial
  • SAP Business Application studio (space type: SAP Cloud Business Application)

Steps

  1. Create a CAP project
  2. Add Fiori elements app
  3. Generate mta.yaml
  4. Add XSUAA configuration
  5. Build & Deploy
  6. Configure the Launchpad

1. Create a CAP project

1.1. Generate a new CAP project.

cds init cap-launchpad

1.2. Add sample schema and service by the following command. This generates simple db and service definitions and also mock data. It is enough for demonstration purpose.

cds add samples

samples

1.3. Execute below commands and run the service locally.

npm install
cds watch

1.4. Add UI annotations to srv/cat-service.cds.

annotate CatalogService.Books with @(
    UI : { 
        SelectionFields  : [
            title
        ],
        LineItem  : [
            { Value : ID },
            { Value : title }, 
            { Value : stock }                                   
        ],
     }
){
    ID @( title: 'ID' );    
    title @( title: 'Title' );
    stock @( title: 'Stock' );
};

Fiori preview will look like this.

 

2. Add Fiori elements app

2.1. Generate Fiori elements app using Fiori tools

2.1.1. Open Yoman UI generator and select “SAP Fiori elements application”.

2.1.2. Select “List Report Object Page”.

2.1.3. Chose “Use a Local CAP Node.js Project” as Data source and select your CAP project folder and OData service.

2.1.4. Select “Books” as Main Entity.

2.1.5. Type below information.

Module Name fiori
Title Books
Namespace demo
Description Books
Configure advanced options No

Press finish and the Fiori app will be added to app folder.
You might notice that the generated project structure is different from normal UI5 applications, for example, it doesn’t have ui5.yaml, package.json, etc. We will add these files in later steps.

Refresh the browser and see that the link to the Fiori app has been added.

 

2.2. Make adjustments for deploying to Cloud Foundry

Next, let’s make some adjustments so that the app can be deployed to Cloud Foundry as a central Launchpad content.

2.2.1. Add fiori/package.json.

*I copied package.json and ui5.yaml files from a project generated by yo fiori-project generator.

{
  "name": "fiori",
  "version": "0.0.1",
  "devDependencies": {
    "@sapui5/ts-types": "1.71.x",
    "@ui5/cli": "2.2.6",
    "@sap/ui5-builder-webide-extension": "1.0.x",
    "bestzip": "2.1.4",
    "rimraf": "3.0.2"
  },
  "scripts": {
    "build": "npm run clean && ui5 build --include-task=generateManifestBundle generateCachebusterInfo && npm run zip",
    "zip": "cd dist && npx bestzip ../fiori-content.zip *",
    "clean": "npx rimraf fiori-content.zip dist"
  },
  "ui5": {
    "dependencies": [
      "@sap/ui5-builder-webide-extension"
    ]
  }
}

 

2.2.2. Add fiori/ui5.yaml.

specVersion: '1.0'
metadata:
  name: fiori
type: application
resources:
  configuration:
    propertiesFileSourceEncoding: UTF-8
builder:
  resources:
    excludes:
      - "/test/**"
      - "/localService/**"
  customTasks:
  - name: webide-extension-task-updateManifestJson
    afterTask: generateVersionInfo
    configuration:
      appFolder: webapp
      destDir: dist
  - name: webide-extension-task-resources
    afterTask: webide-extension-task-updateManifestJson
    configuration:
      nameSpace: ns
  - name: webide-extension-task-copyFile
    afterTask: webide-extension-task-resources
    configuration:
      srcFile: "/xs-app.json"
      destFile: "/xs-app.json"

2.2.3. Add fiori/xs-app.json.

*We will define the destination “cap-launchpad” later.

{
  "welcomeFile": "/index.html",
  "authenticationMethod": "route",
  "logout": {
    "logoutEndpoint": "/do/logout"
  },
  "routes": [
    {
      "authenticationType": "none",
      "csrfProtection": false,
      "source": "^/catalog/",
      "destination": "cap-launchpad"
    },
    {
      "source": "^(.*)$",
      "target": "$1",
      "service": "html5-apps-repo-rt",
      "authenticationType": "xsuaa"
    }
  ]
}

2.2.4. Modify fiori/webapp/manifest.json.

We will make the following changes here.

  • Change the OData path from absolute to relative
        "dataSources": {
            "mainService": {
                // before "uri": "catalog/",
                "uri": "catalog/",
  • Add inbound navigation
    "sap.app": {
        ...
        "crossNavigation": {
            "inbounds": {
                "fe-inbound": {
                    "signature": {
                        "parameters": {},
                        "additionalParameters": "allowed"
                    },
                    "semanticObject": "Sample",
                    "action": "display",
                    "title": "CAP Sample App",
                    "subTitle": "",
                    "icon": ""
                }
            }   
        }  
    },
  • Add sap.cloud.service configuration

This service name needs to be unique within your account and will appear in the runtime URL of the application. For example, /<sap.cloud.service>.<app.id>/<pathToFile>

    "sap.app": {
        ... 
    },
    "sap.cloud": {
        "public": true,
        "service": "my_service"
    },     

 

3. Generate mta.yaml

3.1. Generate mta.yaml.

Go back to your project root and execute the following command.

cds add mta

Next, we will add modules and resources to mta.yaml.

3.2. Add destination-content module.

Here you define destinations and service keys for the destinations. After the MTA app has been deployed, you will see two destinations “…html_repo_host” and “…_uaa_fioir” in your subaccount.

Note that the service name “my_service” which we defined in manifest.json appears at cap.cloud.service section.

modules:
 - name: fiori-destination-content
   type: com.sap.application.content
   requires:
   - name: uaa_fiori
     parameters:
       service-key:
         name: uaa_fiori-key
   - name: fiori_html_repo_host
     parameters:
       service-key:
         name: fiori_html_repo_host-key
   - name: fiori-destination-service
     parameters:
       content-target: true        
   parameters:
     content:
       subaccount:
         destinations:
         - Name: my_service_fiori_html_repo_host
           ServiceInstanceName: fiori-html5-app-host-service
           ServiceKeyName: fiori_html_repo_host-key
           sap.cloud.service: my_service
         - Authentication: OAuth2UserTokenExchange
           Name: my_service_uaa_fiori
           ServiceInstanceName: fiori-xsuaa-service
           ServiceKeyName: uaa_fiori-key
           sap.cloud.service: my_service
         existing_destinations_policy: update    
   build-parameters:
     no-source: true

 

3.3. Add ui_deployer.

It uploads zipped web application content into the HTML5 Application Repository.

 - name: fiori_ui_deployer
   type: com.sap.application.content
   path: .
   requires:
   - name: fiori_html_repo_host
     parameters:
       content-target: true
   build-parameters:
     build-result: resources
     requires:
     - artifacts:
       - fiori-content.zip
       name: fiori
       target-path: resources/

Next to it we specify where “fiori” module is located and its build parameters.

 - name: fiori
   type: html5
   path: app/fiori
   build-parameters:
     builder: custom
     commands:
     - npm run build
     supported-platforms: []   

 

3.4. Add resources.

We will add 3 types of services. Destination, html5-apps-repo, and xsuaa.

resources:
...
 - name: fiori-destination-service
   type: org.cloudfoundry.managed-service
   requires:
     - name: srv-api
   parameters:
     service: destination
     service-name: fiori-destination-service
     service-plan: lite
     config:
       init_data:
         subaccount:
           existing_destinations_policy: update
           destinations:
             - Name: cap-launchpad
               Description: CAP sample service
               Authentication: NoAuthentication
               ProxyType: Internet
               Type: HTTP
               URL: ~{srv-api/srv-url}
               HTML5.DynamicDestination: true
               ForwardAuthToken: true
       
 - name: fiori_html_repo_host
   type: org.cloudfoundry.managed-service
   parameters:
     service: html5-apps-repo
     service-name: fiori-html5-app-host-service
     service-plan: app-host
 - name: uaa_fiori
   type: org.cloudfoundry.managed-service
   parameters:
     path: ./xs-security.json
     service: xsuaa
     service-name: fiori-xsuaa-service
     service-plan: application

 

Along with the destination service, we will create a new destination “cap-launchpad” (this is the destination name we specified in xs-app.json file).

In the old approach (as described in my previous blog), we would directly consume CAP service url in Approuter module with the following way. In the new approach, we don’t have Approuter, so direct consumption of the service URL is not possible. That’s why we need a destination pointing to the CAP service url.

//old approach
 - name: bookshop-app-router
   type: approuter.nodejs
   path: approuter
   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  

In this document you can find destination configuration in JSON format. I have put the JSON content to mta.yaml file, so that I can consume service url exposed by the CAP service.

 

4. Add XSUAA configuration

This is the last step before deploy. Add xs-security.json to the project’s root. This file is referenced by the xsuaa service during deployment.

{
  "xsappname": "fiori",
  "tenant-mode": "dedicated",
  "description": "Security profile of called application",
  "scopes": [
    {
      "name": "uaa.user",
      "description": "UAA"
    }
  ],
  "role-templates": [
    {
      "name": "Token_Exchange",
      "description": "UAA",
      "scope-references": [
        "uaa.user"
      ]
    }
  ]
}

 

5. Build & Deploy

Finally, run below commands to build and deploy the MTA app to Cloud Foundry.

mbt build
cf deploy mta_archives/cap-launchpad_1.0.0.mtar

Note: Make sure you have installed dependencies for the fiori app before executing build.

cd app/fiori
npm install

 

After successful deployment, you will see 3 destinations created in your subaccount.

 

6. Configure the Launchpad

6.1. Go to your Launchpad provider manager and refresh the content.

6.2. Go to content explorer and add the newly created app to content.

6.3. Create a group and assign the app to the group.

6.4. Also, assign the app to Everyone role.

Finally, you can open your Fiori app from the Launchpad.

 

Conclusion

Compared to my previous blog, fewer objects are required for deploying the app to the Launchpad.

Below objects are no longer required.

  • OData v2 proxy
  • Approuter
  • HTML5 Deployer
  • Launchpad Module

Thanks to this, build and deploy has become much faster.

 

What’s Next?

You can add authentication to CAP service by following the steps in my next blog.

Deploying a CAP based Fiori app to a central Launchpad – Part2: Add Authentication

 

28 Comments
You must be Logged on to comment or reply to a post.
  • Thanks for the new tutorial,

    But when i’m trying to deploy all i get message:
    “Skipping deletion of services, because the command line option “–delete-services” is not specified.”

    so i tried cloning your Github project and deploy it as a sample project, but i get the same error,

    and more then that, on the CF i see only 2 applications, the “srv” & the “db-deployer” app (also in my first deploy).

    Any help?

    • Hi ELI NAIM,

      Regarding the message, that’s not an error.

      If you deploy with  “cf deploy <mta_archives> –delete-services”, it will first delete all the services associated with your project, and re-create the services. If you don’t specivy “–delete-services” option, you will receive this message, but that’s fine.

      Second, you don’t see HTML5 apps from applications, but you can see them with command “cf html5-list -u -d”. (You may need to install CF HTML5 Applications Repository CLI Plugin)

      Can you see your app content from Launchpad as described in Step6?

       

       

       

      • thanks, i’v added “–delete-services”.

        But step 6 is is not clear for me, since i am not a subaccount manager and i dont think i have access for “Launchpad provider manager”. Im working on a DEV subaccount (not trail)

        i have exec “cf html5-list -u -d”, and i see “demofiori” app, but when i click on it i get 400 error.
        So you have any idea whats next?🙄

         

  • Dear Mio,

    There is a minor typo in the CDS command line statemet to generate the sample CAP project.

    cds add samles instead of cds add samples

    in case you want to correct it.

     

    Thanks for sharing the content!

     

     

     

  • Happy New Year!

    Should I see the destination cap-launchpad appear in my subaccount destinations? Unfortunately it doesn’t in my deployment. 

    • Happy New Year Gregor Wolf!

      Yes, you should see the destination cap-launchpad. It is created from the following configuration in mta.yaml.

      config:
             init_data:
               subaccount:
                 existing_destinations_policy: update
                 destinations:
                   - Name: cap-launchpad
                     Description: CAP sample service
                     Authentication: NoAuthentication
                     ProxyType: Internet
                     Type: HTTP
                     URL: ~{srv-api/srv-url}
                     HTML5.DynamicDestination: true
                     ForwardAuthToken: true
      • It didn’t create the destination for me. Can you try to proof it? After creating the destination manually all worked fine for me. But of course I want to have also that part automated.

        • Please try the following steps.

          1.Clone my git repository.

          //If you deploy to HANA Cloud
          git clone https://github.com/miyasuta/central-launchpad-cap.git
          //If you deploy to HANA Service
          git clone -b hana-service https://github.com/miyasuta/central-launchpad-cap.git

          2. Install dependency

          //Install dependencies for the root project
          npm install
          //Install dependencies for fiori app
          cd app/fiori
          npm install
          

          3. Build & deploy

          mbt build
          cf deploy mta_archives/cap-launchpad_1.0.0.mtar

          For me after completing above steps, the destination was created.

    • Hi Gregor Wolf ,

      The same happens to me after adapting my own project following the instructions. Did you get it in your own project? Did you find what caused it?

      Thanks in advance.

      Best regards,

      Marc

       

      • I’ve followed Mio’s instructions but the destination wasn’t created. My workaround was to create the destination manually. Haven’t investigated further.

        • Hi Gregor and Mio Yasutake ,

          Solved.

          The fiori-destination-service resource definition is correct in the Github repository, but there is an error in the post text.

          In GitHub:

           - name: fiori-destination-service
             type: org.cloudfoundry.managed-service
             requires:
               - name: srv-api
             parameters:
              service: destination
              service-name: fiori-destination-service
              service-plan: lite
              config:
                  init_data:
                      subaccount:

          In the post text, the “config” tag is missing:

           - name: fiori-destination-service
             type: org.cloudfoundry.managed-service
             requires:
               - name: srv-api
             parameters:
               service: destination
               service-name: fiori-destination-service
               service-plan: lite
               init_data:
                 subaccount:

          Thanks for your quick answer, Gregor.

          Best regards,

          Marc

           

           

    • I have tried below steps to add livereload, but, it’s not working.

      1. Add the following script to package.json.

      "start": "ui5 serve --open index.html"

      2. Add the following modules to dev dependency and configure ui5.yaml. (See documentations)

      3. Run the command ‘npm start’

      Although the metadata is loaded, when I press ‘Go’ button, the following error is received in console.

      Uncaught TypeError: Cannot create property '@odata.etag' on string

       

      An alternative is to select “Connect to an OData service” option in step 2.1.3. (instead of “Use a Local CAP Node.js Project”)

      The app generated with this option comes with middleware fiori-tools-appreload with does the same job as ui5-middleware-livereload. If you want to deploy the app generated by this option, you need to adjust build command in package.json (I haven’t tried this yet).

  • Dear Mio,

     

    In Step 5, Build & Deploy, can you add a note to install node dependencies for the Fiori app. Without performing that step, the build will fail.

    Thanks

    Anubhav

     

  • Hi Mio,

    thank you very much for sharing your knowledge. With your previous blog, I learned how to add a launchpad module to my CAP app and I am now learning how to integrate the new launchpad service. Amazing blogs!

    Just a question about the Approuter module. You mentioned that Approuter is no longer needed (in this scenario), but is needed, for example, in multitenant applications, being required for onboarding consumer tenants.

    My question is, given that my multitenant CAP application already needs an Approuter module, I would like to use it instead of creating a new destination (cap-launchpad in your example). Is there any reason to avoid this approach?

    Thanks in advance.

    Best regards,

    Marc

     

    • Hi Marc,

      Thanks for your positive comment.

      I haven’t created multitenant app myself, so I’m not confident to answer your question, but in Marius’s blog comment he says that there can be many good reasons to continue using an approuter. Depending your use cases, you may find it more appropriate to use Approuter.

      Regards,

      Mio

  • Hello,

    This blog looks amazing, I’m trying to follow the steps but I’m lost at

    step 3.2

    Here you define destinations and service keys for the destinations. After the MTA app has been deployed, you will see two destinations “…html_repo_host” and “…_uaa_fioir” in your subaccount.

    Is there a command missing in the guide? So far I only see that we change the source code to deploy afterwards, or did I miss something?

  • No, I tried to follow the guide on the blog, but I will have a look at it.

    I think I got confused that we are talking of the deploy from step 5 in the step 3.2, thanks 🙂

  • Hi everyone,

     

    thank you for that very helpful post.

    during the build command I am receiving the error :

    Error Message:
    [npm translator] Could not locate module @sap/ui5-builder-webide-extension via resolve logic (error: Cannot find module ‘@sap/ui5-builder-webide-extension/package.json’ from ‘/home/user/projects/cap-launchpad/app/fiori’) or in a collection

    I can not find any hint in the web what this is caused by. package.json file is maintained correctly. Do you have any idea?

    Best Regards

    Max

    • Hi Max,

      Thanks for your comment.

      Regarding the error, have you installed dependencies for fiori app?

      If not, please try the following commands.

      cd app/fiori
      npm install

      Best regards,

      Mio

  • cool thanks. that solved it:)

    one further question: as soon as I deploy 2 html5 apps within the MTA project Im only able to view and access one app via the launchpad service. does that mean app router is still required as soon as you have multiple html5 apps?

    BR
    Max

    • When you have multiple html5 apps, I guess you only need multiple destinations pointing to each app. You need Approuter if your deployment target is not the launchpad.