Make SDK - multipart/form-data - Multiple Files

I have this request in HTTP/1.1 syntax where it is a multipart/form-data type.

POST /documents/facade/complete HTTP/1.1
Content-Type: multipart/form-data; boundary=X-BOUNDARY
Authorization: Bearer ...
Host: localhost
Content-Length: 1072

--X-BOUNDARY
Content-Disposition: form-data; name="title"

Contract
--X-BOUNDARY
Content-Disposition: form-data; name="files"; filename="SAMPLE1.PDF"
Content-Type: application/pdf


--X-BOUNDARY
Content-Disposition: form-data; name="files"; filename="SAMPLE2.PDF"
Content-Type: application/pdf


--X-BOUNDARY
Content-Disposition: form-data; name="signatories[0][name]"

Signatorie 1
--X-BOUNDARY
Content-Disposition: form-data; name="signatories[0][email]"

signatorie1@example.com
--X-BOUNDARY
Content-Disposition: form-data; name="signatories[0][signatureType]"

DEFAULT
--X-BOUNDARY
Content-Disposition: form-data; name="signatories[1][name]"

Signatorie 2
--X-BOUNDARY
Content-Disposition: form-data; name="signatories[1][email]"

signatorie2@example.com
--X-BOUNDARY
Content-Disposition: form-data; name="signatories[1][signatureType]"

DEFAULT
--X-BOUNDARY--

The files can repeat, allowing sending more than one file.

But for now, I couldn’t understand how is the syntax for writing a module in a Custom App that allows files to repeat.

I had reached out to the support, they suggested including different names like files[0], files[1] etc. But the API doesn’t accept that, it has to be files.

Any ideas to solve this problem?

Support Tickets: #656808 #674596

Hello Andy,

most servers support application/json request format even though the documentation does not mention it.

Did you try the common way of implementing the module with the JSON format?

2 Likes

I didn’t entirely understand the suggestion.

For now, using multipart/form-data with an oversimplification I have this api.imljson

{
        "url": "/documents/facade",
        "method": "POST",
        "type": "multipart/form-data",
        "body": {
            "files": {
                    "value": "{{ get( get(parameters.body.files, 1), 'content') }}",
                    "options": {
                        "filename": "{{ get( get(parameters.body.files, 1), 'name') }}"
                    }
                },
                "{{...}}": "{{omit(parameters.body,'files')}}"
        },
        // Response handling
        "response": {
            "output": "{{body}}"
            }
        }
    }

expect.imljson

[{
        "name": "body",
        "type": "collection",
        "spec": [
            {
                "name": "title",
                "label": "Title",
                "type": "text","required": true
            },
            {
                "name": "files","label": "Files","required": true,
                "type": "array","validate": {"min": 1,"max": 1},
                "spec": [
                    {
                        "name": "name",
                        "type": "filename",
                        "required": true,
                        "semantic": "file:name"
                    },
                    {
                        "name": "content",
                        "type": "buffer",
                        "semantic": "file:data"
                    }
                ]
            },
			{
				"name": "signatories",
				"type": "array",
				"spec": [
					{
						"name": "signatureType",
						"type": "select",
						"validate": false,
						"default": "DEFAULT",
						"options": [
							{"label": "Default","value": "DEFAULT"},
							{"label": "Safe ID","value": "SAFE_ID"},
							{"label": "Token","value": "TOKEN"}
						]
					},
					{"name": "name","label": "Name","type": "text"},
					{"name": "email","label": "Email","type": "email"}
				]
			}
        ]
    }]

Are you implying that I could just change the "type" from "multipart/form-data" to "json".

I made a try, and the file content lost its context and doesn’t work.

The response for this request is:

{
    "statusCode": 400,
    "message": "At least one file is required.",
    "error": "Bad Request",
    "code": "DOC001"
}

I also changed the api.imljson to:

{
        "url": "/documents/facade",
        "method": "POST",
        "type": "json",
        "body": "{{parameters.body}}",
        // Response handling
        "response": {
            "output": "{{body}}"
            }
        }
    }

And the result was basically the same:

{
    "statusCode": 400,
    "message": "At least one file is required.",
    "error": "Bad Request",
    "code": "DOC001"
}

image

Sometimes it is worth trying the easier solution, rather than hopping on the advanced solution right away. Some servers actually support application/json format even though it is not documented in the API docs.

In general, the request with the type multipart/form-data that sends the file data must have the structure:

"file": {
    "value": "{{parameters.filedata}}",
    "options": {
      "filename": '"{{parameters.filename}}",
      "contentType": "{{mime(parameters.filename)}}"
    }
  }
}

However, array and object are not supported in multipart/form-data on Make, because form-data must have key-value structure and the value must not be JSON (including object and array).

For example, if the raw parameter key-value has:

{
    "array": "{{parameters.array}}", //as array
    "object": "{{parameters.object}}" //as object
}

Then, it has to be parsed into:

{
    "array[0]": "{{parameters.array[1]}}",
    "array[1]": "{{parameters.array[2]}}",
    "object[option1]": "{{parameters.object.option1}}",
    "object[option2]": "{{parameters.object.option2}}"
}

Therefore, the only solution is to write a custom IML function that will bypass the deficit (it is not possible to work with array in Postman either):

function parseFiles(files) {

    if(!files) return;

    return files.map(file => {
        return {
            value: file.data,
            options: {
                filename: file.filename
            }
        }
    })

}

Using this custom IML function in communication:

... 
"body": {
    "{{...}}": "{{omit(parameters, 'files')}}",
    "files": "{{parseFiles(parameters.files)}}"
}
...