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

Introduction


Recently, there was an announcement that SAP Build Process Automation can now use custom UI5 application for approval forms.

SAP Build Process Automation – Learning Content September 2023

There is also a new tutorial that walks you through the process of creating an UI5 application in SAP Business Application Studio, deploying it to HTML5 Application repository and using it in a process.

Create an SAP UI5 Task for a Business Process

Problem Statement


When developing a UI application, you may want to test it in Business Application Studio. However, applications created using the Workflow Task UI template cannot be directly tested within Business Application Studio. This is because the application relies on specific objects (task model and inbox API) as startup parameters.

During local testing, since these parameters are not provided, the application fails right from the beginning and doesn't even display the view. The image below is the error screen that appears when you attempt to start the app with npm start.

As a result, in order to test the task UI, you have to deploy it every time you make changes and view it within the inbox app of SAP Build Process Automation. In my opinion, this doesn't provide an optimal developer experience.

 

Error screen



Solution


I have developed a simple wrapper application that instantiates the task UI as a component and embed it within the wrapper.

image

Setting Up

 

1. Task UI


Upon generation with the template, the task UI includes routing settings in manifest.json. Consequently, when you run the application within Business Application Studio, it will open the newly empty Form view, even though the primary view is App.view.xml.

 

Structure of Task UI


To rectify this, remove the routes and targets settings from manifest.json as below. This action will not impact the application behavior within the inbox app, as routing is not used within the inbox app.

 

        "routing": {

            "config": {

                "routerClass": "sap.m.routing.Router",

                "viewType": "XML",

                "async": true,

                "viewPath": "namespace.myworkflowuimodule.view",

                "controlAggregation": "pages",

                "controlId": "app",

                "clearControlAggregation": false

            },

            "routes": [],

            "targets": {}

        },

 

 

 

 

2. The wrapper


Now, lets' dive into the step-by-step process of configuring the wrapper.

2.1. Clone the Repository



    • Clone the repository into Business Application Studio.

 

git clone https://github.com/miyasuta/taskui-wrapper.git​

 



2.2. Install Dependency



    • Execute the following command to install dependencies.

 

npm install​

 



2.2. Enable App-to-App navigation


In order for the wrapper to access the task UI application, we need fiori-tools-servestatic settings in ui5.yaml. To achieve this, you can utilize "Enable App-To-App navigation" command of Fiori tools.

    • Enable App-to-App navigation with the following command.

 

    • Select taskui-wrapper as the source.

 

    • Select your task UI project.



2.3. Configure ui5.yaml



    • The settings blow are generated when you enable App-to-App navigation. Replace the dot(.) in the path with a slash (/).

 

    • Under the customMiddleware fiori-tools-proxy, add the following block.

 

        backend:

        - path: /ordersmanagement.ordersmgtnspworkflowuimodule/bpmworkflowruntime  

          pathPrefix: /public/workflow/rest

          url: https://spa-api-gateway-bpi-us-prod.cfapps.us10.hana.ondemand.com

          destination: workflowruntime


Please ensure that the destination workflowruntime has been created in the BTP subaccount (the name of the destination does not matter). For details on how to register the destination, please refere to my previous blog post. Also, replace /ordersmgtnsp/workflowuimodule with the <namespace>/<id> of your application.

 

 

    • In the end, your ui5.yaml will look like the example below:

 

specVersion: "3.1"

metadata:

  name: taskuiwrapper

type: application

server:

  customMiddleware:

    - name: fiori-tools-proxy

      afterMiddleware: compression

      configuration:

        ignoreCertError: false

        ui5:

          path:

            - /resources

            - /test-resources

          url: https://ui5.sap.com

        backend:

          - path: /resources/ordersmgtnsp/workflowuimodule/bpmworkflowruntime

            pathPrefix: /public/workflow/rest

            url: https://spa-api-gateway-bpi-us-prod.cfapps.us10.hana.ondemand.com

            destination: workflowruntime

    - name: fiori-tools-appreload

      afterMiddleware: compression

      configuration:

        port: 35729

        path: webapp

        delay: 300

    - name: fiori-tools-preview

      afterMiddleware: fiori-tools-appreload

      configuration:

        component: taskuiwrapper

        ui5Theme: sap_horizon

    - name: fiori-tools-servestatic

      afterMiddleware: compression

      configuration:

        paths:

          - path: /resources/ordersmgtnsp/workflowuimodule

            src: ../ordersmanagement/workflow-ui-module/webapp

          - path: /appconfig

            src: appconfig

 



