Get creative using SAP Business Technology Platform – Kyma Environment! Part 2
If you’ve followed Part 1 of this blog series, you’ll have seen the way in which I’ve deployed a simple application to upload a document to an SAP BTP, Kyma runtime pod (leveraging a Deployment, Service and API Rule from .yaml file). From here, I could now perform further processing on the document that has been uploaded, such as image classification, optical character recognition, machine learning to mention just a view, although before getting too fancy in the backend, I’d like to share a little insight onto getting a better frontend up and running using an SAP AppGyver low-code solution. If you want to follow along, there will be some prerequisites to follow, though if you want to simply use SAP AppGyver to upload a file somewhere other than Kyma, then this process should work well for you too.
Prerequisites
- SAP AppGyver (Trial or from an existing SAP Business Technology Platform instance)
- Part 1 (optional, only needed if you want to upload file to SAP BTP, Kyma Runtime)
At the end of Part 1, I had used some HTML in my python return code for methods GET and POST giving a response with simple webpage title, a message and a form to find + submit a file with multipart/form-data encoding. What I want to do next is use SAP AppGyver to present a front-end with a better user interface to upload the file so it can be further processed on the Kyma Runtime.
SAP AppGyver Application
One of the best aspects of SAP AppGyver is the user experience when creating applications. Composer Pro is, in a single word, excellent. The more time I’ve been spending using it, the more I enjoy using. The following steps are an example for consumption on a mobile device.
- I’ll start by hitting ‘Create New’ and give my empty page a name, provide a description on the Properties Tab, adjust the background color and clear the padding on the Style Tab.
- From Core Components (on the left) I’ll drag an Image component and snap it in to the top of my page, change its source & display name in the Properties Tab. This will be my banner.
- Now I’ll rename my Title component to ‘Document Manager’ with Text Align set to center from Layout Tab.
- Next, I’ll use a Row component with 2 columns with Component spacing of 28px on top. Each Cell will hold a 60px*60px Image component and a Text component right below. The images will be our buttons to upload and view files on Kyma.
After these steps I have an app that looks like Figure 1.
Add Logic to Pick Files
Now I want to add the ability to upload a document from my device to the SAP BTP, Kyma runtime configured in Part 1. For this I’m going to add logic to the left Cell in my Row component so I don’t specifically have to click the image or the text, just the cell surrounding both. I can select ‘Cell #1 from my Tree menu in the bottom right and this should toggle the correct selection in my main view. I’ll then select the ‘Add Logic to CELL #1’ from the tool bar at the bottom of the screen as seen in Figure 2.
From here, I’ll select the ‘Marketplace’ search function in the Logic components menu on the bottom left and search for ‘Pick Files’. After installing it will appear in the ‘Installed’ tab. I’ll drag it onto the logic diagram for Cell #1 and connect it to the ‘Component Tap’ event for the cell. I.e. When I tap the cell, run the Pick Files component. I also only want the user to upload a single file at a time and only of type .PDF, so I’ll adjust the Pick Files inputs to facilitate this configuration as seen in Figure 3.
I can now hit the ‘Launch’ and test that the when I hit the left Cell, I get a native document picker. This should work for Apple, Android and Windows with no fuss. Once I’ve selected a file I want it to get uploaded to SAP BTP, Kyma runtime.
Low-Code Solution to Upload Files
While I know of two no-code approaches of doing this in SAP AppGyver, neither of them will facilitate my requirement of uploading a file as multipart/form-data to the Flask route in my Python app from Part 1.
- Converting my file to base64 using the Marketplace flow function. I don’t want to have to then decode base64 in my Python app once the file is uploaded, therefore
- Use the Upload File flow function from the Marketplace with a formula to specify that the output of Pick Files should be uploaded to a specific URL.
NOTE – These may work for you depending on your requirements!
To achieve my desired behavior, I’ll use a JavaScript flow function to implement the logic to upload the file, by dragging and dropping it from Core Tab into the flow logic and renaming to ‘JS Upload File’ as seen in Figure 4.
I can double click on the node to open the script editor and then add my inputs. I’ll delete out whatever is there already and add two properties:
- url: This is the link to your endpoint where the document should be uploaded to. If you completed Part 1, then this should be something like: https://flaskupload-subdomain-node.<your-id>.kyma.ondemand.com/, otherwise replace this with the URL that you want to transmit your file to.
- file: This is the output from the Pick Files flow function I’ve used to select the file. It has 6 properties coming from output 1 which I’ll want to use in my JavaScript as seen in Figure 5.
The following JavaScript will cover what I need to transmit the file to my SAP BTP, Kyma runtime pod with the Python + Flask app. Note – It’s very minimal and could be extended to facilitate for better error handling at the very least!
// Goal is take the output of the 'Pick Files' flow function and submit to Flask route using multipart/form-data encoding.
// Declare 2 inputs.
// - First is the endpoint URL where we want to upload our file. We've hard coded this value.
// - Second is the file with it's 6 object properties from the output of 'Pick Files'
let { url, file } = inputs
// Get the path of the file selected in 'Pick Files'. Note - Only allowed single file upload so hard-coded to first i.e. file[0]
let path = await fetch(file[0].path)
// Transform path into blob
let blob = await path.blob()
// Declare the form we'll submit with the payload
const formData = new FormData()
// Append upload details into the formData
formData.append('file', blob, file[0].name)
try {
const response = await fetch(url, {
method: 'POST',
body: formData,
})
return [0, { result: "File Sent" } ]
} catch {
return [0, { result: "File Error" } ]
Finally, I’ll add an Alert Dialog from the Core flow functions connected to the output of JS Upload File and give it a title. It should also contain the Dialog Message which is set as ‘Output of another node’ and will be the ‘result’ message passed from the JavaScript. Note – I’ve deliberately used the term ‘Sent’ as opposed to ‘Uploaded’ since I have not added code to capture a response from my POST method. I.e. I won’t know if its actually been accepted at the other end. I’m just trying to keep things simple for now.
Integration Test on the end-to-end Upload Process
Since I’ve already configured my upload destination on the SAP BTP, Kyma Runtime, I can check if the file has been been accepted by the backend Python program.
As seen in Figure 6:
- Connect to the pod running my app on Kyma from local CLI (See Optional – Verify folder contents on Kyma pod from local terminal from Part 1 to configure this!) and confirm there are no files uploaded
- Launch my app from SAP AppGyver
- Upload 4 PDF documents
- Get acknowledgement that they’ve been sent
- Check from CLI again to see they have been uploaded
It’s nice to see that the files have been uploaded successfully, though I still have some gaps to fill in the future.
- My testing worked fine when running as web-app in browser from both Windows 10 21H2 in Chrome and on iPhone XR in Safari. I tested the SAP AppGyver Preview app on both iOS and Android, but had mixed results.
- iOS transmitted the formData but the payload was not received via Flask route.
- On Android, the formData did not transmit.
- I expect the root cause will be improving my JavaScript when creating the multipart/form-data. Please leave a comment if you’ve got improvements to suggest!
- Another quick improvement I’ll likely make is bringing error codes from the backend to the frontend. This should be easily achieved using Python return code in the Flask Route.
Great, this is functioning well enough. Next I’ll want to do something with these documents I’ve uploaded!!
If you have found this has been helpful, or have some feedback to share on this topic, please leave a comment and follow my profile for future posts.
For more information please see:
SAP Business Technology Platform Topic Page
Ask questions about SAP Business Technology Platform
Read other SAP Business Technology Platform articles and follow blog posts
There are other links embedded in the Blog which may be of use.