nmarus / node-ews

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

Validation Error when adding Attribute to arguments later #66

Open relief-melone opened 7 years ago

relief-melone commented 7 years ago

I have a weird problem at the moment when creating an appointment. My last issues were solved pretty quick and it was more the problem of the guy behind the screen but this one leaves me completely cluess. First im setting my args like this

var args = {
        "attributes" : {
            "SendMeetingInvitations" : "SendToAllAndSaveCopy"
        },
        "SavedItemFolderId": {
            "DistinguishedFolderId": {
                "attributes": {
                    "Id": "calendar"
                }
            }
        },
        "Items" : {
            "CalendarItem" : {
                "Subject" : "",
                "Body" : {
                    "attributes" : {
                    },
                    "$value" : ""
                },
                "ReminderIsSet" : "true",
                "ReminderMinutesBeforeStart" : "30",
                "Start" : "",
                "End" : "",
                "IsAllDayEvent" : "false",
                "LegacyFreeBusyStatus" : "Busy",
                "Location" : ""
            }
        }
    };

if after that I change for example the Subject by

args.Items.CalendarItem.Subject = req.body.Subject;

everything works fine. However if Subject is not yet set in args the Validation of node-ews passes an error saying that CalendarItem has an invalid child "Subject". req.body.Subject is a normal String and yet the validation only works if I overwrite it and its already present in CalendarItem.

kcastrotech commented 7 years ago

What happens if you try args.Items.CalendarItem.Subject = "";

I know it's technically the same as was was declared in args, but I'm curious if that it somehow makes a difference.

Alternatively what if you did null in the args instead of "" "Subject" : null,

kcastrotech commented 7 years ago

Also, can you provide the exact error message?

relief-melone commented 7 years ago

Unfortunately I won't have access to that machine and Exchange Server till monday. But I'll make sure to post the error message now and the one to "" and null as well then

relief-melone commented 7 years ago

Also posted the problem on StackOveflow and got the following response. Maybe you can use that as well https://stackoverflow.com/questions/45512348/node-js-ews-createattachment-failing-due-to-xml-parsing-error?noredirect=1#comment80197173_45512348

