Homelab, Linux, JS & ABAP (~˘▾˘)~
 

[Fiori] BAS – Request is not a local Request

Recently I was continuing an enhancement project of a standard Fiori Application. The already existing enhancement project was created using WebIDE a few years ago. Now I needed to add some more functionality to the enhancement project using the Business Application Studio. To my surprise, the automatic migration of the WebIDE project to a BAS project was working without any issues. Nice! I could make my changes and everything was fine until I tried to deploy the application.

The abap_deploy_task aborted with the error “Request is not a local Request” (in German “Auftrag ist kein lokaler Auftrag“).

Somehow it did not deploy in my Workbench Transport Request and was expecting a Local Transport Request.

At first, I thought, this was a problem in the ui5-deploy.yaml configuration of my App, and therefore I was looking in the wrong direction. But after checking the package of the already deployed enhancement project in the backend, I finally found the reason. The Transport Layer of the Package containing the BSP application was not correct. It turned out, that the previous developer was not able to deploy from his WebIDE directly to the system, instead he deployed to another system, exported and then imported the transport request manually. Therefore, the “Original System” of all objects was incorrect and also the Transport Layer on the Package. After updating both (like described here), I was able to deploy the enhancement project successfully in my Workbench Transport Request.

[Fiori] Launchpad: Failed to load catalogs

Recently, I was confronted with the “Failed to load catalogs” error message when starting the Fiori Launchpad (/ui2/flp) and navigating to the App Finder.

The system was quite new and the Fiori Customizing was not completely done yet. When opening the Dev Tools, I saw a failed request to

/sap/opu/odata/ui2/page_builder_pers/PageSets('%2FUI2%2FFiori2LaunchpadHome')

Opening this failed request in a new tab results in an HTTP 404 from an Apache web service. So it looked like an Apache was set up as reverse proxy in front of the sap system. I tried to call the PageSets endpoint without providing the key %2FUI2%2FFiori2LaunchpadHome and got an HTTP 200. So in general, Apache was working, and the service endpoint page_builder_pers was responding. Next, I did the same service calls from the gateway client and both calls were working fine. So it looked like Apache was the problem when providing the PageSets key. After a quick search, I found this post on Stack Overflow:

https://stackoverflow.com/questions/37834925/apache-reverse-proxy-blocking-sap-fiori-launchpad-url

So the issue was some (in this case) incorrect decoding of ‘%2F’. After contacting the basis team and adding the proposed Apache configs, the request finally resolved successfully, and the Launchpad was displaying some apps.

[SAPUI5] Promisify an oData request

This is discussed for many years and unfortunately will not be implemented in the UI5 framework itself (see here). There are already different blogs describing how to build a wrapper for oData requests (for example here and here).

But with ES2024 it now got super simple to do this:

async function readData(model, entitySet) {
  const [promise, resolve, reject] = Promise.withResolver( )
  model.read(entitySet, {
    success: data => resolve(data),
    error: error => reject(error)
  })
  return promise
}

const user = await readData(oDataModel, "/user")

[SAPUI5] Model binding events

this.getView().bindElement({
				path: sObjectPath,
				events: {
					dataRequested: (oEvent) => {}, // Executed when a request to server is send
					dataReceived: (oEvent) => {},  // Executed when data from server is received
					change:(oEvent) => {},         // Executed everytime you do ElementBinding
				}
			})

The events for dataRequested and dataReceived are only fired, when data is requested or data is received from a backend. This is not the case, when the requested data is already available in the model from a previous backend call. In such situations, the change event comes in handy.

The same can also be done via XML:

binding="{
  path: '/myEntitySet',
  events: {
    dataRequested: 'onDataRequested',
    dataReceived: 'onDataReceived',
    change: 'onDataChange'
  }
}"

[SAPUI5] securityTokenAvailable

Just noticed, that with UI5 version 1.119.0 the getSecurityToken() function got replaced with securityTokenAvailable().

https://sapui5.hana.ondemand.com/#/api/sap.ui.model.odata.v2.ODataModel%23methods/getSecurityToken

https://sapui5.hana.ondemand.com/#/api/sap.ui.model.odata.v2.ODataModel%23methods/securityTokenAvailable

// Returns the current security token if available; triggers a request to fetch the security token if it is not available.
const token = this.getModel().getSecurityToken() // Deprecated

// Returns a promise, which will resolve with the security token as soon as it is available.
const token = await this.getModel().securityTokenAvailable()

[SAPUI5] Add your own Logout functionality to the Launchpad Sandbox

Use the attachLogoutEvent of the ushell container to trigger your approuter logout endpoint, that needs to be configured in your xs-app.json. The code in my launchpad.html looks like this:

    <script>
        sap.ui.getCore().attachInit(() => {

            sap.ushell.Container.createRenderer('fiori2', true).then(renderer => renderer.placeAt("content"))

            sap.ushell.Container.attachLogoutEvent(e => {
                e.preventDefault()
                window.location.replace('/do/logout')
            }, false)

        })
    </script>

This way, you can reuse the default logout dialog logic the launchpad provides.

For completeness, find also my approuter configuration and custom logout page below.

My xs-app.json:

{
  "welcomeFile": "index.html",
  "authenticationMethod": "route",
  "logout": {
    "logoutEndpoint": "/do/logout",
    "logoutPage": "/logged-out.html"
  },
  "sessionTimeout": 60,
  "routes": [
    {
      "source": "^/logged-out.html$",
      "localDir": ".",
      "authenticationType": "none"
    },
    {
      "source": "^/launchpad.html$",
      "localDir": ".",
      "authenticationType": "xsuaa",
      "cacheControl": "no-cache, no-store, must-revalidate"
    },
    {
      "source": "^/appconfig/(.*)$",
      "localDir": ".",
      "authenticationType": "xsuaa"
    },
    {
      "source": "^/user-api(.*)",
      "target": "$1",
      "service": "sap-approuter-userapi",
      "authenticationType": "xsuaa"
    },
...
  ]
}

My logged-out.html file, placed in the approuter folder next to the launchpad.html:

<!DOCTYPE html>
<html>

<head>
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta http-equiv="Content-Type" content="text/html;charset=UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Logged out</title>
    <style>
        h2 {
            font-family: "Arial", sans-serif;
        }
        .centered {
            position: absolute;
            top: 50%;
            left: 50%;
            transform: translate(-50%, -50%);
        }
    </style>
</head>

<body>
    <div class="centered">
        <h2>You are now logged out</h2>
    </div>
</body>

</html>