nmarus / node-ews

A simple JSON wrapper for the Exchange Web Services (EWS) SOAP API.
MIT License
116 stars 52 forks source link

CreateItem MessageDisposition="SendAndSaveCopy" canot set HTML <Body /> #27

Closed aaroncalderon closed 8 years ago

aaroncalderon commented 8 years ago

I followed your example:

var xml2js = require('xml2js');
var when = require('when');
var _ = require('lodash');

var util = require('util');

function convert(xml) {

  var attrkey = 'attributes';

  var parser = new xml2js.Parser({
    attrkey: attrkey,
    trim: true,
    ignoreAttrs: false,
    explicitRoot: false,
    explicitCharkey: false,
    explicitArray: false,
    explicitChildren: false,
    tagNameProcessors: [
      function(tag) {
        return tag.replace('t:', '');
      }
    ]
  });

  return when.promise((resolve, reject) => {
    parser.parseString(xml, (err, result) => {
      if(err) reject(err);
      else {
        var ewsFunction = _.keys(result['soap:Body'])[0];
        var parsed = result['soap:Body'][ewsFunction];
        parsed[attrkey] = _.omit(parsed[attrkey], ['xmlns', 'xmlns:t']);
        if(_.isEmpty(parsed[attrkey])) parsed = _.omit(parsed, [attrkey]);
        resolve(parsed);
      }
    });
  });

}

var xml = '<?xml version="1.0" encoding="utf-8"?>' +
  '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ' +
                 'xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">' +
    '<soap:Body>' +
      '<CreateItem MessageDisposition="SendAndSaveCopy" xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">' +
      '<SavedItemFolderId>' +
        '<t:DistinguishedFolderId Id="sentitems" />' +
      '</SavedItemFolderId>' +
      '<Items>' +
        '<t:Message>' +
          '<t:ItemClass>IPM.Note</t:ItemClass>' +
          '<t:Subject>EWS node email</t:Subject>' +
          '<t:Body BodyType="HTML">Priority - Update specification</t:Body>' +
          '<t:ToRecipients>' +
            '<t:Mailbox>' +
              '<t:EmailAddress>me@example.com</t:EmailAddress>' +
            '</t:Mailbox>' +
          '</t:ToRecipients>' +
          '<t:IsRead>false</t:IsRead>' +
        '</t:Message>' +
      '</Items>' +
    '</CreateItem>' +
    '</soap:Body>' +
  '</soap:Envelope>';

convert(xml).then(json => {
  console.log('ewsArgs = ' + util.inspect(json, false, null));
});

// console output ready for ewsArgs

// ewsArgs = { attributes: { MessageDisposition: 'SendAndSaveCopy' },
//  SavedItemFolderId: { DistinguishedFolderId: { attributes: { Id: 'sentitems' } } },
//  Items:
//   { Message:
//      { ItemClass: 'IPM.Note',
//        Subject: 'EWS node email',
//         Body:
//         { _: 'Priority - Update specification',
//           attributes: { BodyType: 'Text' } },
//        ToRecipients: { Mailbox: { EmailAddress: 'me@example.com' } },
//        IsRead: 'false' } } }

However, I get the following error

Error: a:ErrorSchemaValidation: The request failed schema validation: The element 'http://schemas.microsoft.com/exchange/services/2006/types:Body' cannot contain child element 'http://schemas.microsoft.com/exchange/services/2006/types:HTML' because the parent element's content model is text only.
    at WSDL.xmlToObject (C:\dev\p\ms-exchange\ews\node_modules\node-ews\node_modules\soap\lib\wsdl.js:1467:19)
    at C:\dev\p\ms-exchange\ews\node_modules\node-ews\node_modules\soap\lib\client.js:297:25
    at C:\dev\p\ms-exchange\ews\node_modules\node-ews\lib\http.js:47:9
    at finalCallback (C:\dev\p\ms-exchange\ews\node_modules\httpreq\lib\httpreq.js:133:4)
    at IncomingMessage.<anonymous> (C:\dev\p\ms-exchange\ews\node_modules\httpreq\lib\httpreq.js:393:5)
    at emitNone (events.js:91:20)
    at IncomingMessage.emit (events.js:185:7)
    at endReadableNT (_stream_readable.js:974:12)
    at _combinedTickCallback (internal/process/next_tick.js:74:11)
    at process._tickCallback (internal/process/next_tick.js:98:9)

But, if I comment out the _ attribute, the email is sent. So, I do not know how to add the HTML body, or just text body at all.

Any suggestions?

Further testing

I'm planning on doing a <CreateItem MessageDisposition="SaveOnly"... and then an <UpdateItem... and report back.

aaroncalderon commented 8 years ago

So, I figured it out. Turns out that when I perform a <GetItem /> on an email I created the body is returned as the '$value' not as the _ (underscoer); see example:

{
    "ResponseMessages": {
        "GetItemResponseMessage": {
            "attributes": {
                "ResponseClass": "Success"
            },
            "ResponseCode": "NoError",
            "Items": {
                "Message": {
                    "MimeContent": {
                        "attributes": {
                            "CharacterSet": "UTF-8"
                        },
                        "$value": "VG86ICJDQUx..."
                    },
                    "ItemId": {
                        "attributes": {
                            "Id": "AAAVAEFh...",
                            "ChangeKey": "CQAAA..."
                        }
                    },
                    "Subject": "EWS node email [draft]",
                    "Sensitivity": "Normal",
                    "Body": {
                        "attributes": {
                            "BodyType": "HTML"
                        },
                        "$value": "<p><s>This</s> <u>is</u> <i>the</i> <b>Body.</b></p><h1>This is important.</h1>"
                    },
                    "Size": "26267",
                    "DateTimeSent": "2016-10-19T13:53:32Z",
                    "DateTimeCreated": "2016-10-19T13:53:32Z",
                    "ResponseObjects": {
                        "ForwardItem": null
                    },
                    "HasAttachments": "false",
                    "ToRecipients": {
                        "Mailbox": {
                            "Name": "SOME, ONE",
                            "EmailAddress": "SOMEONE@example.com",
                            "RoutingType": "SMTP"
                        }
                    },
                    "IsReadReceiptRequested": "false",
                    "IsDeliveryReceiptRequested": "false",
                    "IsRead": "true"
                }
            }
        }
    }
}
aaroncalderon commented 8 years ago

So, I didged around and found that xml2js has an option to set the charkey which defaults to _.

So, if we change the file example I put earlyer like this:

var xml2js = require('xml2js');
var when = require('when');
var _ = require('lodash');

var util = require('util');

function convert(xml) {

  var attrkey = 'attributes';
  var charkey = '$value'; // added this to get the correct key > value that exchange expects

  var parser = new xml2js.Parser({
    attrkey: attrkey,
    charkey: charkey,
    trim: true,
    ignoreAttrs: false,
    explicitRoot: false,
    explicitCharkey: false,
    explicitArray: false,
    explicitChildren: false,
    tagNameProcessors: [
      function(tag) {
        return tag.replace('t:', '').replace('m:',''); // and this to cleanup some extra tags, 
                                                       // not specifically used in this example but it is needed
      }
    ]
  });

  return when.promise((resolve, reject) => {
    parser.parseString(xml, (err, result) => {
      if(err) reject(err);
      else {
        var ewsFunction = _.keys(result['soap:Body'])[0];
        var parsed = result['soap:Body'][ewsFunction];
        parsed[attrkey] = _.omit(parsed[attrkey], ['xmlns', 'xmlns:t']);
        if(_.isEmpty(parsed[attrkey])) parsed = _.omit(parsed, [attrkey]);
        resolve(parsed);
      }
    });
  });

}

var xml = '<?xml version="1.0" encoding="utf-8"?>' +
  '<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" ' +
                 'xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">' +
    '<soap:Body>' +
      '<CreateItem MessageDisposition="SendAndSaveCopy" xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">' +
      '<SavedItemFolderId>' +
        '<t:DistinguishedFolderId Id="sentitems" />' +
      '</SavedItemFolderId>' +
      '<Items>' +
        '<t:Message>' +
          '<t:ItemClass>IPM.Note</t:ItemClass>' +
          '<t:Subject>EWS node email</t:Subject>' +
          '<t:Body BodyType="HTML"><![CDATA[This is <b>bold</b> - <b>Priority</b> - Update specification]]></t:Body>' +
          '<t:ToRecipients>' +
            '<t:Mailbox>' +
              '<t:EmailAddress>me@example.com</t:EmailAddress>' +
            '</t:Mailbox>' +
          '</t:ToRecipients>' +
          '<t:IsRead>false</t:IsRead>' +
        '</t:Message>' +
      '</Items>' +
    '</CreateItem>' +
    '</soap:Body>' +
  '</soap:Envelope>';

convert(xml).then(json => {
  console.log('ewsArgs = ' + util.inspect(json, false, null));
});

// console output ready for ewsArgs

// ewsArgs = { attributes: { MessageDisposition: 'SendAndSaveCopy' },
//  SavedItemFolderId: { DistinguishedFolderId: { attributes: { Id: 'sentitems' } } },
//  Items:
//   { Message:
//      { ItemClass: 'IPM.Note',
//        Subject: 'EWS node email',
//         Body:
//         { '$value': 'This is <b>bold</b> - <b>Priority</b> - Update specification',
//           attributes: { BodyType: 'Text' } },
//        ToRecipients: { Mailbox: { EmailAddress: 'me@example.com' } },
//        IsRead: 'false' } } }
nmarus commented 7 years ago

Thanks. Added your updated script in the converter folder.