2.4. Configure component usage in manifest.json



    • In manifest.json, locate the following section under sap.ui5.

 

      "components": {

        "ordersmgtnsp.workflowuimodule": {

          "lazy": true

        }

      }

    },

    "componentUsages": {

      "taskUI": {

        "name": "ordersmgtnsp.workflowuimodule",

        "settings": {}

      }​

 

 

    • Replace ordersmgtnsp.workflowuimodule with the <namespace>.<id> of your application.



 

Running the Wrapper



    • Launch the wrapper app by running npm start command.

    • Input a task instance ID (UUID) into the input field and click the "Show Task UI" button.

 

    • The task UI, along with task-specific data,  will be presented.

 

    • If you wish to inspect the layout without a task instance ID, you can do so for the purpose of layout verification.



What does the Wrapper do?


The controller of the wrapper is responsible for the following tasks:

    • It implements a mock inboxAPI, which positions buttons at the footer based on the parameters received from the task UI.

 

    • It instantiates the task UI component, supplying it with the necessary startup parameters, and embeds the component within its view.



Please note that the wrapper does not perform task instance updates as the inbox app does. However, if desired, you can execute the event handler (actionEventHandler) passed by the task UI which sends a request to update the task instance.

 

sap.ui.define([

    "sap/ui/core/mvc/Controller",

    "sap/ui/model/json/JSONModel",

    "sap/m/Button",

    "sap/m/MessageToast",

    "sap/m/ToolbarSpacer"

],

    /**

     * @Param {typeof sap.ui.core.mvc.Controller} Controller

     */

    function (Controller, JSONModel, Button, MessageToast, ToolbarSpacer) {

        "use strict";



        return Controller.extend("taskuiwrapper.controller.View1", {

            onInit: function () {



            },



            onShowTaskUI: function () {

                const taskModel = new JSONModel({

                    InstanceID: this.byId("taskInstanceId").getValue()

                })



                //clear buttons

                this.byId("toolbar").removeAllContent();

                this.byId("toolbar").addContent(new ToolbarSpacer());



                const that = this;

                const inboxAPI = {

                    updateTask: function () {                       

                    },

                    addAction: function (action, actionEventHandler, listener) {

                        // make the first letter of type uppercase

                        const type = action.type.charAt(0).toUpperCase() + action.type.slice(1);

                        const button = new Button({

                            text: action.label,

                            type: type,

                            press: function () {

                                MessageToast.show(`Action ${action.label} triggered`)

                            }

                        })

                        that.byId("toolbar").addContent(button);

                    }

                };



                this.byId("page").setBusy(true);

                this.getOwnerComponent().createComponent({

                    usage: 'taskUI',

                    componentData: {

                        startupParameters: {

                            taskModel: taskModel, 

                            inboxAPI: inboxAPI

                        }

                    }

                }).then((component)=> {

                    this.byId("attachmentComponentContainer").setComponent(component);

                    this.byId("page").setBusy(false);

                });

                            

            }

        });

    });

 


 

Closing


I hope this wrapper app will make your task UI development experience better. Should you have any questions or require further assistance, please feel free to reach out. Thanks for reading!

28 Comments
Mark112358
Active Participant
0 Kudos

Hi, has anyone had any success with implementing this solution? I've followed the steps, but when I click the Show Task UI button it gets stuck. Has anyone else run into this? Any fixes? 
FYI @MioYasutake 

Mark112358_0-1711644314659.png

specVersion: "3.1"
metadata:
  name: taskuiwrapper
type: application
server:
  customMiddleware:
    - name: fiori-tools-proxy
      afterMiddleware: compression
      configuration:
        ignoreCertError: false
        ui5:
          path:
            - /resources
            - /test-resources
          url: https://ui5.sap.com
        backend:
          - path: /resources/bparoleassignmentui/workflowuimodule/bpmworkflowruntime
            pathPrefix: /public/workflow/rest
            destination: bpa_workflowruntime
    - name: fiori-tools-appreload
      afterMiddleware: compression
      configuration:
        port: 35729
        path: webapp
        delay: 300
    - name: fiori-tools-preview
      afterMiddleware: fiori-tools-appreload
      configuration:
        component: taskuiwrapper
        ui5Theme: sap_horizon
    - name: fiori-tools-servestatic
      afterMiddleware: compression
      configuration:
        paths:
          - path: /resources/bparoleassignmentui/workflowuimodule
            src: ../workflow-ui-module/webapp
          - path: /appconfig
            src: appconfig
jasoninouye
Discoverer
0 Kudos

I also experienced the same thing. After submitting the UUID the application just hangs. 

MioYasutake
Active Contributor
0 Kudos

@Mark112358 @jasoninouye 
It seems that the wrapper is failing to load your task ui. Do you see any error in the console or network tab? The places to check in the wrapper module are:

manifest.json

 

 

      "components": {
        "ordersmgtnsp.workflowuimodule": {
          "lazy": true
        }
      }
    },
    "componentUsages": {
      "taskUI": {
        "name": "ordersmgtnsp.workflowuimodule",
        "settings": {}
      }
    }, 

 

 

