Building binary (non-JSON) webservices with Flow
/This is a short(er) blog post, as I'm packing to get ready to sleep and then wake up early and fly to Brisbane for this weekend's Office 365 Saturday.
We will build upon what we discussed with building Flow as a Service today, and build a non-JSON webservice - we will build it to handle multi-part/form-data which is perfect for handling sending binary files to Flow, from the likes of PowerApps.
This is the third post of the series on Microsoft Flow.
- JSON cheatsheet for Microsoft Flow
- Nested-Flow / Reusable-Function cheatsheet for Microsoft Flow
- Building non-JSON webservices with Flow
- One Connection to Proxy Them All - Microsoft Flow with Azure Functions Proxies
- Building Binary output service with Microsoft Flow
In this post, we will cover:
- Define a Flow service to handle multi-part/form-data
- Creating the OpenAPI (Swagger) definition
- Call this from PowerApps
History
This is a problem that was set out by Paul Culmsee - as I understood the requirements - he wanted to use PowerApp's Add Media control, which does not provide the image as a dataUri and thus our previous solution of sending dataUri (as string) doesn't work.
The Add Media control sends a blob. A binary file.
Paul actually did lots of research and dug up lots of reference links:
- https://powerusers.microsoft.com/t5/PowerApps-Forum/native-camera-amp-flow/m-p/43635
- https://community.powerapps.com/t5/PowerApps-Forum/Retrieving-Data-URI-from-an-image-added-via-the-quot-Add-picture/m-p/40247
The second solution is Solved. But it requires a C# service.
Define a Flow service to handle multi-part/form-data
First - I recall seeing a @triggerMultipartBody() Logic App Workflow Definition. We build upon the previous post of creating a Flow Service.
First difference - we don't use the body JSON schema. We assume the body will be multi-part/form-data, so it won't follow a JSON schema. This is not a JSON service.
Save this Flow, we need the Request URL.
Set up Postman to poke this service
Use the Request URL, set the method to POST, the Body type is form-data. And for the key value - set it to "File", and select a real picture.
Hit send.
You should see this. This is quite good. It means:
- The form-data works.
- We have the image/png, it looks like it's in base64 string.
We don't really care about all the bits of part 0 of the multi-part form-data. We just want the $content.
Select that and wrap it in base64StringToBinary()
Send the picture again from Postman.
And the picture is in SharePoint!
Creating the OpenAPI (Swagger) definition
This part is hard and annoying. But I started with the OpenAPI example from https://community.powerapps.com/t5/PowerApps-Forum/Retrieving-Data-URI-from-an-image-added-via-the-quot-Add-picture/m-p/40247
At the end, I ended up with this:
{ "swagger": "2.0", "info": { "description": "", "version": "1.0.0", "title": "UploadBinary3" }, "host": "prod-18.australiasoutheast.logic.azure.com", "basePath": "/workflows", "schemes": [ "https" ], "paths": { "/76232c77d84d424b8e56ab2f88b672c4/triggers/manual/paths/invoke": { "post": { "summary": "uploads an image", "description": "", "operationId": "uploadFile", "consumes": [ "multipart/form-data" ], "parameters": [ { "name": "api-version", "in": "query", "default": "2016-06-01", "required": false, "type": "string" }, { "name": "sp", "in": "query", "default": "/triggers/manual/run", "required": false, "type": "string" }, { "name": "sv", "in": "query", "default": "1.0", "required": false, "type": "string" }, { "name": "sig", "in": "query", "default": "Mb25lHIBI6Sq4xXkNXpUV_FEyhl1BKgKftQ0-rcOPcE", "required": false, "type": "string" }, { "name": "file", "in": "formData", "description": "file to upload", "required": true, "type": "file" } ], "responses": { "200": { "description": "successful operation" } } } } } }
Arguments such as "api-version", "sp", "sv", "sig" are part of the Request URL. I specify the default value, and mark them as not required.
The one required parameter is "file", the type is "file" and is in "formData".
Regression Bug / Note on Custom Connections
Changes to the Custom Connections in PowerApps and Flow has prompted me to mention a workaround - if we specify "not required arguments with default" for arguments "api-version", "sp", "sv" and "sig" - they don't seem to be populated properly when calling from PowerApps. This seems to be a regression bug.
Instead, go into your Custom Connections screen in PowerApps/Flow, and set the optional values to "hidden" (keep the defaults). This will clean up the PowerApps connections and keep the webservice working.
Call this from PowerApps
Use the Add media control. Configure the button to call our OpenAPI-configured connection. Which shows the correct arguments: file is required, and the others are optional. You can ignore them.
Final test
Final Thoughts
So this is an end to end example of sending form-data from PowerApps to Flow to SharePoint. There are some exercises I think would make this a better application.
Flow should take a second parameter, which could be triggerMultipartBody(1) - it should be a string, and should be used as the filename. Swagger should be adjusted as well. Alternatively, you can do this in the URL as part of the query string.
We probably can make the multi-part form-data handle more than one file at a time. And make the first argument required, but subsequent arguments optional.
In the DataUri example we had issues with the Windows application since it didn't use DataUri - it used Blobs. With this example, we are able to make Windows app work as well.
If I sit back and really consider what this example means - we've built a webservice that can handle binary files, without writing a single line of 'developer' code. It makes me wonder what sort of product is Flow, and just how powerful the entire platform is.
Everyone needs to understand Flow, it has serious potential.