Three things that are not super obvious when working with the new Office 365 Unified API and mail

dev.office.com banner
Office 365 offers a rich API for interacting with Mail. Unfortunately it doesn't always work as you would expect. Here are three things that you have to know before you get started.

Office 365 APIs for modern development

In the past, when you wanted to build an extension on top of Outlook and Exchange you would need to build a VSTO add-in and communicate with Exchange using its SOAP services. Although this model worked, it wasn't quite convenient not to mention the security piece of it.

To simplify building Outlook add-ins that interact with Office 365 Mail Microsoft offers the new Office 365 Unified API and Office.js. Following the industry standards such as REST, OData and web development using JavaScript the new APIs allow a bigger group of developers to extend the existing mail functionality. But first however you have to figure out how they work, because the available documentation doesn't quite get you there.

The sample scenario

Imagine that you were building an Outlook Add-in that would do something with e-mail attachments. Before implementing your add-in logic you would need to retrieve the attachments' contents from Office 365 and pass them to your services.

Getting attachment contents from Office 365

The new Office 365 Unified API allows you to get the contents of an e-mail attachment by calling the following REST API:

https://graph.microsoft.com/beta/me/messages/[message id]/attachments/[attachment id]  

For each attachment you will get information such as its title, type and base64-encoded contents. As you have noticed calling this API requires passing two parameters: the ID of the e-mail message that contains the attachments and the ID of the attachment for which you would like to retrieve additional information.

When building Office Add-ins you can retrieve the e-mail message ID using the Office.context.mailbox.item.itemId property and the IDs of attachments by iterating through the Office.context.mailbox.item.attachments array and getting the value of the id property for each one of them.

var message = Office.context.mailbox.item;  
var messageId = message.itemId;

for (var i = 0; i < message.attachments.length; i++) {  
    var attachment = message.attachments[i];
    var attachmentId = attachment.id;
}

Unfortunately if you would pass the retrieved IDs to the Office 365 Unified API REST call, there is a chance you will get a 404 Not Found response.

One: Outlook message ID is different than the Office 365 Unified API message ID

It turns out that even though Outlook and Office 365 Unified API both work on the same e-mail messages they use different IDs to refer to them. In Outlook a message ID might look like:

AAMkADA0YTE0NTk4LWY1ZmEtNDg4ZS1hODJmLTM5ZWQzOTMxNDUyNABGAAAAAADmnA6UJwaqSYsSDrcydWwQBwCDC2P/qXJETb8qQSeGSdtqAAAAAAEMAACDC2P/qXJETb8qQSeGSdtqAAARNSNzAAA=  

The same ID in order to be processed by the Office 365 Unified API would need to be slightly altered by replacing all / (slash) characters with a - (dash):

AAMkADA0YTE0NTk4LWY1ZmEtNDg4ZS1hODJmLTM5ZWQzOTMxNDUyNABGAAAAAADmnA6UJwaqSYsSDrcydWwQBwCDC2P-qXJETb8qQSeGSdtqAAAAAAEMAACDC2P-qXJETb8qQSeGSdtqAAARNSNzAAA=  

Having edited the message ID, if you would request the attachment contents from the Office 365 Unified API there is a chance you might get yet another 404 Not Found error.

Two: Outlook attachment ID is different than the Office 365 Unified API attachment ID

If you look closely at an attachment ID you might notice that it starts with the ID of the message.

AAMkADA0YTE0NTk4LWY1ZmEtNDg4ZS1hODJmLTM5ZWQzOTMxNDUyNABGAAAAAADmnA6UJwaqSYsSDrcydWwQBwCDC2P/qXJETb8qQSeGSdtqAAAAAAEMAACDC2P/qXJETb8qQSeGSdtqAAARNSNzAAABEgAQAC+zbDZqI81Cn43aLeWLEAQ=  

Not surprisingly also here you would need to replace all / (slash) characters with a - (dash). But even if you did so, you would still might get a 404 Not Found response. It turns out that in case of attachment IDs you also need to replace all + (plus) characters with an _ (underscore). Having done that you would finally be able to get attachment information amongst which its base64-encoded contents.

Three: Attachment contents returned by the Office 365 Unified API are base64-encoded... twice!

According to the documentation the Office 365 Unified API returns base64-encoded contents of attachments. If you were interested in processing these contents, you would first need to convert them to the original binary data. Theoretically this can easily be done by calling the window.atob(base64string) function to base64-decode the contents string and then convert it to a byte array.

function getBinaryFileContents(base64FileContents) {  
  var raw = window.atob(base64FileContents);
  var rawLength = raw.length;
  var array = new Uint8Array(new ArrayBuffer(rawLength));

  for(var i = 0; i < rawLength; i++) {
    array[i] = raw.charCodeAt(i);
  }

  return array;
}

Unfortunately if you would try to open the file after such processing you would find out that it's broken and its contents are unreadable.

It turns out that attachments contents returned by the Office 365 Unified API are base64-encoded not once but twice! So in order to get the binary contents you have to wrap the window.atob call with another window.atob call. This will allow you to get the binary contents of your attachment just as expected:

function getBinaryFileContents(base64FileContents) {  
  // atob has to be called twice because the FileAttachment.ContentBytes property
  // return by the attachments endpoint is base64-encoded twice
  var raw = window.atob(window.atob(base64FileContents));
  var rawLength = raw.length;
  var array = new Uint8Array(new ArrayBuffer(rawLength));

  for(var i = 0; i < rawLength; i++) {
    array[i] = raw.charCodeAt(i);
  }

  return array;
}

Summary

The new Office 365 Unified API and Office.js API offer rich capabilities for extending Office applications with additional functionality. At the moment the new Office 365 Unified API is still in beta which might explain it's not working as you would expect. Still you would expect the documentation to reflect the different inconsistencies. As it doesn't, knowing what they are can help you get started exploring these APIs in your applications.

Comments

comments powered by Disqus