ui5.yaml

 

 

    - name: fiori-tools-servestatic
      afterMiddleware: compression
      configuration:
        paths:
          - path: /resources/ordersmgtnsp/workflowuimodule
            src: ../ordersmanagement/workflow-ui-module/webapp
          - path: /appconfig
            src: appconfig

 

 

Also, today when I tried to run the task UI, I had to make a small adjustment in ui5.yaml to be able to call workflow api.

 

 

# before
        backend:
          - path: /resources/ordersmgtnsp/workflowuimodule/bpmworkflowruntime

# after
        backend:
          - path: /ordersmanagement.ordersmgtnspworkflowuimodule/bpmworkflowruntime

 

 

MioYasuatke_2-1711659384207.png

Mark112358
Active Participant
0 Kudos

@MioYasutake 

Thanks for taking the time to respond. I made your changes, but I'm still running into the same issue. 

This is the console log error.

Mark112358_0-1711668519091.png

Taskui-wrapper ui5.yaml

Mark112358_1-1711668551089.png

Taskui-wrapper manifest.json

Mark112358_2-1711668579481.png

Folder structure

Mark112358_3-1711668788353.png

 

workflow-ui-module manifest.json

{
    "_version": "1.26.0",
    "sap.app": {
        "id": "bpa_role_assignment_ui",
        "type": "application",
        "i18n": "i18n/i18n.properties",
        "applicationVersion": {
            "version": "0.0.1"
        },
        "title": "{{appTitle}}",
        "description": "{{appDescription}}",
        "resources": "resources.json",
        "sourceTemplate": {
            "id": "@sap/generator-fiori:basic",
            "version": "1.8.4",
            "toolsId": "b937175d-390a-45b6-87c9-768f86130af0"
        },
        "dataSources": {
            "mainService": {
                "uri": "/sap/opu/odata/",
                "type": "OData",
                "settings": {
                    "annotations": [],
                    "localUri": "localService/metadata.xml",
                    "odataVersion": "2.0"
                }
            }
        }
    },
    "sap.ui": {
        "technology": "UI5",
        "icons": {
            "icon": "",
            "favIcon": "",
            "phone": "",
            "phone@2": "",
            "tablet": "",
            "tablet@2": ""
        },
        "deviceTypes": {
            "desktop": true,
            "tablet": true,
            "phone": true
        }
    },
    "sap.ui5": {
        "flexEnabled": true,
        "dependencies": {
            "minUI5Version": "1.82.2",
            "libs": {
                "sap.m": {},
                "sap.ui.core": {},
                "sap.f": {},
                "sap.suite.ui.generic.template": {},
                "sap.ui.comp": {},
                "sap.ui.generic.app": {},
                "sap.ui.table": {},
                "sap.ushell": {}
            }
        },
        "contentDensities": {
            "compact": true,
            "cozy": true
        },
        "models": {
            "i18n": {
                "type": "sap.ui.model.resource.ResourceModel",
                "settings": {
                    "bundleName": "bparoleassignmentui.workflowuimodule.i18n.i18n"
                }
            },
            "": {
                "dataSource": "mainService",
                "preload": true,
                "settings": {}
            }
        },
        "resources": {
            "css": [
                {
                    "uri": "css/style.css"
                }
            ]
        },
       
        "rootView": {
            "viewName": "bparoleassignmentui.workflowuimodule.view.App",
            "type": "XML",
            "async": true,
            "id": "App"
        }
    },
    "sap.cloud": {
        "public": true,
        "service": "bpa_role_assignment_ui"
    },
    "sap.bpa.task": {
        "_version": "1.0.0",
        "outcomes": [
            {
                "id": "approve",
                "label": "Approve"
            },
            {
                "id": "reject",
                "label": "Reject"
            }
        ],
        "inputs": {
            "$schema": "http://json-schema.org/draft-07/schema",
            "title": "input",
            "type": "object",
            "required": [
                "customerName",
                "orderNumber",
                "orderAmount",
                "orderDate",
                "expectedDeliveryDate",
                "shippingCountry"
            ],
            "properties": {
                "customerName": {
                    "type": "string",
                    "title": "Customer Name",
                    "description": "Customer Name"
                },
                "orderNumber": {
                    "type": "string",
                    "title": "Order Number",
                    "description": "Order Number"
                },
                "orderAmount": {
                    "type": "number",
                    "title": "Order Amount",
                    "description": "Order Amount"
                },
                "orderDate": {
                    "type": "string",
                    "title": "Order Date",
                    "description": "Order Date"
                },
                "expectedDeliveryDate": {
                    "type": "string",
                    "title": "Expected Delivery Date",
                    "description": "End Date of the Vacation"
                },
                "shippingCountry": {
                    "type": "string",
                    "title": "Shipping Country",
                    "description": "Shipping Country"
                }
            }
        },
        "outputs": {
            "$schema": "http://json-schema.org/draft-07/schema",
            "title": "output",
            "type": "object",
            "required": [
                "comment"
            ],
            "properties": {
                "comment": {
                    "type": "string",
                    "title": "Comment",
                    "description": "Comment to buyer"
                }
            }
        },
        "category": "approval"
    }

}

 

