Skip to Content
Technical Articles
Author's profile photo Marius Obert

Serverless SAP Fiori Apps on SAP BTP, Cloud Foundry environment

In this post, I show-case the latest innovation for web apps in SAP BTP, Cloud Foundry environment – hosting them without an approuter. With this new capability of the SAP Launchpad service, you no longer need to deploy backend components to host web apps – which, therefore, run serverless on SAP BTP.

Updates:

29th Jan 2021: Rebranding to SAP BTP

16th Feb 2021: Switch to instance-level destinations

22nd Mar 2021: Performance tip

Now that the HTML5 Application Repository gets more and more popular, I received a couple of questions about whether it is possible to use only one approuter for multiple web apps to lower the total cost of ownership (TCO). The good news is that this can be done fairly easily. The even better news is that you don’t need an approuter at all! The existing SAP Cloud Portal subscription and the new SAP Launchpad subscription service already come with an integrated HTML5 application runtime that will allow you to access web apps directly via the Launchpad URL. In this blog post, I will explain how to deploy regular web apps and SAP Fiori apps and let them be managed by SAP BTP, Cloud Foundry runtime (Check out this post if you want to do the same in the Kyma runtime). Fiori Apps can then easily be found by the Content Explorer of the Launchpad service and directly be added to a Launchpad.

The Content Explorer of the Launchpad service detected an available SAP Fiori app automatically and suggests adding it to the SAP Fiori Launchpad.

Old state

Several things need to happen in SAP BTP, Cloud Foundry environment to run a web app. For once, the application needs to be hosted somewhere. As I explained in a talk earlier this year, many reasons speak for the HTML5 Application Repository service. Therefore, we need to (a) upload the web app into this repository during the deployment. Next, we need an approuter to (b) redirect incoming traffic to the web app that resides in the repository service. If the web app is an SAP Fiori application, we might have used the Cloud Foundry service “portal” to embed it in an SAP Fiori Launchpad. For this, we needed a (c) deployer application to upload the configuration file to the service instance bound to the approuter. For most apps, there are also destinations involved which point to the backend systems that provide data. So we also need to (d) maintain destination in the SAP BTP Cockpit. 

a) https://www.npmjs.com/package/@sap/approuter

b) https://www.npmjs.com/package/@sap/html5-app-deployer

c) https://www.npmjs.com/package/@sap/portal-cf-content-deployer

d) <Done manually in the SAP BTP Cockpit>

The architecture of the old approach

For two (b and c) out of these four tasks, we typically leverage the Cloud Foundry tasks, which are Node.js modules. These modules run for a short time and stop once the job is completed. During this time, they consume memory and CPU resources, which has a small (but present) impact on your TCO. The most immense impact on the TCO is caused by the approuter. It runs 24/7 and scales with the complexity and usage of the web app. In many scenarios, developers use even one approuter per web app, which multiplies the TCO.

This also explains why I got asked by multiple developers, whether it’s possible to use a single approuter for multiple SAP Fiori apps. In the next section, I will show you something even better: You’ll be able to serve your web apps with NO backend component at all.

Make your web apps “serverless”

The core idea is quite simple: The SAP Fiori Launchpad itself also a web app that is connected to an HTML5 application runtime. The advantage of that service is that it’s hosted by SAP for you, so you don’t need to manage the HTML5 application runtime, and you can simply consume it.

This post described how you can use the SAP Business Application Studio to create a new SAPUI5 app managed by SAP BTP out-of-the-box.

The URL of your SAP Fiori Launchpad service typically looks like this:

https://<yourID>.launchpad.cfapps.<region>.hana.ondemand.com/site?siteId=<siteID>#Shell-home

We can now reuse the approuter of this application and make our web apps accessible under:

https://<yourID>.launchpad.cfapps.<region>.hana.ondemand.com/<myService>.<appID>-<version>

As you can see, each web app needs to contain a unique appId, the version, and refer to a service to form this URL – all three values are maintained in the manifest.json . If you want to build such a web app “by hand,” start with a minimal version of this manifest.json file:

{
  "_version": "1.12.0",
  "sap.app": {
    "id": "helloworld",
    "applicationVersion": {
      "version": "1.0.0"
    }
  },
  "sap.cloud": {
    "service": "basic.service"
  },
  "sap.ui5": {
    "dependencies": {
        "minUI5Version": "1.65.0"
    }
  }
}

Besides this, you need to create service keys for certain Cloud Foundry service instances. These keys are necessary for destinations that are created during the deployment of your application. You can use the following snippet to define the keys and destinations in your mta.yamlfile.

modules:
  - name: hello-world-destination-content
    type: com.sap.application.content
    build-parameters:
      no-source: true
    requires:
      - name: hello-world_uaa
        parameters:
          service-key:
            name: hello-world_uaa-key
      - name: hello-world_html_repo_host
        parameters:
          service-key:
            name: hello-world_html_repo_host-key
      - name: hello-world-destination-service
        parameters:
          content-target: true
    parameters:
          content:
            instance:
              existing_destinations_policy: update
              destinations:
                - Name: my_service_hello_world_html_repo_host
                  ServiceInstanceName: hello-world_html_repo_host
                  ServiceKeyName: hello-world_html_repo_host-key
                  sap.cloud.service: basic.service
                - Authentication: OAuth2UserTokenExchange
                  Name: my_service_uaa_hello_world
                  ServiceInstanceName: hello-world_uaa
                  ServiceKeyName: uaa_hello-world-key
                  sap.cloud.service: basic.service
    ...

resources:
  - name: hello-world-destination-service
    type: org.cloudfoundry.managed-service
    parameters:
      service: destination
      service-name: hello-world-destination-service
      service-plan: lite
  - name: hello-world_html_repo_host
    type: org.cloudfoundry.managed-service
    parameters:
      service: html5-apps-repo
      service-plan: app-host
  - name: hello-world_uaa
    type: org.cloudfoundry.managed-service
    parameters:
      path: ./xs-security.json
      service: xsuaa
      service-plan: application

You can now remove the approuter module from your project as you don’t need it anymore. This helps you a lot to reduce the TCO footprint of your project. In case you defined routes on the approuter ( router/xs-app.json ), you need to copy these routes to the xs-app.json file of your web app. When you do this, you move the route to the URL suffix of the web app. E.g. the URL will be

https://<yourID>.launchpad.cfapps.<region>.hana.ondemand.com/<myService>.<appID>-<version>/route1

instead of

https://<yourID>.launchpad.cfapps.<region>.hana.ondemand.com/route1

To account for this change, you need to switch from absolute to relative URLs in your web app. Most likely you’ll need to make changes to the datasources property in the manifest.json.

