Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

EWS: 'Set action is invalid for property' when editing RequiredAttendees

Before you close this as a duplicate please look at the other similarly titled question, there is no answer to the problem he just marked it as answered and left.

I am getting this lovely and descriptive error from the EWS manged API whenever I attempt to edit the RequiredAttendees property on an appointment.

Set action is invalid for property.

Looking at the exception details shows me that it is indeed the RequiredAttendees property that is causing problems but I have no idea why.

The credentials I use to connect to the service are those of the meeting organizer, I have even tried impersonating the user with no luck. Scratching my head trying to figure out what went wrong here.

Here are the relevant parts of the update routine that are causing problems.

PropertySet props = new PropertySet(
        AppointmentSchema.Start,
        AppointmentSchema.End,
        AppointmentSchema.Id,
        AppointmentSchema.Organizer,
        AppointmentSchema.Subject,
        AppointmentSchema.Body,
        AppointmentSchema.RequiredAttendees);
props.RequestedBodyType = BodyType.Text;

Appointment appointment = Appointment.Bind(_service, new ItemId(appointmentId), props);

if (IsResource(appointment.Organizer.Address) && appointment.Organizer.Address != resourceId)
{
    /*
    * removed for brevity, no attendee manipulation here
    */
}
else 
{
    List<Attendee> remove = new List<Attendee>();
    foreach (var attendee in appointment.RequiredAttendees)
    {
        if (IsResource(attendee.Address) && attendee.Address != resourceId)
        {
            remove.Add(attendee);
        }
    }
    remove.ForEach(a => appointment.RequiredAttendees.Remove(a));
    if (!appointment.RequiredAttendees.Any(a => a.Address == resourceId))
    {
        appointment.RequiredAttendees.Add(resourceId);
    }
}

/*
* removed for brevity, no attendee manipulation here
*/

if (IsAvailable(resourceId, startTime, endTime, appointmentId))
    appointment.Update(ConflictResolutionMode.AlwaysOverwrite, SendInvitationsOrCancellationsMode.SendToAllAndSaveCopy);
else
    throw new RoomUnavailableException();

Request Trace:

<Trace Tag = "EwsRequest" Tid="14" Time="2017-09-25 20:20:24Z" Version="15.00.0847.030">
  <?xml version = "1.0" encoding="utf-8"?>
  <soap:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/">
    <soap:Header>
      <t:RequestServerVersion Version = "Exchange2013" />
    </ soap:Header>
    <soap:Body>
      <m:UpdateItem ConflictResolution = "AlwaysOverwrite" SendMeetingInvitationsOrCancellations="SendToAllAndSaveCopy">
        <m:ItemChanges>
          <t:ItemChange>
            <t:ItemId Id = "AAMkAGEwYWRjZjA3LWNlZjAtNDI2Ny05ZjQwLWUzYWZjOThhMjkzNwBGAAAAAABWdX+yf6THTpO/1LYpoG6xBwD6lEwS6u8XQbDhIlTh/X/UAAAAAAENAAD6lEwS6u8XQbDhIlTh/X/UAAAi3oSdAAA=" ChangeKey="DwAAABYAAAD6lEwS6u8XQbDhIlTh/X/UAAAi3ocU" />
            <t:Updates>
              <t:SetItemField>
                <t:FieldURI FieldURI = "calendar:RequiredAttendees" />
                < t:CalendarItem>
                  <t:RequiredAttendees>
                    <t:Attendee>
                      <t:Mailbox>
                        <t:Name>Exchange Test</t:Name>
                        <t:EmailAddress>etest @supertester.com</t:EmailAddress>
                        <t:RoutingType>SMTP</t:RoutingType>
                        <t:MailboxType>Mailbox</t:MailboxType>
                      </t:Mailbox>
                    </t:Attendee>
                    <t:Attendee>
                      <t:Mailbox>
                        <t:EmailAddress>redroom @supertester.com</t:EmailAddress>
                      </t:Mailbox>
                    </t:Attendee>
                  </t:RequiredAttendees>
                </t:CalendarItem>
              </t:SetItemField>
            </t:Updates>
          </t:ItemChange>
        </m:ItemChanges>
      </m:UpdateItem>
    </soap:Body>
  </soap:Envelope>
</Trace>

Response Trace:

<Trace Tag = "EwsResponse" Tid="14" Time="2017-09-25 20:20:24Z" Version="15.00.0847.030">
  <?xml version = "1.0" encoding="utf-8"?>
  <s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
    <s:Header>
      <h:ServerVersionInfo MajorVersion = "15" MinorVersion="1" MajorBuildNumber="225" MinorBuildNumber="41" Version="V2_48" xmlns:h="http://schemas.microsoft.com/exchange/services/2006/types" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" />
    </s:Header>
    <s:Body xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
      <m:UpdateItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
        <m:ResponseMessages>
          <m:UpdateItemResponseMessage ResponseClass = "Error" >
            <m:MessageText>Set action is invalid for property.</m:MessageText>
            <m:ResponseCode>ErrorInvalidPropertySet</m:ResponseCode>
            <m:DescriptiveLinkKey>0</m:DescriptiveLinkKey>
            <m:MessageXml>
              <t:FieldURI FieldURI = "calendar:RequiredAttendees" />
            </m:MessageXml>
            <m:Items />
          </m:UpdateItemResponseMessage>
        </m:ResponseMessages>
      </m:UpdateItemResponse>
    </s:Body>
  </s:Envelope>
</Trace>
like image 343
FlyingStreudel Avatar asked Oct 17 '22 05:10

FlyingStreudel


2 Answers

Big thanks to Glen Scales for pointing me in the right direction with this one.

When I was retrieving appointments I was using the following code:

CalendarFolder calendar = CalendarFolder.Bind(_service, new FolderId(WellKnownFolderName.Calendar, resourceId), PropertySet.IdOnly);
CalendarView cView = new CalendarView(startDate, endDate, _maxAppointments);
cView.PropertySet = new PropertySet(PropertySet.IdOnly);
FindItemsResults<Appointment> appointments = calendar.FindAppointments(cView);

Where resourceId was the address of the room mailbox, not the organizer of the meeting.

Editing the attendees on an appointment that is not the original is not allowed so that is what was giving me the error. In order to update the attendees I have to retrieve the organizers appointment with the following code (borrowed heavily from this post):

appointment = FindOrganizerAppointment(appointment);

/// <summary>
/// Finds the related Appointment.
/// </summary>
/// <param name="appointment">The appointment whose original is to be found.</param>
/// <returns></returns>
private Appointment FindOrganizerAppointment(Appointment appointment)
{
    try
    {
        Impersonate(appointment.Organizer.Address);

        var filter = new SearchFilter.IsEqualTo
        {
            PropertyDefinition = new ExtendedPropertyDefinition
                (DefaultExtendedPropertySet.Meeting, 0x03, MapiPropertyType.Binary),
            Value = GetObjectIdStringFromUid(appointment.ICalUid)
        };

        var view = new ItemView(1) { PropertySet = new PropertySet(BasePropertySet.FirstClassProperties) };

        return _service.FindItems(WellKnownFolderName.Calendar, filter, view).Items[0] as Appointment;
    }
    catch (Exception e)
    {
         throw e;
    }
    finally
    {
        DisableImpersonation();
    }
}

/// <summary>
/// Gets the object id string from uid.
/// <remarks>The UID is formatted as a hex-string and the GlobalObjectId is displayed as a Base64 string.</remarks>
/// </summary>
/// <param name="id">The uid.</param>
/// <returns></returns>
private static string GetObjectIdStringFromUid(string id)
{
    var buffer = new byte[id.Length / 2];
    for (int i = 0; i < id.Length / 2; i++)
    {
        var hexValue = byte.Parse(id.Substring(i * 2, 2), System.Globalization.NumberStyles.AllowHexSpecifier);
        buffer[i] = hexValue;
    }
    return Convert.ToBase64String(buffer);
}
like image 148
FlyingStreudel Avatar answered Oct 30 '22 22:10

FlyingStreudel


Please do note that "RequiredAttendees" object will be filled only if it a meeting.

In your else statement add this check

if(appointment.IsMeeting)
{
  List<Attendee> remove = new List<Attendee>();
 foreach (var attendee in appointment.RequiredAttendees)
 {
    if (IsResource(attendee.Address) && attendee.Address != resourceId)
    {
        remove.Add(attendee);
    }
 }
 remove.ForEach(a => appointment.RequiredAttendees.Remove(a));
 if (!appointment.RequiredAttendees.Any(a => a.Address == resourceId))
 {
    appointment.RequiredAttendees.Add(resourceId);
 }
}

info about appointment meeting property MSDN link

like image 44
Sivakrishna Donepudi Avatar answered Oct 31 '22 00:10

Sivakrishna Donepudi