MioYasutake
Active Contributor

@Mark112358 

The name of your root folder is "bpa_ui5_role_ui", but in the configuration of fiori-tools-servestatic in the ui5.yaml file of the taskui-wrapper, it is set as srv: ../bpa_role_assignment_ui/workflow-ui-module/webapp. What if you try changing "bpa_role_assignment_ui" to "bpa_ui5_role_ui"?

If that doesn't work either, would it be possible for you to share the source code via Git? 

Mark112358
Active Participant
0 Kudos

@MioYasutake 

That didn't work for me either. I'm getting the same error. I also started from scratch and did the tutorial verbatim and then your tutorial. Same error. Weird? 

 

Here is the repo: https://github.com/MindsetConsulting/bpa.ui5.role.ui

MioYasutake
Active Contributor

@Mark112358 

Thanks for sharing the code. I have changed the warpper's ui5.yaml as below and it's working.

specVersion: "3.1"
metadata:
  name: taskuiwrapper
type: application
server:
  customMiddleware:
    - name: fiori-tools-proxy
      afterMiddleware: compression
      configuration:
        ignoreCertError: false
        ui5:
          path:
            - /resources
            - /test-resources
          url: https://ui5.sap.com
        backend:
          - path: /bpa_role_assignment_ui.bparoleassignmentuiworkflowuimodule/api/public/workflow/rest/v1
            pathPrefix: /public/workflow/rest/v1
            url: https://spa-api-gateway-bpi-us-prod.cfapps.us10.hana.ondemand.com
            destination: bpa_workflowruntime                      
    - name: fiori-tools-appreload
      afterMiddleware: compression
      configuration:
        port: 35729
        path: webapp
        delay: 300
    - name: fiori-tools-preview
      afterMiddleware: fiori-tools-appreload
      configuration:
        component: taskuiwrapper
        ui5Theme: sap_horizon
    - name: fiori-tools-servestatic
      afterMiddleware: compression
      configuration:
        paths:
          - path: /resources/bparoleassignmentui/workflowuimodule
            src: ../workflow-ui-module/webapp
          - path: /appconfig
            src: appconfig
