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.

  1. JSON cheatsheet for Microsoft Flow
  2. Nested-Flow / Reusable-Function cheatsheet for Microsoft Flow
  3. Building non-JSON webservices with Flow
  4. One Connection to Proxy Them All - Microsoft Flow with Azure Functions Proxies
  5. Building Binary output service with Microsoft Flow

In this post, we will cover:

  1. Define a Flow service to handle multi-part/form-data
  2. Creating the OpenAPI (Swagger) definition
  3. 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: 

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.

trigger-1.png

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:

  1. The form-data works.
  2. 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.

test-0.png

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.