"sap.app": {
    "dataSources": {
        "mainService": {
            // OLD: "uri": "/v2/Northwind/Northwind.svc/",
            "uri": "v2/Northwind/Northwind.svc/",

You can find the entire sample project on GitHub if you want to clone it from there. Once you run the typical build and deploy commands, you will be able to see your web app listed under “HTML5 Applications” on your subaccount.

The%20cockpit%20shows%20a%20list%20of%20all%20web%20apps%20that%20are%20available%20in%20this%20subaccount

The cockpit shows a list of all web apps that are available in this subaccount.

Note: This UI is not available in trial accounts at the moment. You will only be able to see this in production accounts!

Click on the application name to open the web app. You’ll notice that the URL is different than the URL of typical approuter apps because there is not Cloud Foundry application involved here. 

Deploy without a Cloud Foundry task

In the old approach, we used CF modules of type com.sap.html5.application-content to zip and upload the static web app resources to the HTML5 Application Repository. In the new approach, we will use the native GACD interface to upload the web application directly into the HTML5 Application Repository. This means we don’t need a Cloud Foundry task for b) any longer, and therefore it won’t consume memory and CPU.

For this, we need to make sure the web app is zipped during the build process. This can be achieved in multiple ways, either with a package from npm or with this custom task for the UI5 tooling builder.

Once the zip archive has been created, you just to reference it from the new MTA module:

modules:
  - name: hello-world_ui_deployer
    type: com.sap.application.content
    path: .
    requires:
      - name: hello-world_html_repo_host
        parameters:
          content-target: true
    build-parameters:
      build-result: resources
      requires:
        - artifacts:
            - HTML5Module-content.zip
          name: HTML5Module
          target-path: resources/

Notice that the module type has been changed and that there is no package.json file needed anymore. With this, you reduced the footprint of your overall projects a little more.

Expose SAP Fiori apps in the Content Explorer

In this new approach, we won’t be using the “developer sandbox” of the Fiori Launchpad (aka the Portal service instance) but the actual management UI that comes with the full Launchpad subscription. In other words, we don’t need the service instance in the mta.yaml file, and therefore task c) is also out of the game. We just made the footprint of the project a little bit smaller.

Your Fiori app needs to fulfill the following criteria when you want to make it accessible via the Content Explorer:

  1. Contain an inbound navigation intent in the manifest.
    {
      "sap.app": {
          "crossNavigation": {
              "inbounds": {
                  "fe-inbound": {
                      "signature": {
                          "parameters": {},
                          "additionalParameters": "allowed"
                      },
                      "semanticObject": "masterDetail",
                      "action": "display",
                      "title": "Available Categories",
                      "subTitle": "",
                      "icon": ""
                  }
     ​
  2. Include a “manifest-bundle.zip” file that includes the manifest.

Redeploy your project after you made these changes. Now, navigate to the management user interface for the Launchpad subscription and go to the Provider Manager. You should see a content provider named “HTML5 Apps“, use the refresh button to sync with the HTML5 Application Repository. You’ll also need to click this button every time when a changed application should be updated on the launchpad site.

The%20HTML5%20Application%20Repository%20as%20a%20Content%20Provider

The HTML5 Application Repository as a Content Provider

Next, switch over to the Content Manager and choose the tab Content Explorer. You are now able to see your Fiori App and to click the “Add to My Content” button.

The%20Content%20Explorer%20found%20one%20SAP%20Fiori%20app

The Content Explorer found one SAP Fiori app.

 

Congrats! You have now removed all Cloud Foundry modules that consume memory and CPU from your project and therefore reduced the footprint of your SAP Fiori app significantly.

 

The architecture of the new approach

You can find an entire sample Fiori app in our Multi-Cloud HTML5 Apps Repo on GitHub.

Or you can follow this tutorial to write new SAP Fiori apps from the scratch with easy-ui5.

Destinations as code

Infrastructure as code is used to define compute resources in a text file and to provision them automatically when needed. Similarly, this feature can relieve you of configuring the destinations manually in the SAP BTP Cockpit and automating your development flow.

I recently learned that you can define destinations in a JSON file, similar to the xs-security.json file that we already know. This feature can be used to declutter the destinations on the subaccount level and move them to the service instance-level. You only need to make sure that HTML5 runtime is enabled for the service instance and that the update strategy of the destinations is defined in the configuration file:

{
  "HTML5Runtime_enabled": true,
  "version": "1.0.0",
  "init_data": {
    "instance": {
      "existing_destinations_policy": "update",
      "destinations": [
        {
          "Name": "Northwind",
          "Description": "Automatically generated Northwind destination",
          "Authentication": "NoAuthentication",
          "ProxyType": "Internet",
          "Type": "HTTP",
          "URL": "https://services.odata.org",
          "HTML5.DynamicDestination": true
        }
      ]
    }
  }
}

Do not add credentials in such a file because then you might accidentally include them in the git history of your repo.

And last but not least, you need to include this JSON file in the resource definition of the destination service instance in the mta.yaml file:

resources:
  - name: mock-destination
    type: org.cloudfoundry.managed-service
    parameters:
      service-plan: lite
      service: destination
      path: ./destinations.json

More information on this can be found in the documentation.

 

So task d) from above is not needed any longer either. With this, we got rid of all tasks that required manual work or consumed compute resources to deploy a web app. Hurray!

You can find an entire sample Fiori app in our Multi-Cloud HTML5 Apps Repo on GitHub.

Hands-On

Check out this video tutorial which will show you how to use the managed application router in SAP Business Application Studio:

Watch it here

Advantages

I already mentioned it a couple of times across this post; the most significant advantage of this approach is the reduced footprint that saves you money. But this is, by far, not the only benefit. It also helps you scale your web apps because you won’t need to decide how much memory you want to allocate to the approuter. By reducing the included modules, you can simplify the architecture of the project significantly and, therefore, also reduce the planning overhead when designing such a Fiori App. This simplified architecture also accelerates the deployment. It now only takes about 30 seconds to deploy a Fiori Application and to create the needed service keys and destinations, whereby it took a few minutes and required manual configuration in the past.

Disadvantages

Even though many reasons speak for the new approach, there are also a couple of reasons that speak for an approuter that is entirely under your control. One reason might be that you prefer to keep the routes on the approuter-level. Or maybe you want to configure or extend the default approuter. It could also make sense to keep the approuter combined with servers written with the Cloud Application Programming Model.

Performance tip

Requests for i18n files that return HTTP code 404 will slow down your web app. Many times the locale includes the country while the translation in the i18n doesn’t include the country causing the UI5 fallback and the many redundant 404.

So it is highly recommended to check the console logs and provide the missing translation files, providing also full locale language and country, to avoid the UI5 fallback logic.

For better performance, resource bundles are cached and these redundant calls to the server can be avoided.

Summary

In this post, you have learned:

  • that an approuter is no longer needed to host web apps in SAP BTP, Cloud Foundry environment
  • how to let SAP BTP manage your web apps
  • how to make an SAP Fiori app detectable via the Content Explorer
  • how to define destinations with text files
  • that it can still make sense to use an approuter

Assigned Tags

      89 Comments
      You must be Logged on to comment or reply to a post.
      Author's profile photo Pierre Dominique
      Pierre Dominique

      Thanks for sharing this Marius, we'll try this approach for our CAP + Fiori apps. I have one question though, what do you mean with this statement:

       It could also make sense to keep the approuter combined with servers written with the Cloud Application Programming Model.

      If I understand correctly we should have a CAP project with app (only the approuter), srv & db modules and a separate project for the Fiori app using the approach described in your blog?

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Pierre,

      awesome, I'd be happy to learn how much you like this new approach once you implemented it.

      What I meant to say is there can be many good reasons to continue using an approuter. Not more and not less than that 🙂

      Personally, I like to manage all modules of one project in one mono repo. E.g. you could have a project that includes CAP artifacts (approuter, srv, db) and one or more SAP Fiori Apps. As you already have an approuter in this scenario anyway, you could just use it for routing to your web apps as well.

      I guess it should also be possible to leave the approuter out entirely. Then you only had a srv and db module and you would need to add the "srv destination" somehow to your subaccount. But I haven't tried this yet so I don't want to promise too much now.

      I hope this answered your question.

      Author's profile photo Pierre Dominique
      Pierre Dominique

      Ok this makes sense. I'd be interested to know if we can get rid of the app router entirely, please let us know if you find a way to do it. 🙂

      Author's profile photo Maxim Kuul
      Maxim Kuul

      Again great article Marius, thank you for your contributions to the community!

      One thing I want to point out: you position this concept as "serverless" and I think it might be mixed up with Serverless Architecture like FaaS / BaaS which is sort of "backend upon request". In your case I guess what you mean is serving HTML5 from the approuter without another "serving" app required, but if you go for a "full" setup with DB, APIs etc. you would still need a proper backend / server. Please correct me if I'm wrong.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Maxim,

      I deliberately choose this bold title to create an association with FaaS. When talking about “serverless”, most people think of FaaS but to me, serverless is the more general concept of not having to worry about compute resources and perfect scalability. And this concept is also used here.

      You are right, in most of today’s use-cases with CAP, there will most-likey be a backend component such as the srv or db module. But that doesn’t need to be true by-default. You could also create destinations that point to the SAP Cloud Platform Serverless Runtime and then your entire stack would be serverless.

      Author's profile photo Maxim Kuul
      Maxim Kuul

      Great, thanks for the clarification Marius!

      Author's profile photo Jeremy Chabert
      Jeremy Chabert

      It works like a charm ! Thank you Marius.

      I finally found my mistake thanks to you.

      The issue was that the part sap.cloud was missing in my manifest.json.

      Best regards,

      Jérémy

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Awesome! Yes, this part is a little bit tricky, it took me a while to get it right too.

      Author's profile photo Petr Dubšík
      Petr Dubšík

      Is it possible to use UI5 application managed by CF SaaS approuter as a custom UI in Workflow?

      I`m currently struggling to make it work.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I have to admit that I haven't tried this because I don't work with the Workflow service. My gut feeling tells me that it should be possible.

      Author's profile photo Ari Lulu
      Ari Lulu

      Did you check the following documentation for Integrating SAP Cloud Platform Business Services:
      https://help.sap.com/viewer/ad4b9f0b14b0458cad9bd27bf435637d/Cloud/en-US/1b80373be4fc4cbb8a7529495c9c9d62.html

      Author's profile photo Vladimír Balko
      Vladimír Balko

      Yes, it is possible.

      Just define route in xs-app.json to the correct router. Thats it.

      You can also can use such apps (deployed to saas router) in port site.

      You have to make destination with SaaS approuter - same as your custom approuter and then just use that destination in an application tile definition.

      Author's profile photo Kivanc Aktas
      Kivanc Aktas

      Hi Marius,

      First of all thanks for your clean explanation and effort I’m really getting benefit from your blog series.

      I’m trying to create some structure and guidebook for migrating our current Fiori applications (Standard, extended and custom) from NEO to Cloud Foundry cloud portal/launchpad service. I’m able to create custom application and deploy with or without approuter with BAS but the issues are starting;

      • When I try to adapt traditional Fiori aplications to the cf portal. I can’t able to use yeoman ui generators because it is not compatible with your structure or existing Fiori app’s structure. (When I try to add new html5 module to your Fiori MTA than it fails to create mtar archive)
      • I was trying to follow official migration steps, that document was not clear enough but at least it was something and I can’t reach anymore till from this morning. Migrating HTML5 Applications from the SAP Cloud Platform Neo to the Cloud Foundry Environment
      • S/4 is not available for us yet and we would like to complete this migration. I will appricate if you guide me to apply best practice for complete this neo to cf migration.

      Kind regards,

      Kivanc

      Author's profile photo Ari Lulu
      Ari Lulu

      Hi Kivanc,

      The link to the migration guide can be found on the HTML5 Applications product page.

      Best Regards,

      Ari

       

       

       

      Author's profile photo Kivanc Aktas
      Kivanc Aktas

      Hi Ari,

       

      • When I try to reach first link that you have shared I'm getting 403 error.
        • 403
      • If I follow your second link and try to search html5 migration guide, it brings me the link that I shared on previous comment and it is not reachable anymore. (issue started this morning)

      Kind regards,

      Kivanc

      Author's profile photo Ari Lulu
      Ari Lulu

      Hi Kivanc,

      Please try again. It should be accessible now.

      Best Regards,

      Ari

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Kivanc,

      thanks for your nice feedback. I have to admit I'm not 100% sure which problem you are facing but these migration instructions might be what you are looking for.

      Author's profile photo Kivanc Aktas
      Kivanc Aktas

      Hi Marius,

      Thanks for your prompt response.

      I will try to migrate extended Fiori app (from NEO to CF) according to blog that you have shared, it looks promising.

      I tried to reproduce the other issue (it is not my primary focus for now) to explain you better. Briefly; I have cloned the repo , cd to managed-html5-runtime-fiori-mta and try to add new html5 module with:

      I'm using online SAP Business Application Studio because the local VS Code with yeoman generator extensions (Fiori) is not the same and has other major issues.

      $ yo fiori-module

      I’m selecting no auth and adding some flight service from our on-prem gateway.

      user: multi-cloud-html5-apps-samples $ cd managed-html5-runtime-fiori-mta/
      user: managed-html5-runtime-fiori-mta $ yo fiori-module
      ? Enter the module name sampleHTML5Module
      The "sampleHTML5Module" module will be generated in the "sampleHTML5Module" folder located at /home/user/projects/multi-cloud-html5-apps-samples/managed-html5-runtime-fiori-mta.
      ? Select the template you want to use SAPUI5 Application
      ? Do you want to add authentication? No
      ? Enter a namespace com.companyname
      ? Do you want to enable Karma tests? No
      ? Enter a view name View1
      ? Do you want to add a data service? Yes
      ? Select a system My SAP systems
      ? Select a source SOMEONPREMGATEWAY (Catalog)
      ? Select a service ZEXAMPLE_FLIGHT_SRV
      $ npm install
      $ npm run build

      The mta.yaml file is not correct anymore and it is giving the errors like below:

      2020-10-27 09:26:08] ERROR the "mta.yaml" file is not valid: 
      line 61: the "managed-html5-runtime-fiori-mta_html_repo_host" property set required by the "managed-html5-runtime-fiori-mta_ui_deployer" module is not defined
      make: *** [Makefile_20201027092608.mta:26: pre_validate] Error 1
      Error: could not build the MTA project: could not execute the "make -f Makefile_20201027092608.mta p=cf mtar= strict=true mode=" command: exit status 2
      npm ERR! code ELIFECYCLE
      npm ERR! errno 1
      npm ERR! managed-fiori@1.0.0 build: `mbt build`
      npm ERR! Exit status 1
      npm ERR! 
      npm ERR! Failed at the managed-fiori@1.0.0 build script.
      npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
      
      npm ERR! A complete log of this run can be found in:

       

      I’m aware that it can be fixed with editing mta.yaml manually but my target is avoiding manual editing and create some standard guide and use existing SAP standard tools, with this approach all developers can able to follow this standards to create/extend/migrate different kind of Fiori apps on the CF Portal/Launchpad.

      Kind regards,

      Kivanc

      Author's profile photo Umar Rauf
      Umar Rauf

      Hi Marius,

      Thanks for the great blog! When you pull an app via the content provider HTML5 Apps, it doesn't allow you to change the app/tile settings. Is there a way to make such changes? In my scenario, I'd like to setup a couple of tiles for the same app, each passing a different set of params to the UI5 app to control its behavior.

      Thanks,

      Umar

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Umar,

       

      this is a good question. I'll forward the question to a development expert and get back to you.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I received the feedback that this is not possible at the moment 🙁
      A workaround could be the usage of multiple apps with slightly different manifest files or using the old approach for now.

      Author's profile photo Umar Rauf
      Umar Rauf

      Thanks Marius for your quick feedback. This really helps demystify that challenge that I was facing. For now, we have created a couple of versions of the same app as suggested. However, it would be really great if at some point soon SAP addresses this issue to avoid maintenance of multiple versions of essentially the same app.

      Author's profile photo Bert Deterd
      Bert Deterd

      Struggling with the destination settings

      How does this work if you want a connection to an HANA Cloud oData service

      I do not know the url beforehand so how can I set this in mta.yaml / destinations.json ?

      Is there some placeholder / variable I can use from my module (gen/srv) ?

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Bert,

      a really good question! I have to confess that I haven't found a good way to this myself yet. Usually, when we do have an approuter, we inject this destination via the environment variable. But this option is not possible as there is no approuter any longer. The best solution imho would be to use the "srv_api" placeholder and to use it to configure a destination in the mta.yaml - but that would be a new feature that needs to be implemented first.

      I'm curious how you create the HANA Cloud OData service. Are you using CAP? And if so, do you have an approuter involved? It might make sense to use this one to inject the destination.

      If not, I would create the destination manually for now.

      I hope I could answer the question at least partially.

      Author's profile photo Bert Deterd
      Bert Deterd

      Hi Marius

      Seems to work now.

      I copied the destinations.json in your example into my mta.yaml file

      My odata-service-module has a section:

        provides:
           - name: kgk_srv_api
             properties:
               url: ${default-url}
      I am using this url property in my resource to create a destination with the correct url from my odata-service-module:
      ...
         Type: HTTP
         URL: ~{kgk_srv_api/url}
         HTML5.DynamicDestination: true
         ForwardAuthToken: true
      ...
      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Nice!

      Author's profile photo Markus Feifel
      Markus Feifel

      Hi everyone,

      I have a (beginner) question about the "xs-app.json" files. Did I understand it right that you have to define two "xs-app.json" files when you want to use the HTML5 Application Repository Service. One for the application router and one for the UI5 web application?
      If yes, which definition do you have to do in the file for the app router and which in the UI5 web application? Especially for the "routes" definition?

      Is it right, that you have to define all the destination and external routes in the "xs-app.json" for the app router and JUST the following in the "xs-app.json" for the UI5 web application:

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

      Thanks in advance,

      Markus

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Markus,

      If you want to go for the "managed approach", you only need one xsapp in the web app. You're right in case you want to use an approuter. In this case, it's up to you which xsapp file defines the route - both will be able to redirect traffic to the destinations you created. Be aware that the URL depend on the file that you defied them in.

      The section <Make your web apps “serverless”> explains this in more detail.

       

      Author's profile photo Markus Feifel
      Markus Feifel

      Thank you for the response, Marius.

      But is there a best practice where to put which route? Or is it really up to me and doesn't matter.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I'm not aware of an official best practice but I would recommend grouping the routes logically.

      Do you use one approuter for multiple (web) apps? Then that approuter should define the routes that all apps commonly share. Do you have routes that only one app needs? Then define them in the app itself.

      Author's profile photo Sergio Rozenszajn
      Sergio Rozenszajn

      Central approuter xs-app.json routes should be used for routes that are not html5 application dependent. For example if you use some backend application that also exposes a UI like x-ray or sap-analytics you can model the corresponding routes in central xs-app.json.

      Routes that are used by html5 applications (for example the html5 application backend), should be modeled in html5 application xs-app.json. The main reasons for this:

      a. html5 application and it's routes have a common lifecycle

      b. If you model routes in central xs-app.json, you will have to push and re-start the approuter on every route change, causing downtimes.

       

      Author's profile photo Bert Deterd
      Bert Deterd

      Can I make a non-fiori app also available in the Portal/LaunchPad?

      I created the minimal requirements for the manifest.json but the app is not getting visible in the Content Manager (also not if I refresh the saas_approuter)

      I am wondering if it is possible what I am trying to accomplish or is the LaunchPad / Portal strictly for Fiori apps?

       

       

       

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Bert,

      afaik this is not possible via the Content Provider that I show-cases in this post. But you can embed any URL in the Fiori Launchpad when you create the app manually.

      Author's profile photo Vladimír Balko
      Vladimír Balko

      hello Marius Obert ,

       

      where can I find log from that Saas approuter? In normal approuter you can go into space and in detail of approuter application, you can find events and logs. Is it possible to find something similar in saas one?

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I'm afraid this is not possible at the moment 🙁

      Author's profile photo Ari Lulu
      Ari Lulu

      Hi Balko,

      In your subaccount, in SAP Cloud Platform Cockpit, you can access HTML5 Applications UI (from the side-bar menu). For each application, it is possible to see the relevant runtime logs in the actions column.

      Please try it out and let me know if it covers your requirement.

      Best Regards,

      Ari

       

      Author's profile photo Vladimír Balko
      Vladimír Balko

      Hello Ari Lulu ,

      I`m aware of that possibility (logs for apps in html5 menu) but that logs are always empty - I dont understand why. So I guess, that should be some other logs.

      My problem is, that I have UI5 app deployed to SaaS app router and that app uses workflow API. I added wf into requires (into resources also), but I still got html 500 error when I try to call wf api.

      Here is part of my mta.yaml

      modules:
      - name: capexui-destination-content
        type: com.sap.application.content
        requires:
        - name: uaa_capex
          parameters:
            service-key:
              name: uaa_capexUI-key
        - name: capex_html_repo_host
          parameters:
            service-key:
              name: capex_html_repo_host-key
        - name: destination
          parameters:
            content-target: true
        - name: wf_zentiva
          parameters:
            service-key:
              name: capex_wf-key
        parameters:
          content:
            subaccount:
              destinations:
              - Name: showCapex_capexUI_html_repo_host
                ServiceInstanceName: capex_html_repo_host
                ServiceKeyName: capex_html_repo_host-key
                sap.cloud.service: showCapex
              - Authentication: OAuth2UserTokenExchange
                Name: showCapex_uaa_capexUI
                ServiceInstanceName: capex-ui-xsuaa-service
                ServiceKeyName: uaa_capexUI-key
                sap.cloud.service: showCapex
              - Name: showCapex_capexUI_wf
                ServiceInstanceName: wf_zentiva
                ServiceKeyName: capex_wf-key
                sap.cloud.service: wf_capex
              existing_destinations_policy: update
      ...
      resources: 
      - name: wf_capex
        type: org.cloudfoundry.existing-service
        parameters:
          service: workflow
          service-plan: standard

       

      Marius Obert do I need to add also destination into parameters: content: subaccount for wf? How to add it in case of existing service?

      Author's profile photo Ari Lulu
      Ari Lulu

      Yes. a destination to bs is required:

      https://help.sap.com/viewer/8c8e1958338140699bd4811b37b82ece/Cloud/en-US/1b80373be4fc4cbb8a7529495c9c9d62.html

      Author's profile photo Uma Anbazhagan
      Uma Anbazhagan

      Thanks! This really helped.

      Author's profile photo Sergio Rozenszajn
      Sergio Rozenszajn

      Are you using wf as a backend in your UI or using the wf UI directly?. If you use it as a backend, please paste here your xs-app.json.

       

      Author's profile photo Holger Schäfer
      Holger Schäfer

      Hi Marius,

      great informational blog!

      Dou you know maybe any documentation ressource concerning exposed business service api's from SAP Managed ApPRouter?

      I am currently using comsapuitheming.runtime, but i am missing an overview off available business services (like new approuter sap-approuter-userapi and especiall addtional UX endpoints).

      Best regards
      Holger

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      A very good question! Tbh, I'm not sure. Maybe Ari Lulu or Sergio Rozenszajn can chime in here.

      Author's profile photo Ari Lulu
      Ari Lulu

      All business services are actually the services available in the market place.

      You can see all available services here:

      https://discovery-center.cloud.sap/serviceCatalog/

      sap-approuter-userapi is a special service provided as part of approuter application:

      https://www.npmjs.com/package/@sap/approuter#user-api-service

      Author's profile photo Nagandla Raghu Ram
      Nagandla Raghu Ram

      Hello Marius,

      Thanks for the Blog. I would like to know more information on Standard Apps.

      How to Configure Standard Fiori Apps on BTP Launchpad Services. How to Extend Standard Fiori Apps using Business Application Studio? Could you please through some light on this...

      Kind Regards,

      Raghu

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Sorry, I'm afraid I'm not deep in this topic and wouldn't know how to help out here 🙁

      Author's profile photo Dries Van Vaerenbergh
      Dries Van Vaerenbergh

      Hi Marius Obert ,

      Thanks a lot for this great blogpost and Tech Bytes Tutorial, I learned a lot as usual!

      If I understood correctly you can deploy multiple Fiori Modules (from the same project) into the same HTML Repo (reusing it).

      Now I had a question about deploying different projects (every project containing a Fiori Module) into the same HTML Repo.

      When I try such a setup (with the same html5-host reused) I overwrite the previous app over and over again.

      I was wondering if such a setup is possible? And if it is, how you can achieve it.

      Thanks in advance! 🙂

      Best regards,

      Dries

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Thank you, Dries!

      Yes, this behavior is by design. If you want to upload web apps from multiple projects, you gotta use distinct html5-host service instances. There's no way to reuse the service instance and avoid overriding the content (here's also a similar question but I guess this answer is probably sufficient as well 🙂 )

      Author's profile photo Dries Van Vaerenbergh
      Dries Van Vaerenbergh

      Thanks a lot for your fast reaction Marius!

      The answer is sufficient indeed, but thanks for sharing! 🙂

      Best regards,

      Dries

      Author's profile photo Holger Schäfer
      Holger Schäfer

      Hi Dries,

      we have the same need and i figured out, that you can only have multiple apps inside one repo, if you deploy them together. In all other cases, previous content is flushed.

      So each of our UI will get his own html5-apps-repo which will be visible through the central approuter (works also for custom app routers using just one html5_repo_runtime).

      This is also quite comfortable, because using it this way, you can use cf html-push to quickly (10 secs) test deploy your UI to CF dev target to test ist behind approuter.

      If you can not use the serverless approuter, another great feature is Cross MTA Dependencies
      https://blogs.sap.com/2020/01/24/cross-mta-dependencies.-custom-ui5-library-reference-example

      Best Regards
      Holger

       

      Author's profile photo Dries Van Vaerenbergh
      Dries Van Vaerenbergh

      Hi Holger,

      Thanks for confirming!

      The cf html-push is a great solution for these quick deployments indeed. The CI/CD scenario will also be a lot easier and possible by using distinct html5-host service instances.

      Thanks for sharing!

      Best regards,

      Dries

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      Hi Dries Van Vaerenbergh and Holger Schäfer ,

      what parameters do you use for html5-push? Can html5-push use the zip from

      ui5-task-zipper?
      Thanks,
        Wolfgang
      Author's profile photo Holger Schäfer
      Holger Schäfer

      Hi Wolfgang,

      as described in my former öost, i am using one repo for each html5 app.

      From inside mta.yaml the build is triggered using

      npm run build:ui --prefix .. 

      and inside package.json i am using the following scripts

      "build:ui": "run-p package:ui <package:uix>", // triggered inside mta
      
      "package:ui": "run-s clean:ui build:ui zip:ui",
      
      "clean:ui": "npx rimraf ui/ui-shop-content.zip ui/shop/dist",
      "build:ui": "ui5 build --config ui/shop/ui5.yaml --clean-dest --include-task=generateManifestBundle generateCachebusterInfo --dest ui/shop/dist",
      "zip:ui": "cd ui/shop/dist && npx bestzip ../../ui-shop-content.zip *",
      
      // just deploy html5 app
      "deploy:html5": "npm run build:ui && npm run cf:html5:push",
      "cf:html5:push": "cd ui/shop/dist && cf html5-push -n webshop_html5_repo_host .",

      Above i left examples using && and run-p|run-s that does the same but with a nicer syntax using package npm-run-all. You can also use "run-s build:ui cf:html5:push".

      Doing it this way, i share the build task between mta build and html5 push.

      The html5 push does not need the zip, but it needs to be inside the resource folder to deploy correctly. There is also an cli param telling to point to the path, but there is a bug while uploading the folder structure will handled incorrectly in some lib cases. So you have to change directory to you build dist folder (see also trailing '.' for current dir in html5-push command!).

      Doing it this way works for me in all cases correctly (I do not know, if they fixed my error git ticket in the meantime).

      Best Regards
      Holger

      Author's profile photo Holger Schäfer
      Holger Schäfer

      Hi Marius,

      great blog and thanks for the update 16th Feb 2021 update (did not catch this, but will give it a try).

      I am starting more and more following following contents, because some blogs are currently heavily used as documentation for missing help docu.

      This is good on the one hand, but also bad, because you need to know what you are searching for 😉
      If you are new with a topic, it is also hard to figure out the benefits of such a post for you.

      Best Regards
      Holger

      Author's profile photo Rubens Cesar Pereira
      Rubens Cesar Pereira

      Hi experts,

      I implemented the tutorial bellow to return RoleCollections from user when accessing "/getuserinfo".

      https://blogs.sap.com/2019/05/23/how-to-get-the-email-of-the-logged-in-user-in-cloud-foundry

      This works well when I perform the tests through the approuter, however when exposing my application as saas_approuter and when trying to access "/ getuserinfo" I get the message "File not found", could someone help me?

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Rubens,

      the instructions you linked can only be used or standalone approuter but not for the managed approuter. In general, this approach is not needed anymore as there exists an easier option now:
      https://blogs.sap.com/2021/02/20/sap-tech-bytes-approuter-user-api-service/

       

      I hope this helps you

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      Hi Marius Obert and experts,

      is there already some information how to bring this serverless deployment and SaaS offering together?

      Can we offer a set of html5 apps deployed in this way to our customers?

      Thanks,

      Wolfgang

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      That's a good question. I'm afraid I currently don't have an answer for you as I just started to have a closer look in this area.

      Author's profile photo Wolfgang Röckelein
      Wolfgang Röckelein

      Hi Marius Obert ,

      thanks for looking into this!

      On CF with dediated app-router this was handled by the app-router (with a dynamic route and properties like TENANT_HOST_PATTERN and SAP_JWT_TRUST_ACL) with some xs-security,json config and a reference to a saas-registry service.

      On NEO a partner could subscribe his apps to a customers subaccount.

      Regards,

      Wolfgang

      Author's profile photo Davide Scarano
      Davide Scarano

      Hello Marius,

       

      I have switched from absolute to relative URL and it works in my deployed application in the HTML5 app repository, however it doesn't work when the same app is ran through the Launchpad service.

       

      Kindly see the screenshots attached.

       

      This is my OData call to Northwind

       

      This is my xs-app.json

       

       

      This is how my deployed application calls the odata, and it works.

       

       

      Same call fails through Launchpad.

       

      Thanks!

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I don't see anything wrong in the code. But it seems that the launchpad sends the URL to the wrong endpoint (I'm not sure why 🙁 )

      Author's profile photo Renaat Haleydt
      Renaat Haleydt

      Hello Marius,

      Thanks for the amazing blog post! It helped me understand how to use the managed application router.

      When I'm implementing this on my trial landscape, everything works like a charm for the calls with the default model defined in the manifest.
      However, when I make any ajax calls to the same url used in the manifest, the Launchpad service is directing the url to the 'cp.portal' and is causing a 404 error.

      To describe the problem a bit better, I listed the steps I did to reproduce it.

      I used the 'managed-html5-runtime-fiori-mta' project on Github to describe the problem I'm facing.

      In the project itself, everything works great, the Launchpad service works well for the routes to the Products entity from the Northwind service.
      The problem is that the Launchpad service sends the calls, made with ajax, to the wrong endpoint (cp.portal).

      The only changes I made on this project are:

      1. I added an extension controller (to demonstrate the ajax calls in the manifest.json)
        (line 90 until 99)
        Added%20ListReport%20controller%20extension

        ListReport controller extension

      2. I added the 'ListReportExt.controller.js' file to the HTML5Module like you can see in the picture:
        HTML5Module%20directory
      3. The 'ListReportExt.controller.js' looks like this:
        sap.ui.controller("com.sap.fioriapp.ext.controller.ListReportExt", {
        
        	onInitSmartFilterBarExtension: function (oEvent) {
        		var sUrl = "v2/Northwind/Northwind.svc/Products";
                $.ajax({
                    type: "GET",
                    dataType: "json",
                    url: sUrl,
                    success: function (oData) {
                        // This works
                    },
                    error: function (oError) {
                        // Returns error
                    }
                });
        	}
        });​
      4. I build and deployed the project again to Cloud Foundry
      5. When I open the application from the 'HTML5 Application' tab in my subaccount, the ajax call is directing me to the correct route and works as it should:
        Ajax%20call%20from%20HTML5%20Applications
      6. When I open the same application from the Launchpad service, the ajax call is directing me through the 'cp.portal' and is causing a 404 error.
        Launchpad%20service

       

      Do you have any idea how to prevent this please? Or what's the reason why it's doing this with ajax calls?

      Thanks in advance!
      Renaat

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi Renaat,

      I can confirm this issue and I've posted the question: Calling Service using AJAX in Fiori Elements Extension doesn't work in Launchpad to avoid cluttering the comments here.

      Best regards
      Gregor

      Author's profile photo Gregor Wolf
      Gregor Wolf

      Hi Renaat,

      I've found a solution:

      var sUrl = this.getView().getModel().aBindings[0].oModel.sServiceUrl + "/Products";

      Best regards
      Gregor

      Author's profile photo Renaat Haleydt
      Renaat Haleydt

      Hey Gregor,

      Thanks for checking the problem and finding a solution.
      Your workaround works like a charm, thanks for that!

      I was following this blog post of Marius because we are migrating our internal planning tool from Neo to Cloud Foundry.
      We have developed a lot of custom SAPUI5 applications that are not linked to a OData model.
      Our back end is a REST API that only returns JSON, so there is no model defined in the manifest.json.
      We are getting the data via ajax calls in our controllers, create a JSON model with that dataz and bind it to our table for example.

      A brief overview of our setup looks like the following:

      • xs-app.json

         

        {
          "welcomeFile": "/index.html",
          "authenticationMethod": "route",
          "routes": [
            {
              "source": "^/v2/(.*)$",
              "destination": "Covid19",
              "authenticationType": "xsuaa",
              "csrfProtection": false
            },
            {
              "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"
            }
          ]
        }
      • App.controller.js
        onInit: function() {
        var sUrl = "v2/us/daily.json";
                    $.ajax({
        				type: "GET",
        				dataType: "json",
        				url: sUrl,
        				success: function(data) {
        					var oModel = new sap.ui.model.json.JSONModel();
        					oModel.setData(data.d.results);
        					oThis._setTableModel(oModel); // In this method, we bind the model to the table lobsTable
        				},
        				error: function(oError) {
        					sap.m.MessageBox.error(
        						"An error occurred while loading the Daily COVID19 cases", {
        							styleClass: "sapUiSizeCompact",
        							details: oError
        						}
        					);
        				}
        			});
        		},​
      • Covid19 destination on SAP BTP
        Covid19%20destination%20on%20SAP%20BTP

      With this setup, it's not possible to get the model with the bindings, because there are no bindings yet before making the Ajax call.

      Do you have any idea how to get our projects working in Cloud Foundry?

      Thanks again for your help!

      Kind regards,
      Renaat

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi,

      sorry for not replying so far. I'm still waiting for a statement from the development team but for now, please go with Gregor's suggestion.

      Author's profile photo Holger Schäfer
      Holger Schäfer

      Hi,

      just a small question from my side.

      Is there a special reason, why you do not use the odata model read directly? example

      this.getView().getModel().read("/Products", {
          success: function () {},
          error: function (oError) {}
      });

      In this case, generally the odata model should normalize and resolve the path automatically (if not, this would be an issue).

      Using ajax call, you can use the fix from gregor, or try the following methods from v2 model implementation:

      sNormalizedPath = this._normalizePath(sPath, oContext, bCanonical);
      sDeepPath = this.resolveDeep(sPath, oContext);

       

      If there is no special reason for the ajax call, the odata.model.read also handles batch calls correctly!

       

      Best Regards
      Holger

      Author's profile photo Renaat Haleydt
      Renaat Haleydt

      Hi Holger,

      The reason we don't use methods from the OData model, is because we don't have an OData service in our back end.
      Our back end only returns JSON format unfortunately.
      That's why I also tried with the Covid19 API that returns JSON and not the Northwind for instance.

      Because we don't have an OData model, we cannot use the solution of Gregor unfortunately.

      Currently, all our applications are using ajax calls to communicate with the back end.
      It would be a great help if we just could use that logic in our migrating applications, so we don't have to rewrite everything.

      I also tried defining a default JSON model in our manifest like this:

      "dataSources": {
                  "mainService": {
                      "uri": "v2/us/daily.json",
                      "type": "JSON"
                  }
              },
      ...
      "models": {
                  "i18n": {
                      "type": "sap.ui.model.resource.ResourceModel",
                      "settings": {
                          "bundleName": "be.planningtoollobmodule.i18n.i18n"
                      }
                  },
                  "": {
                      "type": "sap.ui.model.json.JSONModel",
                      "dataSource": "mainService"
                  }
              },

      In the xs-app.json, I defined the routes to link the 'v2/us/daily.json' to the Covid19 destination like below.

      {
        "welcomeFile": "/index.html",
        "authenticationMethod": "route",
        "routes": [
          {
            "source": "^/v2/(.*)$",
            "destination": "Covid19",
            "authenticationType": "xsuaa",
            "csrfProtection": false
          },
          {
            "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"
          }
        ]
      }
      

      The Covid19 destination looks like this:
      Covid19%20destination%20in%20SAP%20BTP

      Covid19 destination in SAP BTP

       

      In my controller, I tried both the ajax call & the loadData method from the sap.ui.model.json.JSONModel.

      // Possibility 1 via loadData method
      this.getOwnerComponent().getModel().loadData("v2/us/daily.json").then(function (oData) {
          console.log("It works");
          var oModel = new sap.ui.model.json.JSONModel();
          oModel.setData(data.d.results);
          that._setTableModel(oModel); // In this method, we bind the model to the table
      }).catch(function (oError) {
          console.log("It fails");
      });
      
      // Possibility 2 via ajax call
      var sUrl = "v2/us/daily.json";
      $.ajax({
          type: "GET",
          dataType: "json",
          url: sUrl,
          success: function (data) {
              console.log("It works");
              var oModel = new sap.ui.model.json.JSONModel();
              oModel.setData(data.d.results);
              that._setTableModel(oModel); // In this method, we bind the model to the table
          },
          error: function (oError) {
              console.log("It fails");
          }
      });
      

      Both ways are returning a 404, not found because the launchpad service is directing the calls to 'cp.portal'.

      I hope this clears some things.

      Thanks for the replies!
      Kind regards,
      Renaat

      Author's profile photo David Webster
      David Webster

      Hi,

      I was having a similar issue with with the source reference for a sap.m.image control (calculated via a formatter). Working standalone but not via portal, had the same "cp.portal" reference.

      Did a little debugging and found the following solution worked for me:

      this.getOwnerComponent().getManifestObject().resolveUri()

      In your example give the below a try:

      var sUrl = this.getOwnerComponent().getManifestObject().resolveUri( "v2/us/daily.json" );

       

      Hopefully that helps.

       

      Cheers,

      Dave

      Author's profile photo Renaat Haleydt
      Renaat Haleydt

      Hey David,

      Thanks for the reply!

      This does the trick and solves my problem.
      Much appreciated!

      Kind regards,
      Renaat

      Author's profile photo Andreas Mazzola
      Andreas Mazzola

      Hi David,

      you are my hero. 🙂

      I was having the same problem and your solution helps

      var sUrl = this.getOwnerComponent().getManifestObject().resolveUri( "v2/us/daily.json" );

      Thank you very much

      Andreas

       

      Author's profile photo Vibhor Bhatnagar
      Vibhor Bhatnagar

      Hi Marius Obert 

      Thanks for this great post !!

      I see here you are using instance level destinations. However, the documentation for lauchpad service still has following Restriction message:

      Restriction%20Message

       

      Can you confirm if instance level destinations are now fully supported with HTML5Applications managed by SAP BTP, or if there are any limiting cases?

      Regards

      Vibhor

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Good catch! This is not valid any longer and needs to be updated. I already informed the right colleagues that will take care of this.

       

      Thanks!

      Author's profile photo Quentin Siewe Tchuente
      Quentin Siewe Tchuente

      Hi Marius Obert ,

      thanks for the wonderful post. I followed all the steps and i could deploy the app properly. But I have some issue while trying to access the data through a destination. I got a 401 Unauthorized response code though the authentication is well configured on the BTP.

      The Authentication on the BTP is Basic Authentication with the credentials (username and password) of a technical user. In my xs-app.json file the authenticationType is xsuaa.

      Do you have any ideas what the problem could be?

      Regards

      Quentin

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Could be a number of reasons. Most likely it is related to an incorrectly configured destination or destination usage.

      I'd start to investigate there.

      Author's profile photo Quentin Siewe Tchuente
      Quentin Siewe Tchuente

      Thanks for your answer. I have already checked everything but I am not able to find a solution. The destination is well configured and I have already tested the url with the user and it works.

      I have a feeling that the problem could be the additional properties. I have HTML5.dynamicdestination set to true. Are there any other additional properties that shoul be set?

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Have you tested the URL as shown in this video and used the same properties?

      Author's profile photo Roman Velichko
      Roman Velichko

      Hi, to All.

      Help me figure out why, after dploying the application, a call simply from the HTML Application Repository shows the SAP BTP Logon Page, and if you run the application directly by URL  with query parameters CODE (like this ...?code=AAA) I hav error HTTP / 400 bad request occurs

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Could be an incorrect declaration in the xs-app.json file. Can you share yours here?

      Author's profile photo Roman Velichko
      Roman Velichko

      Marius, Hi.

       

      I just deploy multi-cloud-html5-apps-samples/managed-html5-runtime-basic-mta from git hub.

       

      npm run build

      cf7 deploy

       

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      I just tried it and it works for me. Maybe you tried a (failed) deployment attempt before that produced some corrupted service instances. Can you try to redeploy the project in a new space (or a cleaned up space)

       

      Or maybe you tried to authenticate directly against the authentication URL of your subaccount instead of the URL of the web app: https://<subaccount>.authentication.eu10.hana.ondemand.com/login

      Author's profile photo Roman Velichko
      Roman Velichko

      New attempt to reproduce issue:

      1. Completely new trial account
      2. Clone from git
      3. build & deploy (see logs bellow)
      4. after deploy go to trial, HTMLApplication and copy to clipboard link to helloworld application
      5. Start browser in incognito mode
      6. Paste link from (4) to address bar ---> Everything OK and SAP BTP show me login page

      Now copy link from HTML5Applications and add to it this part ?code=AAAAA

      1. Start browser in incognito mode
      2. Paste new link with query parameter to address bar ---> No OK (screenshot in attachment)

       

      Ok URL:

      https://0cff07d3trial.launchpad.cfapps.eu10.hana.ondemand.com/6bb8df49-ac29-44fc-bb40-6503ed75aa8e.basicservice.helloworld-1.0.0/index.html

       

      Not Ok URL:

      https://0cff07d3trial.launchpad.cfapps.eu10.hana.ondemand.com/6bb8df49-ac29-44fc-bb40-6503ed75aa8e.basicservice.helloworld-1.0.0/index.html?code=AAA

       

      PS.

      If I already successful loged in SAP BTP this link (https://0cff07d3trial.launchpad.cfapps.eu10.hana.ondemand.com/6bb8df49-ac29-44fc-bb40-6503ed75aa8e.basicservice.helloworld-1.0.0/index.html?code=AAA) work <b>fine</b>.

      1.error-screen

       

      BuildLog

      # roman @ metha-laptop in C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta on git:master ≡ [14:49:22]
      $ npm run build

      > managed-html5-runtime-basic-mta@1.0.0 build C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta
      > mbt build

      [2021-09-20 14:49:28] INFO Cloud MTA Build Tool version 1.2.4
      [2021-09-20 14:49:28] INFO generating the "Makefile_20210920144928.mta" file...
      [2021-09-20 14:49:28] INFO done
      [2021-09-20 14:49:28] INFO executing the "make -f Makefile_20210920144928.mta p=cf mtar= strict=true mode=" command...
      [2021-09-20 14:49:28] INFO validating the MTA project
      [2021-09-20 14:49:28] INFO running the "before-all" build...
      [2021-09-20 14:49:28] INFO executing the "npm install" command...
      .npm WARN managed-html5-runtime-basic-mta@1.0.0 No repository field.
      npm WARN managed-html5-runtime-basic-mta@1.0.0 No license field.

      audited 68 packages in 1.004s

      2 packages are looking for funding
      run `npm fund` for details

      found 0 vulnerabilities

      .[2021-09-20 14:49:30] INFO validating the MTA project
      [2021-09-20 14:49:30] INFO building the "hello-world-destination-content" module...
      [2021-09-20 14:49:30] INFO the "hello-world-destination-content" module was not built because the "no-source" build parameter is set to "true"
      [2021-09-20 14:49:30] INFO finished building the "hello-world-destination-content" module
      [2021-09-20 14:49:30] INFO building the "HTML5Module" module...
      [2021-09-20 14:49:30] INFO executing the "npm run build" command...

      > html5module@0.0.1 build C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\HTML5Module
      > npm run clean && npm run zip

      .
      > html5module@0.0.1 clean C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\HTML5Module
      > npx rimraf HTML5Module-content.zip dist

      .
      > html5module@0.0.1 zip C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\HTML5Module
      > npx bestzip HTML5Module-content.zip *

      Writing * to HTML5Module-content.zip...
      zipped!
      [2021-09-20 14:49:33] INFO finished building the "HTML5Module" module
      [2021-09-20 14:49:33] INFO building the "hello-world_ui_deployer" module...
      [2021-09-20 14:49:33] INFO copying files matching the [HTML5Module-content.zip,...] patterns from the "C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\HTML5Module" folder to the "C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\resources" folder
      [2021-09-20 14:49:33] INFO copying the "HTML5Module-content.zip" pattern from the "C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\HTML5Module" folder to the "C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\resources" folder
      [2021-09-20 14:49:33] INFO the build results of the "hello-world_ui_deployer" module will be packaged and saved in the "C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\.managed-html5-runtime-basic-mta_mta_build_tmp\hello-world_ui_deployer" folder
      [2021-09-20 14:49:33] INFO finished building the "hello-world_ui_deployer" module
      [2021-09-20 14:49:33] INFO running the "after-all" build...
      [2021-09-20 14:49:33] INFO generating the metadata...
      [2021-09-20 14:49:33] INFO generating the "C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\.managed-html5-runtime-basic-mta_mta_build_tmp\META-INF\mtad.yaml" file...
      [2021-09-20 14:49:33] INFO generating the MTA archive...
      [2021-09-20 14:49:33] INFO the MTA archive generated at: C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\mta_archives\hello-world_1.0.0.mtar
      [2021-09-20 14:49:33] INFO cleaning temporary files...

      # roman @ metha-laptop in C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta on git:master ≡ [14:49:34]
      $ cf deploy .\mta_archives\hello-world_1.0.0.mtar
      Deploying multi-target app archive .\mta_archives\hello-world_1.0.0.mtar in org 0cff07d3trial / space dev as roman.velichko@saprun.com...

      Uploading 1 files...
      C:\var\vscode\multi-cloud-html5-apps-samples\managed-html5-runtime-basic-mta\mta_archives\hello-world_1.0.0.mtar
      OK
      Operation ID: e6e2b383-1a08-11ec-a8ca-eeee0a9b0ff5
      Deploying in org "0cff07d3trial" and space "dev"
      Detected MTA schema version: "3"
      No deployed MTA detected - this is initial deployment
      Detected new MTA version: "1.0.0"
      Processing service "hello-world-destination-service"...
      Processing service "hello-world_uaa"...
      Setting service "hello-world_uaa" parameters from "xs-security.json"
      Creating service "hello-world-destination-service" from MTA resource "hello-world-destination-service"...
      Processing service "hello-world_html_repo_host"...
      Creating service "hello-world_uaa" from MTA resource "hello-world_uaa"...
      Creating service "hello-world_html_repo_host" from MTA resource "hello-world_html_repo_host"...
      1 of 1 done
      1 of 1 done
      1 of 1 done
      Creating service key "hello-world_uaa-key" for service "hello-world_uaa"...
      Creating service key "hello-world_html_repo_host-key" for service "hello-world_html_repo_host"...
      Creating service key "hello-world-destination-content-hello-world-destination-service-credentials" for service "hello-world-destination-service"...
      Deploying content module "hello-world-destination-content" in target service "hello-world-destination-service"...
      Creating service key "hello-world_ui_deployer-hello-world_html_repo_host-credentials" for service "hello-world_html_repo_host"...
      Uploading content module "hello-world_ui_deployer" in target service "hello-world_html_repo_host"...
      Deploying content module "hello-world_ui_deployer" in target service "hello-world_html_repo_host"...
      Skipping deletion of services, because the command line option "--delete-services" is not specified.
      Process finished.
      Use "cf dmol -i e6e2b383-1a08-11ec-a8ca-eeee0a9b0ff5" to download the logs of the process.

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      That seems really strange. Maybe there is an incorrect configuration in the UAA of your trial.

      It definitely works in my environment but maybe someone else here has experience with this issue.

      Author's profile photo Roman Velichko
      Roman Velichko

      Hi to all.

       

      Please give me advice about deploy another html5 application to same html5_repo_host service instance ? I create another html5 app, create new manifest.json and mta.yaml files and sucsessfuly deploy it to trial account, but all other early deployed apps is deleted from html5_repo 🙁 and i cant see them in cf html5-list

      Getting list of HTML5 applications in org 79bd99d2trial / space dev as roman.velichko@saprun.com...
      OK

      name version app-host-id service instance visibility last changed
      comsaprunvschedule 1.0.0 ae593090-a265-4bbd-9761-f7f90d36e17e vschedule_html5-apps-repo-service public Thu, 21 Oct 2021 14:38:30 GMT

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      Hi Roman,

      good question! The "trick" is to use another service instance. It's a "delta uploads" to host instances are not supported so you either need to upload all web apps at the same time (with the same job) or use multiple services instances.

      Author's profile photo Roman Velichko
      Roman Velichko

      Thanx a lot... This is how I imagined it, but in large projects, the entire deployment can be difficult... In any case, thanks, we will think ...

      Author's profile photo Marius Obert
      Marius Obert
      Blog Post Author

      You're welcome. I understand that this might not be optimal for every project.
      I know Gregor Wolf is also pushing for this and there are some web resources where he requested this feature. It might make sense to subscribe to them to stay informed about possible updates. @Gregor would you mind sharing the links here as I don't have them handy 🙂

      Author's profile photo Luiz Gomes
      Luiz Gomes

      Does anyone have any example of a migration from a UI5 app using approuter with xsuaa to serverless and still connecting the destination in this new way?

      Author's profile photo Satyajit Narkhede
      Satyajit Narkhede

      Hi Marius Obert ,

       

      Could please show case how to use OAuth2UserTokenExchange Destination in SAPUI5 . Normal Basis authentication works but not with OAuth2UserTokenExchange. 

      Author's profile photo DEO DEO
      DEO DEO

      Hello Renaat Haleydt  and Gregor Wolf

      I have tried this for your suggestion with rest API call using Ajax but no luck.

       

      dest

      xs-app.json

          {
            "source": "^/einv/(.*)$",
            "destination": "Cleartext",
            "authenticationType": "xsuaa",
            "csrfProtection": false
          },
          "Cleartext": {
                      "uri": "einv/v2/eInvoice/",
                      "type": "JSON"
                  }
              }
      Ajax call-------------------------------------------
       CleartextAPI2: function () {
                      // var sUrl = "/einv/v2/eInvoice/get"+"?irn=a55d00de0a953a56ca8e32f402ae67259cbead87ad9d9d8aaf39bfedab8d8098"
                      var sUrl = this.getOwnerComponent().getManifestObject().resolveUri( "einv/v2/eInvoice/get?irn=a55d00de0a953a56ca8e32f402ae67259cbead87ad9d9d8aaf39bfedab8d8098");
                      //var sUrl = "/Cleartext"
                      $.ajax({
                          url: sUrl,
                          method: "GET",
                          headers: {
                              "X-Cleartax-Auth-Token": "1.0e601f35-9b93-4d02-a133-374776f8fc47_e1c8e001e44577b3f279d738bf7c807c185f5ebd8c3beb6b122cd4d85e285985",
                              "gstin": "06AAFCD5862R017",
                              "owner_id": "a959273c-ae3d-4424-9feb-13d8e483ab68",
                              "Content-Type": "application/json"
                              // }
                          },
                          // befo//reSend: function (request) {
                          //     // request.setRequestHeader("X-Cleartax-Auth-Token", "JSESSIONID=");
                          //     // request.setRequestHeader("Cookie", 'JSESSIONID=<jsessionid>');
                          // },
                          // data: JSON.stringify(aClearTextData),
                          // cache: true,
                          success: function (oResponse) {
                              MessageBox.success("success");
                          },
                          error: function (oError) {
                          }.bind(this)
                      });
                  },\
      -----------------------------------------

       

      Really surprised REST API call with nonOdata service SAP supports or not ?Any Input?

      Br

      DD