Mark112358
Active Participant
0 Kudos

@MioYasutake 

Thanks for continuing to help me out. 

The component.js file load issue appears to be resolved. But it's still getting hung up. 

Mark112358_0-1711744055565.pngMark112358_1-1711744068707.png

 

Mark112358
Active Participant

@MioYasutake Please discard my previous message. Thank you so much for helping me out. 

MioYasutake
Active Contributor
0 Kudos

@Mark112358 

I see the same errors in the network tab, but the app is loaded.

MioYasuatke_0-1711746687259.png

The $metadata request (which is failing) is sent because it is defined in the manifest.json of your task UI app. Removing the sections below will suppress the request.

        "dataSources": {
            "mainService": {
                "uri": "/sap/opu/odata/",
                "type": "OData",
                "settings": {
                    "annotations": [],
                    "localUri": "localService/metadata.xml",
                    "odataVersion": "2.0"
                }
            }
        }
...
        "models": {
            ...
            "": {
                "dataSource": "mainService",
                "preload": true,
                "settings": {}
            }
        },

 

Mark112358
Active Participant
0 Kudos

@MioYasutake 

Are you able to get the workflow-ui-module controller to load so you can inspect it in the dev tools? I can see in the network tab that the workflow-ui-module is called, but I don't see it in the folder structure so I can do debugging. See below. I can only see the controller for the wrapper. Any thoughts about this? 

Mark112358_0-1712010170719.png

 

 

MioYasutake
Active Contributor

@Mark112358 

You can find the controller of your task ui under resources/bparoleassginmentui/workflowuimodule/controller.

MioYasuatke_2-1712015164756.png

Mark112358
Active Participant
0 Kudos

@MioYasutake 

 

Hi, 

Have you had any issues binding an Odata service to the view in any of your use cases? We are seeing instances where the Odata call to the BE service isn't getting triggered. We are following normal UI5 practices. We are able to create a quick UI5 app and this service works just fine, it's just for this Ui Task app that it appears to not follow the same rules. 

Mark112358_0-1712260188813.pngMark112358_1-1712260233189.pngMark112358_2-1712260253577.png

 

 

MioYasutake
Active Contributor
0 Kudos

@Mark112358 
I haven't tried connecting to OData service within a task UI, but in order to access backend services you need to add configurations in the wrapper's ui5.yaml as below:

https://www.npmjs.com/package/@sap/ux-ui5-tooling#connecting-to-a-back-end-system-with-destination

- name: fiori-tools-proxy
  afterMiddleware: compression
  configuration:
    backend:
    - path: /sap
      url: https://my.backend.com:1234
      destination: my_backend