Yes, fixed now. I used to pass a buffer as content (Buffer.from(b64string, 'base64') so it used to fail. I am now passing the b64string itself as content without converting it to Buffer. It could be something else in your case (if you're having an issue) but there's high chance it's the content format. – Hochman7G

relief-melone commented 7 years ago

Ok, So "" and null have the same effect. If the Subject is not previously set to something in the args and later defined this is the error message you'll receive

a:ErrorSchemaValidation: The request failed schema validation: The element 'Cale ndarItem' in namespace 'http://schemas.microsoft.com/exchange/services/2006/type s' has invalid child element 'Subject' in namespace 'http://schemas.microsoft.co m/exchange/services/2006/types'. List of possible elements expected: 'When, IsMe eting, IsCancelled, IsRecurring, MeetingRequestWasSent, IsResponseRequested, Cal endarItemType, MyResponseType, Organizer, RequiredAttendees, OptionalAttendees, Resources, ConflictingMeetingCount, AdjacentMeetingCount, ConflictingMeetings, A djacentMeetings, Duration, TimeZone, AppointmentReplyTime, AppointmentSequenceNu mber, AppointmentState, Recurrence, FirstOccurrence, LastOccurrence, ModifiedOcc urrences, DeletedOccurrences, MeetingTimeZone, ConferenceType, AllowNewTimePropo sal, IsOnlineMeeting, MeetingWorkspaceUrl, NetShowUrl' in namespace 'http://sche mas.microsoft.com/exchange/services/2006/types'. a:ErrorSchemaValidation: The request failed schema validation: The element 'Cale ndarItem' in namespace 'http://schemas.microsoft.com/exchange/services/2006/type s' has invalid child element 'Subject' in namespace 'http://schemas.microsoft.co m/exchange/services/2006/types'. List of possible elements expected: 'When, IsMe eting, IsCancelled, IsRecurring, MeetingRequestWasSent, IsResponseRequested, Cal endarItemType, MyResponseType, Organizer, RequiredAttendees, OptionalAttendees, Resources, ConflictingMeetingCount, AdjacentMeetingCount, ConflictingMeetings, A djacentMeetings, Duration, TimeZone, AppointmentReplyTime, AppointmentSequenceNu mber, AppointmentState, Recurrence, FirstOccurrence, LastOccurrence, ModifiedOcc urrences, DeletedOccurrences, MeetingTimeZone, ConferenceType, AllowNewTimePropo sal, IsOnlineMeeting, MeetingWorkspaceUrl, NetShowUrl' in namespace 'http://sche mas.microsoft.com/exchange/services/2006/types'.

kcastrotech commented 7 years ago

It looks like Subject cannot be blank: https://msdn.microsoft.com/en-us/library/office/aa565100(v=exchg.150).aspx#Anchor_3

However your statement just now about

If the Subject is not previously set to something in the args and later defined this is the error message you'll receive

is a bit confusing. You stated in your original message:

if after that I change for example the Subject by args.Items.CalendarItem.Subject = req.body.Subject; everything works fine.

So does this error happen if you leave it blank ("" or null)? If so then this should be answered by definition of Subject in the MS link above.

Or does this error only happen if you set it then later change it?

relief-melone commented 7 years ago

It just happens if i haven't previously set it. So I completely leave out Subject and define the "Items" like var args = { "attributes" : { "SendMeetingInvitations" : "SendToAllAndSaveCopy" }, "SavedItemFolderId": { "DistinguishedFolderId": { "attributes": { "Id": "calendar" } } }, "Items" : { "CalendarItem" : { "Subject" : "", "Body" : { "attributes" : { }, "$value" : "" }, "ReminderIsSet" : "true", "ReminderMinutesBeforeStart" : "30", "Start" : "", "End" : "", "IsAllDayEvent" : "false", "LegacyFreeBusyStatus" : "Busy", "Location" : "" } } };

then

`args.Items.CalendarItem.Subject = req.body.Subject'

then ews.run

it will not work. Even though the result of console.log(args) will look exaclty the same.

kcastrotech commented 7 years ago

That is interesting.

I'd suggest taking a peek at the generated XML. The way I do that is to edit \MY_NODE_PROJECT\node_modules\node-ews\node_modules\soap\lib\client.js and add console.log('XML:',xml); after the xml = "<?xml ...." line which for me was line 263.

Try comparing the working XML to the broken XML and see if anything jumps out at you.

relief-melone commented 7 years ago

OK. The only difference I see is the order of the tags. I'll post 2 xmls. The first one is the non-working one. The second one will work.

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:tns="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:wsi="http://ws-i.org/schemas/conformanceClaim/">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <CreateItem SendMeetingInvitations="SendToAllAndSaveCopy" xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
      <SavedItemFolderId>
        <t:DistinguishedFolderId Id="calendar" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
        </t:DistinguishedFolderId>
      </SavedItemFolderId>
      <Items>
        <t:CalendarItem xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
          <t:Body BodyType="HTML">TEST</t:Body>
          <t:ReminderIsSet>true</t:ReminderIsSet>
          <t:ReminderMinutesBeforeStart>15</t:ReminderMinutesBeforeStart>
          <t:Start>2017-10-11T06:10:36.890Z</t:Start>
          <t:End>2017-10-11T07:10:48.909Z</t:End>
          <t:IsAllDayEvent>false</t:IsAllDayEvent>
          <t:LegacyFreeBusyStatus>Busy</t:LegacyFreeBusyStatus>
          <t:Location>
          </t:Location>
          <t:Subject>TEST</t:Subject>
        </t:CalendarItem>
      </Items>
    </CreateItem>
  </soap:Body>
</soap:Envelope>

Here's the working one

<?xml version="1.0" encoding="UTF-8"?>
<soap:Envelope xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"  xmlns:tns="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:wsi="http://ws-i.org/schemas/conformanceClaim/">
  <soap:Header>
  </soap:Header>
  <soap:Body>
    <CreateItem SendMeetingInvitations="SendToAllAndSaveCopy" xmlns="http://schemas.microsoft.com/exchange/services/2006/messages">
      <SavedItemFolderId>
        <t:DistinguishedFolderId Id="calendar" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
        </t:DistinguishedFolderId>
      </SavedItemFolderId>
      <Items>
        <t:CalendarItem xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
          <t:Subject>TEST</t:Subject>
          <t:Body BodyType="HTML">TEST</t:Body>
          <t:ReminderIsSet>true</t:ReminderIsSet>
          <t:ReminderMinutesBeforeStart>15</t:ReminderMinutesBeforeStart>
          <t:Start>2017-10-11T06:10:36.890Z</t:Start>
          <t:End>2017-10-11T07:10:48.909Z</t:End>
          <t:IsAllDayEvent>false</t:IsAllDayEvent>
          <t:LegacyFreeBusyStatus>Busy</t:LegacyFreeBusyStatus>
          <t:Location>
          </t:Location>
        </t:CalendarItem>
      </Items>
    </CreateItem>
  </soap:Body>
</soap:Envelope>

Still a little bit weird that seems to be enough to fail validation.

kcastrotech commented 7 years ago

It is picky about the order of elements so that makes sense. However it doesn't make sense that even though you've defined Subject in your args (albeit as "") that it somehow changes the order when you later set it. I have done that in several of my scripts without issue.

It's a bit off-the-wall, but can you test if maybe if setting it as ["Subject"] instead of .Subject makes a difference like so:

var args = {
    "attributes" : { "SendMeetingInvitations" : "SendToAllAndSaveCopy" },
    "SavedItemFolderId": { "DistinguishedFolderId": { "attributes": { "Id": "calendar" } } },
    "Items" : {
        "CalendarItem" : {
            "Subject" : "",
            "Body" : { "attributes" : { }, "$value" : "" },
            "ReminderIsSet" : "true",
            "ReminderMinutesBeforeStart" : "30",
            "Start" : "",
            "End" : "",
            "IsAllDayEvent" : "false",
            "LegacyFreeBusyStatus" : "Busy",
            "Location" : ""
        }
    }
};

args.Items.CalendarItem["Subject"] = "A TEST SUBJECT";

Alternatively something I've also done with success is to create a template object and then merge my args with it like this:

var _ = require('lodash'); //loadash should be installed by default as part of node-ews

var ewsArgs = { //EWS argument template with defaults
    "attributes" : { "SendMeetingInvitations" : "SendToAllAndSaveCopy" },
    "SavedItemFolderId": { "DistinguishedFolderId": { "attributes": { "Id": "calendar" } } },
    "Items" : {
        "CalendarItem" : {
            "Subject" : "",
            "Body" : { "attributes" : { }, "$value" : "" },
            "ReminderIsSet" : "true",
            "ReminderMinutesBeforeStart" : "30",
            "Start" : "",
            "End" : "",
            "IsAllDayEvent" : "false",
            "LegacyFreeBusyStatus" : "Busy",
            "Location" : ""
        }
    }
};

var args = {
    "Items" : {
        "CalendarItem" : {
            "Subject" : "MY TEST SUBJECT",
            "Body" : {"$value" : "This is a test email" },
            "Start" : "2017-10-21T18:00:00.000Z",
            "End" : "2017-10-21T19:00:00.000Z"
        }
    }
};

//This should merge args into ewsArgs, preserving ewsArgs structure
_.merge(ewsArgs,args);

ews.run("CreateItem", ewsArgs);

You can also simplify this by just merging the CalendarItem:

var _ = require('lodash'); //loadash should be installed by default as part of node-ews

var ewsArgs = { //EWS argument template with defaults
    "attributes" : { "SendMeetingInvitations" : "SendToAllAndSaveCopy" },
    "SavedItemFolderId": { "DistinguishedFolderId": { "attributes": { "Id": "calendar" } } },
    "Items" : {
        "CalendarItem" : {
            "Subject" : "",
            "Body" : { "attributes" : { }, "$value" : "" },
            "ReminderIsSet" : "true",
            "ReminderMinutesBeforeStart" : "30",
            "Start" : "",
            "End" : "",
            "IsAllDayEvent" : "false",
            "LegacyFreeBusyStatus" : "Busy",
            "Location" : ""
        }
    }
};

var args = {
    "Subject" : "MY TEST SUBJECT",
    "Body" : {"$value" : "This is a test email" },
    "Start" : "2017-10-21T18:00:00.000Z",
    "End" : "2017-10-21T19:00:00.000Z"
};

//This should merge args into ewsArgs.Items.CalendarItem, preserving ewsArgs structure
_.merge(ewsArgs.Items.CalendarItem,args);

ews.run("CreateItem", ewsArgs);
kcastrotech commented 7 years ago

Just following up on this. Were you able to get it working?

relief-melone commented 7 years ago

Yep. Ill elaborate a bit more when Im back from vacation. So much. I figured out what happens but you won't be able to change the behaviour

kcastrotech commented 7 years ago

Thanks for the update!

kcastrotech commented 6 years ago

@relief-melone, just a friendly reminder.