fastmail / JMAP-Samples

sample code for beginning JMAP developers

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Example for creating an Email with attachment?

gorenje opened this issue · comments

Hi There!

Following up from #18 - howabout an example for adding an attachment?

From RFC specs (which btw are very broad and difficult to understand), to the Jmap.io, to the example code here and finally to the example code from jmap.io, I understand that it's not simply a case of attaching a binary string to the Email/set command and bob's your uncle!

No, as I understand there must be a hidden Blob/set command:

There is a single endpoint that handles all file uploads for an account, regardless of what they are to be used for.

source

Now I would like to know what is that endpoint in the case of Fastmail and what exactly do I send to that endpoint? Or can I send a Blob/set command as part of my Email/set and EmailSubmission/set commands that I send to https://api.fastmail.com/jmap/api endpoint?

Every time I have a look at Jmap, I have the impression that the standard isn't going anywhere - broken examples is just one example. Where are the well explained example code snippets? Please point me to them!

I am continually looking through code examples to find how to do the most basic things with Jmap, something that a thousand SMTP libraries would do in a second.

Why doesn't Fastmail produce a single library that does everything in a simply and straight forward way? Instead we have these samples that cover how to download an email using five different languages ... does not really cut the ice! And that for at least five years - there has been little or no movement in the standard.

This repo does not even have an readme pointing to the specs and perhaps jmap.io and perhaps to better implementation than the ones here. It all seems to be disregarded and unmaintained (viewed from outside).

Sorry for the critical comments, it's the JMap command Frustration/get, from the specs:

The Frustration/get command is triggered after spending an hour fruitlessly trying to understand the JMAP documentation. The return value of this command MUST be shared with the broader world by creating a issue on JMAP-Samples. This SHOULD reduce the size of the return value.

Ok, one step further the upload url can found via GET https://api.fastmail.com/jmap/session and seems to be https://api.fastmail.com/jmap/upload/{accountId}/ - found that bit of info at https://www.fastmail.com/for-developers/integrating-with-fastmail/

Ok, one more step closer, from the specs:

There is a single endpoint that handles all file uploads for an
account, regardless of what they are to be used for. The Session
object (see Section 2) has an "uploadUrl" property in URI Template
(level 1) format [RFC6570], which MUST contain a variable called
"accountId". The client may use this template in combination with an
"accountId" to get the URL of the file upload resource.

Yes this is it, I've found it!

To upload a file, the client submits an authenticated POST request to
the file upload resource.

Alright, I can this! But what goes into the post? What formatting? Is it json or just binary? Let me give it a whirl on the old FJ!

POST https://api.fastmail.com/jmap/upload/uaXXXXXXX/

Response:

{"expires":"2023-06-19T09:38:58Z","blobId":"G0cbee624de16ac05fab83axxxxxxxx","size":506874,"type":"application/octet-stream","accountId":"uaXXXXXXX"}

Very important: initially I forgot the trailing '/' after accountID --> INCLUDE IT, else it does not work! i.e. https://api.fastmail.com/jmap/upload/uaXXXXXXX/ works, https://api.fastmail.com/jmap/upload/uaXXXXXXX does not work. Error message in the latter case: payload: "Path contains unexpected components" - right!

Ok, got it working. In the end it was a matter of uploading the attachment, then adding the following hash to my draft email:

if (msg.email.attachments) {
    draft["attachments"] = [];
    msg.email.attachments.forEach(function(att){
        draft["attachments"].push({
            "blobId": att.blobId,
            "type": att.type,
            "name": att.filename
        });
    });
}

where draft is the typical email object that Email/set requires:

var draft = {
    "from": [{ "email": from_email, "name": "Acme Inc." }],
    "to": [{ "email": to_email }],
    "subject": msg.email.subject,
    "keywords": { "$draft": true },
    "mailboxIds": {  },
    "bodyValues": {
        "body": { "value": msg.email.text, "charset": "utf-8" },
        "bodyHTML": { "value": msg.email.html, "charset": "utf-8" }
    },
    "textBody": [{ "partId": "body", "type": "text/plain" }],
    "htmlBody": [{ "partId": "bodyHTML", "type": "text/html" }],
};

att.blobId and att.type are the responses from the upload endpoint, att.filename is of my own choosing.

So after all that, it's working great!

But why was I frustrated in the beginning? Because after 4 or 5 years of JMAP ("JMAP is the developer-friendly, open API standard for modern mail clients and applications to manage email faster." - jmap.io), it's still a matter of going through the RFC specs (which aren't interesting reads), guessing how the data structures might look like and then hoping it will work.

The protocol is super complex* and difficult for a human to understand, hence there should be a good libraries that abstract that complexity away. I should be able to use a very simple API via a library without going through the specs each time and trying to get the mental picture of a protocol that was written for lawyers (MUST, SHOULD, COULD, WOULD, PERHAPS ... blah):

The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
"SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
"OPTIONAL" in this document are to be interpreted as described in
BCP 14 [RFC2119] [RFC8174] when, and only when, they appear in all
capitals, as shown here.

If JMAP isn't already dead, then referring to a RFC as the implementation guide is not helping. (And jmap.io is just an rehash of those specs.) We are, afterall, living in an age of REST APIs and README style documentation. It has the feeling that Fastmail isn't interested in pushing JMAP in any particular interesting form.

*=XML like structures modelled in JSON is what it looks and feels like!

Nonetheless, I do appreciate their email business, Fastmail is definitely the best email service in town 👍 would recommend it to anyone!

For anyone interested in doing JMAP in Node-RED, I have a implemented a basic flow which interacts with Fastmails Jmap.