Additionally, from the code you presented, OData model is already defined in the manifest.json ( I'm guessing that the mainService is used by the default model ). Therefore, no need to create another model in the onInit method.

Mark112358
Active Participant
0 Kudos

@MioYasutake 

 

Have you had a chance to try and call an Odata service? Can you try and see if you can get the Northwind service working in your example? I'm curious if you run into CORS errors. 

MioYasutake
Active Contributor
0 Kudos

@Mark112358 

Yes, I can retrieve data from Northwind OData service.

MioYasutake_0-1712693748134.png

To achieve this, add an OData service using Service Manager.

MioYasutake_1-1712693801450.png

MioYasutake_2-1712693823422.png

This will add necessary settings to the task ui project. Then, copy the setting in ui5.yaml to the ui5.yaml of the wrapper.

MioYasutake_3-1712693884750.png

The ui5.yaml file of the taskui-wrapper will look like the image below:

MioYasutake_4-1712693925665.png

Mark112358
Active Participant
0 Kudos

@MioYasutake 

This is the strangest thing. I can get my services to work fine in a "dummy" ui5 application but for some weird reason. I keep getting a CORS error (metadata call is fine) when I make a GET call to any Odata service.

 

Mark112358_0-1712721470699.png

Wrapper .yaml file

Mark112358_1-1712721522907.png

Ui .yaml file

Mark112358_2-1712721540898.png

UI manifest.json

Mark112358_3-1712721570537.png

 

 

 

MioYasutake
Active Contributor
0 Kudos

@Mark112358 

It looks that calls to the SAP backend are encountering CORS errors. How about Northwind? Also, OData calls in UI5 applications are normally sent in $batch requests. Are you sending GET requests from the controller logic?

Mark112358
Active Participant
0 Kudos

@MioYasutake 

Northwind is getting the same CORS error. 

 

Yes, I'm doing a GET call in the controller. I was struggling to get the Odata call to work using the normal configuration. I assumed the Taskwrapper solution was creating some issues, so I went with an ajax call in the controller. 

 

Mark112358
Active Participant
0 Kudos

@MioYasutake 

 

Scratch that. I got Northwind to work.. 

Hopefully, this gets me closer to getting my Odata service to work. 

Mark112358_0-1712762930421.png

 

Mark112358
Active Participant

@MioYasutake 

I'm happy to report I got my Odata service working. FYI - I requested to add you on LI. 

Thanks for everything. 

Mark112358
Active Participant
0 Kudos

For those following along. My Destination for my Odata service wasn't set up correctly. It didn't have the correct properties added and the URL was missing the Port number. 

Mark112358
Active Participant
0 Kudos

@MioYasutake 

Have you tried to deploy your form in a BPA with the Northwind service? Does the service still work for you? Our service works just fine when running it locally, but as soon as we deploy it, add it to our automation and run the process. The service call doesn't happen. 

MioYasutake
Active Contributor
0 Kudos

@Mark112358 

Yes, the task ui is working in inbox app as well:

MioYasutake_0-1712867028031.png

If the OData call is not working, check xs-app.json to see if the northwind is included the routes.

 

{
  "welcomeFile": "/index.html",
  "authenticationMethod": "route",
  "routes": [
    {
      "source": "^/api/(.*)$",
      "target": "$1",
      "service": "com.sap.spa.processautomation",
      "endpoint": "api",
      "csrfProtection": true,
      "authenticationType": "xsuaa"
    },
    {
      "source": "^/V2/(.*)$",
      "target": "$1",
      "destination": "Northwind",
      "authenticationType": "none",
      "csrfProtection": false
    },

 

 

Mark112358
Active Participant
0 Kudos

@MioYasutake 

That didn't work for us. This is what we added. 

Mark112358_0-1712942935890.png

 

MioYasutake
Active Contributor
0 Kudos

@Mark112358 

The file above is xs-security.json, but you need to add a route to xs-app.json.

Mark112358
Active Participant
0 Kudos

@MioYasutake 

Oops, sorry. That was an oversight on my part. I made the adjustment and I'm still not getting the service to work. Could you share your configuration for Northwind? 

MioYasutake
Active Contributor

@Mark112358 

The screenshot below is my destination setting for Northwind.

MioYasutake_0-1712987534416.png

The xs-app.json setting for sap OData should look like as follows:

    {
      "source": "^/sap/(.*)$",
      "target": "/sap/$1",
      "destination": "<destination name>",
      "authenticationType": "xsuaa",
      "csrfProtection": false
    },

 

Labels in this area