EasyEWS: JavaScript Library Improved

I have been working on improving my various Outlook proof-of-concepts and nearly every one of them require using EWS to access data about items in Exchange, which are not exposed through the object model. As such I have been constantly improving easyEWS.js. I have just posted version 1.0.6 and also have provided a published CDN which you can now use to always get the latest version of the library. Here is the CDN:

https://cdn.jsdelivr.net/gh/davecra/easyEws/easyEws.js

Here is the latest version on GitHub:

https://github.com/davecra/easyEWS/blob/master/easyEws.js

Specifically, the improvements I have made are:

  1. Typed every var using JSDoc. See my post on that here. This should help with debugging any issues you have. And if you have issues with my library, please contact me.
  2. Updated the Error handling callbacks and the Debugging callback to include more information. Every function can be called with this pattern, and you will get more information now if something does wrong. Here is the essential pattern every function uses:

[code lang=”javascript” collapse=”false”]

easyEws.fn([x, [y],] function(z) {
// this is the success
}, function(error) {
// this is the failure – error
}, function(debug) {
// this is the debug information which contains:
// the soap message sent to the server
// the soap response from the server
// the server status message
// the server error message if any
}

[/code]

9 thoughts on “EasyEWS: JavaScript Library Improved”

  1. […] have found the need to install code that I have been reusing over and over again. Most importantly, easyEws. So, I created a npmjs account (davecra) and I published easyEws. But what is even better and what […]

  2. Hello,

    I am developing an web add-in for Outlook.
    When using easyEWS, I am getting a Status: succeeded, but the response is empty.

    when I step thru to the .makeEwsRequestAsync call here:
    function asyncEws(soap, successCallback, errorCallback, debugCallback) {
    Office.context.mailbox.makeEwsRequestAsync(soap, function (ewsResult) {
    if (ewsResult.status == “succeeded”)
    ewsResult.status is “succeeded”, but .value is undefined.

    This is my code (basically a copy from your ReadMe):
    $.getScript(“https://appsforoffice.microsoft.com/lib/1/hosted/office.js”, function () {
    Office.onReady(function () {
    // Office is ready

    if (Office.context.mailbox && Office.context.mailbox.userProfile) {
    emailAddress = Office.context.mailbox.userProfile.emailAddress;
    }
    else {
    utils.showHtmlDialog(“Cannot connect to user mailbox.”,”Error”);
    }

    var conversationId = Office.context.mailbox.item.conversationId;
    $.getScript(“https://cdn.jsdelivr.net/gh/davecra/easyEws/easyEws.js”, function () {
    easyEws.findConversationItems(conversationId, function (itemArray) {
    if (itemArray === null || itemArray.length === 0) {
    console.log(“No_coversation_items_found”);
    return;
    }
    // we will grab the first item as the newest
    var mostRecentId = itemArray[0];
    console.log(“Most recent conversation is: ” + mostRecentId);
    }, function (error) {
    console.log(error);
    }, function (debug) {
    console.log(debug);
    });
    });
    });
    });

    And this is what I see in the console:
    TypeError: “node is null”
    getNodes https://cdn.jsdelivr.net/gh/davecra/easyEws/easyEws.js?_=1592572633028:1188
    findConversationItems https://cdn.jsdelivr.net/gh/davecra/easyEws/easyEws.js?_=1592572633028:582
    asyncEws https://cdn.jsdelivr.net/gh/davecra/easyEws/easyEws.js?_=1592572633028:1155
    onreadystatechange https://appsforoffice.microsoft.com/lib/1/hosted/outlookwebapp-15.01.js:17
    $2P_1 https://appsforoffice.microsoft.com/lib/1/hosted/outlookwebapp-15.01.js:17
    m_1 https://appsforoffice.microsoft.com/lib/1/hosted/outlookwebapp-15.01.js:17
    /_Assorted/OutlookAddin/SaveEmail/SaveEmail.js:47:29
    STATUS: succeeded
    —- START SOAP —-
    IdOnly TreeOrderDescending
    —- END SOAP —-
    —- START RESPONSE —-
    —- END RESPONSE —-

    TIA,

    1. Hi Alex!

      I just tested this function in my test harness again and it works for me. What I cannot tell from your code is if you are working with a READ item or a COMPOSE item. If you are using a COMPOSE item, you will need to save it first in order to get the conversation ID. This is what I just tested and confirmed it works:

      function testHarness(event) {
      var mailItem = Office.cast.item.toMessageCompose(Office.context.mailbox.item);
      mailItem.saveAsync(function() {
      var id = mailItem.conversationId;
      easyEws.findConversationItems(id, function(successArray) {
      var cnt = successArray.length;
      if(cnt == 0) {
      console.log(“EMPTY”);
      }
      else{
      console.log(“contains items: ” + cnt.toString());
      }
      event.completed();
      }, function(error) {
      console.log(error);
      }, function(debug) {
      console.log(debug);
      });
      });
      }

      I would also confirm you have a valid conversation ID: var conversationId = Office.context.mailbox.item.conversationId; Output this to console to verify. Next, I see the debug output which looks like the XML is displayed as text. You should see full XML there. Even so, if it is converted to text output, you should see results there. One or more entry ID’s if there are any other items in the conversation. So this leads me to believe the conversationID going in is null/empty or the item does not have any conversation items.

      One other test you can try is this. Set a breakpoint in asyncEws and grab the SOAP xml right before the code enters into the makeEwsRequestAsync() command. Then use this tool to connect to the same mailbox and run that soap:

      https://github.com/dseph/EwsEditor

      One installed, go to the Tools menu and click EWS Post. Supply the log in information in the top and then paste the same XML soap from easyEWS into the “Request Text” box. From there click Run and check out the results.

      Do you get the same thing – a null list? If so, it is something wrong with the item or the mailbox. If not, then I will need a little more context on what you are doing. What type of item are you selecting in the mailbox, is it read or compose and is this the main users mailbox or are you trying to do this against a shared mailbox?

      1. David,
        Thank you for your quick response.

        I am not sure where you see the referenced version, but I am using the same url you just provided.
        I am working with Read objects and tried getMailItem first.

        I installed EwsEditor, copied Soap contents which produce an empty response from debug window and got a correct response in the EwsEditor.
        I am also able to use their built in functions to get the email by both itemId and conversationId.

      2. Hi Alex!
        Sorry, I see you are using the correct link in your code. The article this comment is attached to was using a much older link and that was where I assumed you were pointing to.

        I have tested the function in my READ harness and it also appears to work just fine. I have tried with both local install of easyEws (node_modules) and from the CDN both with $.getScript() and using a standard script tab. And I get the correct result on each email thread I test. This tells me there is likely something else going on.

        Since you can get it to work in easyEws then the issue is not permissions or policies on the backend. You are not getting any errors , but I can only go over what else is required that I know of. Your add-in permission needs to be set to ReadWriteMailbox for the makeEwsRequestAsync() call to work.

        Looking over your debug output it is blank. And your SOAP is not formatted as XML:

        —- START SOAP —-
        IdOnly TreeOrderDescending <—— SHOULD BE FORMATTED AS XML
        —- END SOAP —-
        —- START RESPONSE —-
        —- END RESPONSE —- <——– THIS MEAN NULL

        First, I am hoping that the copy/paste into the form just stripped out your XML and that is why I only see IdOnly TreeOrderDescending. Do you see a full XML stream here? If not, then there is something else going wrong on your browser.
        The second item is that the return SOAP is null. This means that makeEwsRequest async did not return anything at all. Seeing that you are not getting an error of any kind because we are in the ewsResult.status == "succeeded" conditional, the only other thing I can think is that the returned ewsResult.value is formatted in a way that the $.parseXML() is failing on it. That might be browser specific.

        If you set a breakpoint right after the status=="succeeded" conditional and grab the ewsResult.value, what do you see? That is what SHOULD be in the debug output that is missing in the above block.
        And which browser, OS, and App are you using?
        Exchange Online (Office 365), or Exchange on-prem? If on-prem which version 2016 or 2019?

  3. Permissions – yes, it’s set to ReadWriteMailbox, the default value of ReadWriteItem was raising permission errors

    xml – yes, it seems the XML got stripped out. I pasted the xml from the debug output without any modifications into EWSEditor and got desired result (for both ItemId and ConversationId)

    ewsResult.Value = “”

    I am using Firefox 77.0.1 on Windows 10. Hosted Exchange 2013.

    1. Hi Alex!

      I have FireFox 77.0.1 on Windows 10 (64bit). I only have an Exchange 2016 server to test against, so that may be the differentiator here. I then installed and tested my code with various configurations and all of them returned the correct value.

      The only difference at this point is that you are on Exchange 2013. Do you have a newer version of Exchange you can test against to verify?

      Behind the scenes, my code uses this function in the Office API’s:
      https://docs.microsoft.com/en-us/office/dev/add-ins/outlook/web-services

      Another option, just to verify is to issue the makeEwsRequestAsync() call from your add-in without my code. Do you get the same result? If so, the issue is likely support for the functionality in Exchange 2013. I know the support there is very, very limited. Exchange 2013 only supports API 1.1 (6+ years old now). Here is a detail of the support levels:
      https://docs.microsoft.com/en-us/office/dev/add-ins/reference/requirement-sets/outlook-api-requirement-sets#requirement-sets-supported-by-exchange-servers-and-outlook-clients
      https://docs.microsoft.com/en-us/javascript/api/outlook/office.mailbox?view=outlook-js-1.1#makeewsrequestasync-data–callback–usercontext-

      1. I ran the code against M365 and it returned expected results.
        Of course, the fact that I am dealing with Exch2013 and Req. 1.1 is the reason I have to use EWS in the first place…
        From what I understand, 1.1 exploses a Body property for mailbox.item but no way to actually read or set the text?

        Thank you for all your help.

Leave a Reply