Open Yenthe666 opened 8 years ago
@trustrachel, @got-root, @ematthews could I ask you guys for input please?
So sorry about the delay - I don't officially maintain this any more. :) I'm at a different company and don't have access to an Exchange server, so I can only give advice.
So the error you're getting probably means the XML you're sending Exchange is either malformed or not correct, so Exchange is confused and doesn't know what to do. PyExchange is just passing on the error.
The first thing to do is test sending the raw SOAP request by using this script. Just change the REQUEST string to whatever XML you're sending:
https://gist.github.com/trustrachel/b70d79ea670f048ed165
If that doesn't work, then just fix as necessary and off you go. If it does work, let me know and I can dig more.
thank you!
@trustrachel no problem for the delay, I'm happy you took the time to respond. So thanks a lot for that :smile: Alright so I gave your testscript a run with a demo script from SharePoint to create a new contact. I've used the sample from https://msdn.microsoft.com/en-us/library/aa580529(v=exchg.140).aspx in your testscript (https://gist.github.com/trustrachel/b70d79ea670f048ed165)
How the testscript looks:
#!/usr/bin/env python
import os
import logging
import requests
from requests_ntlm import HttpNtlmAuth
import getpass
import xml.dom.minidom
logging.basicConfig(level=logging.DEBUG)
EXCHANGE_SERVER = 'https://mail.mymail.com'
DOMAIN = 'MyDomain'
USERNAME = 'yenthe'#os.environ.get('yenthe.vanginneken') or 'DUMMY_USERNAME'
# PASSWORD is prompted for
# This just asks the server for all the timezones it knows about
# http://msdn.microsoft.com/en-us/library/dd899430(v=exchg.140).aspx
REQUEST = """<?xml version="1.0" encoding="utf-8"?>
<soap:Envelope
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<soap:Body>
<CreateItem xmlns="http://schemas.microsoft.com/exchange/services/2006/messages" >
<SavedItemFolderId>
<t:DistinguishedFolderId Id="contacts"/>
</SavedItemFolderId>
<Items>
<t:Contact>
<t:FileAs>SampleContact</t:FileAs>
<t:GivenName>Tanja</t:GivenName>
<t:CompanyName>Blue Yonder Airlines</t:CompanyName>
<t:EmailAddresses>
<t:Entry Key="EmailAddress1">tplate@example.com</t:Entry>
</t:EmailAddresses>
<t:PhysicalAddresses>
<t:Entry Key="Business">
<t:Street>1234 56th Ave</t:Street>
<t:City>La Habra</t:City>
<t:State>CA</t:State>
<t:CountryOrRegion>USA</t:CountryOrRegion>
</t:Entry>
</t:PhysicalAddresses>
<t:PhoneNumbers>
<t:Entry Key="BusinessPhone">4255550199</t:Entry>
</t:PhoneNumbers>
<t:JobTitle>Manager</t:JobTitle>
<t:Surname>Plate</t:Surname>
</t:Contact>
</Items>
</CreateItem>
</soap:Body>
</soap:Envelope>"""
if __name__ == '__main__':
if EXCHANGE_SERVER == '' or USERNAME == "DUMMY_USERNAME" or DOMAIN == "DOMAIN":
raise SystemExit("Hey, you need to edit the script to add your custom information before you run it.")
PASSWORD = os.environ.get('EXCHANGE_PASSWORD') or getpass.getpass()
HEADERS = {
'Content-type': 'text/xml; charset=utf-8 ',
'Accept': 'text/xml'
}
response = requests.post(EXCHANGE_SERVER,
auth=HttpNtlmAuth('%s\\%s' % (DOMAIN, USERNAME), PASSWORD),
data=REQUEST,
headers=HEADERS)
# lxml is better, but all I want to do is pretty print the XML response
print(xml.dom.minidom.parseString(response.text).toprettyxml())
When I run this script I will get the following results back (so a succesfull creation):
INFO:urllib3.connectionpool:Starting new HTTPS connection (1): mail.mymail.com
DEBUG:urllib3.connectionpool:Setting read timeout to None
DEBUG:urllib3.connectionpool:"POST /EWS/Exchange.asmx HTTP/1.1" 401 0
DEBUG:urllib3.connectionpool:Setting read timeout to None
DEBUG:urllib3.connectionpool:"POST /EWS/Exchange.asmx HTTP/1.1" 401 0
DEBUG:urllib3.connectionpool:Setting read timeout to None
DEBUG:urllib3.connectionpool:"POST /EWS/Exchange.asmx HTTP/1.1" 200 None
<?xml version="1.0" ?>
<s:Envelope xmlns:s="http://schemas.xmlsoap.org/soap/envelope/">
<s:Header>
<h:ServerVersionInfo MajorBuildNumber="174" MajorVersion="14" MinorBuildNumber="1" MinorVersion="3" xmlns="http://schemas.microsoft.com/exchange/services/2006/types" xmlns:h="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:xsd="http://www.w3.org/2001/XMLSchema" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<m:CreateItemResponse xmlns:m="http://schemas.microsoft.com/exchange/services/2006/messages" xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:ResponseMessages>
<m:CreateItemResponseMessage ResponseClass="Success">
<m:ResponseCode>NoError</m:ResponseCode>
<m:Items>
<t:Contact>
<t:ItemId ChangeKey="EQAAABYAAACrppuUEDMRTaciavm/Wr6lAAB7y06S" Id="AAAdAFllbnRoZS5WYW5HaW5uZWtlbkB2YW5yb2V5LmJlAEYAAAAAADrZHpTamLVLl+s1ej9c6goHAKumm5QQMxFNpyJq+b9avqUAAAAAv9wAAKumm5QQMxFNpyJq+b9avqUAAHvLRpIAAA=="/>
</t:Contact>
</m:Items>
</m:CreateItemResponseMessage>
</m:ResponseMessages>
</m:CreateItemResponse>
</s:Body>
</s:Envelope>
Now, back to the code that I've added to the library, which you can find here: https://github.com/Yenthe666/pyexchange/blob/master/pyexchange/exchange2010/soap_request.py#L634-L683 The method to create a new contact:
def new_contact(contact):
"""
Requests a new Contact to be created
https://msdn.microsoft.com/en-us/library/aa564690(v=exchg.140).aspx
<m:CreateItem
xmlns=m:"http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:SavedItemFolderId>
<t:DistinguishedFolderId Id="contacts"/>
</m:SavedItemFolderId>
<m:Items>
<t:Contact>
<t:FileAs>SampleContact</t:FileAs>
<t:GivenName>{contact.subject}</t:GivenName>
<t:CompanyName>{contact.body}</t:CompanyName>
<t:EmailAddresses>
<t:Entry Key="EmailAddress1">tplate@example.com</t:Entry>
</t:EmailAddresses>
<t:PhysicalAddresses>
<t:Entry Key="Business">
<t:Street>1234 56th Ave</t:Street>
<t:City>La Habra</t:City>
<t:State>CA</t:State>
<t:CountryOrRegion>USA</t:CountryOrRegion>
</t:Entry>
</t:PhysicalAddresses>
<t:PhoneNumbers>
<t:Entry Key="BusinessPhone">4255550199</t:Entry>
</t:PhoneNumbers>
<t:JobTitle>Manager</t:JobTitle>
<t:Surname>Plate</t:Surname>
</t:Contact>
</m:Items>
</m:CreateItem>
"""
id = T.DistinguishedFolderId(Id=contact.calendar_id) if contact.calendar_id in DISTINGUISHED_IDS else T.FolderId(Id=calendar.contact_id)
root = M.CreateItem(
M.SavedItemFolderId(id),
M.Items(
T.Contact(
T.GivenName(contact.name),
T.CompanyName(contact.company_name or u'', BodyType="HTML"),
)
),
SendMeetingInvitations="SendToAllAndSaveCopy"
)
contact_node = root.xpath(u'/m:CreateItem/m:Items/t:Contact', namespaces=NAMESPACES)[0]
return root
I have no idea what is wrong with my code though.. Think you could have a look at it too? There must be something wrong.
Aha, you're not sending the XML you expect. You see where you're setting the root
variable? There's a stray SendMeetingInvitations="SendToAllAndSaveCopy"
, which is only for calendar stuff.
Try getting rid of that. If that still doesn't work, try upping the logging to debug and you should see exactly what's going over the wire to Exchange and what you're getting back. IIRC, Exchange will tell you if you have malformed XML.
This is the one real flaw in using raw XML instead of a more heavyweight SOAP library, we don't get the benefit of XML schemas. :disappointed:
@trustrachel thanks for the feedback, good catch there! I've removed the line SendMeetingInvitations="SendToAllAndSaveCopy"
so I now have the following code:
def new_contact(contact):
"""
Requests a new Contact to be created
https://msdn.microsoft.com/en-us/library/aa564690(v=exchg.140).aspx
<m:CreateItem
xmlns=m:"http://schemas.microsoft.com/exchange/services/2006/messages"
xmlns:t="http://schemas.microsoft.com/exchange/services/2006/types">
<m:SavedItemFolderId>
<t:DistinguishedFolderId Id="contacts"/>
</m:SavedItemFolderId>
<m:Items>
<t:Contact>
<t:FileAs>SampleContact</t:FileAs>
<t:GivenName>{contact.subject}</t:GivenName>
<t:CompanyName>{contact.body}</t:CompanyName>
<t:EmailAddresses>
<t:Entry Key="EmailAddress1">tplate@example.com</t:Entry>
</t:EmailAddresses>
<t:PhysicalAddresses>
<t:Entry Key="Business">
<t:Street>1234 56th Ave</t:Street>
<t:City>La Habra</t:City>
<t:State>CA</t:State>
<t:CountryOrRegion>USA</t:CountryOrRegion>
</t:Entry>
</t:PhysicalAddresses>
<t:PhoneNumbers>
<t:Entry Key="BusinessPhone">4255550199</t:Entry>
</t:PhoneNumbers>
<t:JobTitle>Manager</t:JobTitle>
<t:Surname>Plate</t:Surname>
</t:Contact>
</m:Items>
</m:CreateItem>
"""
id = T.DistinguishedFolderId(Id=contact.calendar_id) if contact.calendar_id in DISTINGUISHED_IDS else T.FolderId(Id=calendar.contact_id)
root = M.CreateItem(
M.SavedItemFolderId(id),
M.Items(
T.Contact(
T.GivenName(contact.name),
T.CompanyName(contact.company_name or u'', BodyType="HTML"),
)
),
)
contact_node = root.xpath(u'/m:CreateItem/m:Items/t:Contact', namespaces=NAMESPACES)[0]
return root
So, where exactly is the debugger level defined? I can't find it directly. And yep the debugging is horrid so it seems. Perhaps I should just go with the SOAP without using the library..
It's just using the standard Python logger - you set it in whatever script you're using to run pyexchange. The easiest thing is to just set the root logger to DEBUG, but I think you can just target pyexchange with logger.getLogger('pyexchange'). If you don't know how to do that, see this: https://docs.python.org/2/howto/logging.html#configuring-logging
SOAP will also work, but I found that they're all either very slow or don't handle unicode.
@trustrachel I finally had some time to look at it and found the fix. The problem was some HTML field that wasn't allowed there to be used in that way. :+1: Now I have a new error:
AttributeError: 'Exchange2010ContactEvent' object has no attribute '_parse_id_and_change_key_from_response'
Think you could have a look at this? I've made an issue on my own branch here: https://github.com/Yenthe666/pyexchange/issues/1
It looks to me like my self programmed Exchange2010ContactEvent class and Exchange2010FolderService are missing something or are doing something wrong.
@trustrachel sorry to ping you again but could you have a look please?
Hi guys,
Since this library had some amazing implementations and did a lot of the things I needed I decided to expand it and put some effort in to it. I'm not used to anything related Exchange / SOAP but tried to manage with what was already here. I've forked this repository and added as much as I could for now. You can see my added code here: https://github.com/linkedin/pyexchange/compare/master...Yenthe666:master
I've now ran stuck on the following error:
Which comes from exceptions.py in the function FailedExchangeException:
Example code to create a new contact:
Could anybody help me and finish this implementation? I do not know how to fix this issue but I think this is a good start.