I am relatively new to SOAP frameworks and have been reading through Spynes docs and trying to figure out to build a service that accepts the following request:
<?xml version='1.0' encoding='UTF-8'?>
<SOAP-ENV:Envelope xmlns:SOAP-ENV="http://..." xmlns:xsi=http:/..." xmlns:xsd="http://...">
<SOAP-ENV:Body>
<modifyRequest returnData="everything" xmlns="urn:...">
<attr ID="..."/>
<data>
</data>
</modifyRequest>
</SOAP-ENV:Body>
</SOAP-ENV:Envelope>
I have read through the docs but just have not seen enough more complex examples to figure out how to put together something to handle this. The <attr ID="..."/> tag must be processed for the ID attribute and the <data> tags contain some varying collection of additional xml. I understand its better to formally define the service but for now I was hoping to use anyXML (?) to accept whatever is in the tags. I need to accept and process the ID attribute along with its xml payload contained within the data tags.
I'd be grateful for any guidance,
Thanks.
Here's how you'd do it:
NS = 'urn:...'
class Attr(ComplexModel):
__namespace__ = NS
_type_info = [
('ID', XmlAttribute(UnsignedInteger32)),
]
class ModifyRequest(ComplexModel):
__namespace__ = NS
_type_info = [
('returnData', XmlAttribute(Unicode(values=['everything', 'something', 'anything', 'etc']))),
('attr', Attr),
('data', AnyXml),
]
class SomeService(ServiceBase):
#rpc(ModifyRequest, _body_style='bare')
def modifyRequest(ctx, request):
pass
This requires Spyne 2.11 though, _body_style='bare' in problematic in 2.10 and older.
Related
I've tried a lot, but I haven't found a working solution to my problem, so I hope you can help me:
I am about to write a python module, which sends an XML request to a DNS server, receives an XML as response and should process this response. However, I am already failing in sending the request.
I have an XML base structure which I want to fill with different elements depending on the action to be performed on the DNS.
For this purpose I read in an XML string with .fromstring, edit the xml object and want to send it back to the server with .tostring. The problem is that .tostring does not return a usable xml string. The following example shows what I mean:
import xml.etree.ElementTree as ET
import requests
headers = {'HeaderSOAP': 'SOAPAction:urn:QIPServices#getEntry'}
body = """<soapenv:Envelope xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:urn="urn:QIPServices">
<soapenv:Header/>
<soapenv:Body>
<urn:getEntry soapenv:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">
<login xsi:type="xsd:string">USERNAME</login>
<password xsi:type="xsd:string">PASSWD</password>
<sharedsecret xsi:type="xsd:string">SECRET</sharedsecret>
<VPN xsi:type="xsd:string">VPN</VPN>
<IPoderName xsi:type="xsd:string">IPorFQDN</IPoderName>
</urn:getEntry>
</soapenv:Body>
</soapenv:Envelope>
"""
root = ET.fromstring(body)
ThatsTheProblem = ET.tostring(root, encoding='utf-8')
print(ThatsTheProblem)
returns:
b'<ns0:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns1="urn:QIPServices" xmlns:xsi="http://www.w3.org/2001/XMLSchema-
instance">\n <ns0:Header />\n <ns0:Body>\n
<ns1:getEntry
ns0:encodingStyle="http://schemas.xmlsoap.org/soap/encoding/">\n
<login xsi:type="xsd:string">USERNAME</login>\n <password
xsi:type="xsd:string">PASSWD</password>\n <sharedsecret
xsi:type="xsd:string">SECRET</sharedsecret>\n <VPN
xsi:type="xsd:string">VPN</VPN>\n <IPoderName
xsi:type="xsd:string">IPorFQDN</IPoderName>\n
</ns1:getEntry>\n </ns0:Body>\n </ns0:Envelope>'
Without having changed anything, the import and output not only changed the complete formatting, there are also whitespaces everywhere in the file. When I send this XML to the server using
response = requests.post(url,data=ThatsTheProblem, headers=headers)
I get the following answer:
Application failed during request deserialization: Unresolved prefix \'xsd\' for attribute value \'xsd:string\'\n
Which I attribute to the problem I described at the beginning.
If anyone has a solution to this problem I would be very grateful.
Thanks and have a nice day.
Using Python zeep, I'm interacting with Salesforce's SOAP (specifically, Metadata) API.
Trying to createMetadata I get this error:
Fault: Must specify a {http://www.w3.org/2001/XMLSchema-instance}type attribute value for the {http://soap.sforce.com/2006/04/metadata}metadata element
I've gathered that this is not about parameters passed to the method (the way createMetadata requires a metadata argument, which itself is an object with a fullName field), but rather about a missing xsi:type attribute somewhere.
This is my zeep call:
resp = service['createMetadata'](_soapheaders=soap_headers,
metadata=[{'fullName': 'SomeCustomObject'}])
This is the generated XML:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body>
<ns0:createMetadata xmlns:ns0="http://soap.sforce.com/2006/04/metadata">
<ns0:metadata>
<ns0:fullName>SomeCustomObject</ns0:fullName>
</ns0:metadata>
</ns0:createMetadata>
</soap-env:Body>
</soap-env:Envelope>
My question is: how can I set that xsi:type on whatever it needs to be set on (that ns0:metadata guy?) using zeep?
UPDATE:
Instead of using a dictionary to represent the metadata object, I replaced it with this:
metadata_type = client.get_type('{http://soap.sforce.com/2006/04/metadata}Metadata')
metadata = metadata_type(fullName='SomeCustomObject')
resp = service['createMetadata'](_soapheaders=soap_headers, metadata=[metadata])
The new generated XML is:
<soap-env:Envelope xmlns:soap-env="http://schemas.xmlsoap.org/soap/envelope/">
<soap-env:Body>
<ns0:createMetadata xmlns:ns0="http://soap.sforce.com/2006/04/metadata">
<ns0:metadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="ns0:Metadata">
<ns0:fullName>SomeCustomObject</ns0:fullName>
</ns0:metadata>
</ns0:createMetadata>
</soap-env:Body>
</soap-env:Envelope>
which has the xsi:type attribute on the ns0:metadata tag, but I get the same error as before. So I guess it wasn't about a missing xsi:type. Any ideas on what it is?
Here you can see that xsi is defined in metadata.
<metadata xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:type="CustomField">
</metadata>
The answer is that xsi:type should use 'CustomObject' (or another appropriate type) instead of 'Metadata', which is the "parent" type, I believe. Which also then requires passing more than just fullName.
In zeep that means instead of
metadata_type = client.get_type('{http://soap.sforce.com/2006/04/metadata}Metadata')
metadata = metadata_type(fullName='SomeCustomObject')
I used
custom_object_type = client.get_type('{http://soap.sforce.com/2006/04/metadata}CustomObject')
custom_object = custom_object_type(fullName='SomeCustomObject__c',
label='SomeCustomObject',
pluralLabel='SomeCustomObjects',
nameField={'label': 'name', 'type': 'Text'},
deploymentStatus='Deployed',
sharingModel='ReadWrite')
and then finally:
resp = service['createMetadata'](_soapheaders=soap_headers,
metadata=[custom_object])
I am trying to parse the below xml using the python. I do not understand which type of xml this is as I never worked on this kind of xml.I just got it from a api response form Microsoft.
Now my question is how to parse and get the value of BinarySecurityToken in my python code.
I refer this question Parse XML SOAP response with Python
But look like this has also some xmlns to get the text .However in my xml I can't see any nearby xmlns value through I can get the value.
Please let me know how to get the value of a specific filed using python from below xml.
<?xml version="1.0" encoding="utf-8" ?>
<S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:wsa="http://www.w3.org/2005/08/addressing">
<S:Header>
<wsa:Action xmlns:S="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="Action" S:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2005/02/trust/RSTR/Issue</wsa:Action>
<wsa:To xmlns:S="http://www.w3.org/2003/05/soap-envelope" xmlns:wsa="http://www.w3.org/2005/08/addressing" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="To" S:mustUnderstand="1">http://schemas.xmlsoap.org/ws/2004/08/addressing/role/anonymous</wsa:To>
<wsse:Security S:mustUnderstand="1">
<wsu:Timestamp xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" wsu:Id="TS">
<wsu:Created>2017-06-12T10:23:01Z</wsu:Created>
<wsu:Expires>2017-06-12T10:28:01Z</wsu:Expires>
</wsu:Timestamp>
</wsse:Security>
</S:Header>
<S:Body>
<wst:RequestSecurityTokenResponse xmlns:S="http://www.w3.org/2003/05/soap-envelope" xmlns:wst="http://schemas.xmlsoap.org/ws/2005/02/trust" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" xmlns:wsu="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd" xmlns:saml="urn:oasis:names:tc:SAML:1.0:assertion" xmlns:wsp="http://schemas.xmlsoap.org/ws/2004/09/policy" xmlns:psf="http://schemas.microsoft.com/Passport/SoapServices/SOAPFault">
<wst:TokenType>urn:passport:compact</wst:TokenType>
<wsp:AppliesTo xmlns:wsa="http://www.w3.org/2005/08/addressing">
<wsa:EndpointReference>
<wsa:Address>https://something.something.something.com</wsa:Address>
</wsa:EndpointReference>
</wsp:AppliesTo>
<wst:Lifetime>
<wsu:Created>2017-06-12T10:23:01Z</wsu:Created>
<wsu:Expires>2017-06-13T10:23:01Z</wsu:Expires>
</wst:Lifetime>
<wst:RequestedSecurityToken>
<wsse:BinarySecurityToken Id="Compact0">my token</wsse:BinarySecurityToken>
</wst:RequestedSecurityToken>
<wst:RequestedAttachedReference>
<wsse:SecurityTokenReference>
<wsse:Reference URI="wwwww=">
</wsse:Reference>
</wsse:SecurityTokenReference>
</wst:RequestedAttachedReference>
<wst:RequestedUnattachedReference>
<wsse:SecurityTokenReference>
<wsse:Reference URI="swsw=">
</wsse:Reference>
</wsse:SecurityTokenReference>
</wst:RequestedUnattachedReference>
</wst:RequestSecurityTokenResponse>
</S:Body>
</S:Envelope>
This declaration is part of the start tag of the root element:
xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"
It means that elements with the wsse prefix (such as BinarySecurityToken) are in the http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd namespace.
The solution is basically the same as in the answer to the linked question. It's just another namespace:
import xml.etree.ElementTree as ET
tree = ET.parse('soap.xml')
print tree.find('.//{http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd}BinarySecurityToken').text
Here is another way of doing it:
import xml.etree.ElementTree as ET
ns = {"wsse": "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"}
tree = ET.parse('soap.xml')
print tree.find('.//wsse:BinarySecurityToken', ns).text
The output in both cases is my token.
See https://docs.python.org/2.7/library/xml.etree.elementtree.html#parsing-xml-with-namespaces.
Creating a namespace dict helped me. Thank you #mzjn for linking that article.
In my SOAP response, I found that I was having to use the full path to the element to extract the text.
For example, I am working with FEDEX API, and one element that I needed to find was TrackDetails. My initial .find() looked like .find('{http://fedex.com/ws/track/v16}TrackDetails')
I was able to simplify this to the following:
ns = {'TrackDetails': 'http://fedex.com/ws/track/v16'}
tree.find('TrackDetails:TrackDetails',ns)
You see TrackDetails twice because I named the key TrackDetails in the dict, but you could name this anything you want. Just helped me to remember what I was working on in my project, but the TrackDetails after the : is the actual element in the SOAP response that I need.
Hope this helps someone!
I am in the process of implementing some pre-defined WSDL which uses multiple namespaces. For simplicity, I have requests that look something like:
<?xml version='1.0' encoding='UTF-8'?>
<soapenv:Envelope xmlns:soapenv="schemas.xmlsoap.org/soap/envelope/">
<soapenv:Header>
</soapenv:Header>
<soapenv:Body>
<a:Foo xmlns:a="www.example.com/schema/a" AttrA="a1" AttrB="b2">
<b:Baz xmlns:b="www.example.com/schema/b" AttrC="c3"/>
<a:Bar>blah</a:Bar>
</a:Foo>
</soapenv:Body>
</soapenv:Envelope>
I've been using code like:
from spyne.model.primitive import Unicode
from spyne.model.complex import Iterable, XmlAttribute, ComplexModel, ComplexModelMeta, ComplexModelBase
from spyne.service import ServiceBase
from spyne.protocol.soap import Soap11
from spyne.application import Application
from spyne.decorator import srpc, rpc
class BazBase(ComplexModelBase):
__namespace__ = "www.example.com/schema/b"
__metaclass__ = ComplexModelMeta
class Baz(BazBase):
Thing = Unicode
AttrC = XmlAttribute(Unicode)
class FooService(ServiceBase):
__namespace__ = "www.example.com/schema/a"
#rpc(XmlAttribute(Unicode), XmlAttribute(Unicode), Baz, Unicode, _returns=Iterable(Unicode))
def Foo(ctx, AttrA, AttrB, Baz, Bar):
yield 'Hello, %s' % Bar
app = Application([FooService],
"www.example.com/schema/a",
in_protocol=Soap11(validator='lxml'),
out_protocol=Soap11(),
)
to parse things, but I get:
<?xml version='1.0' encoding='utf-8'?>
<senv:Envelope xmlns:senv="schemas.xmlsoap.org/soap/envelope/">
<senv:Body>
<senv:Fault>
<faultcode>senv:Client.SchemaValidationError</faultcode>
<faultstring>
<string>:1:0:ERROR:SCHEMASV:SCHEMAV_ELEMENT_CONTENT:
Element '{www.example.com/schema/b}Baz': This element
is not expected. Expected is one of (
{www.example.com/schema/a}Baz,
{www.example.com/schema/a}Bar ).</faultstring>
<faultactor></faultactor>
</senv:Fault>
</senv:Body>
</senv:Envelope>
as the response.
I've tried using the schema_tag parameter, but nothing I put in there seems to work, with errors like 'ValueError: Unhandled schema_tag / type combination.' or 'ValueError: InvalidTagName'
What do I need to do to properly handle multiple namespaces in the same request document?
To my knowledge, Spyne does the right thing there and the request is incorrect. Child elements are always under parent's namespace. The children to those elements can be in own namespace.
<a:Foo xmlns:a="www.example.com/schema/a" AttrA="a1" AttrB="b2">
<a:Baz xmlns:b="www.example.com/schema/b" AttrC="c3"/>
<a:Bar>blah</a:Bar>
</a:Foo>
That said, you can just use the soft validator which doesn't care about namespaces.
I am attempting to pull list Items from a sharepoint server(via python/suds) and am having some difficulty with making queries to GetListItems. If i provide no other parameters to GetListItems other than the list Name, it will return all the items in the default view for that List. I want to query a specific set of items(ID=15) to be returned and a specific set of fields(Date and Description) to be returned for those items. Here is my SOAP packet:
<?xml version="1.0" encoding="UTF-8"?>
<SOAP-ENV:Envelope xmlns:ns0="http://schemas.xmlsoap.org/soap/envelope "xmlns:ns1="http://schemas.microsoft.com/sharepoint/soap/" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:SOAP-ENV="http://schemas.xmlsoap.org/soap/envelope/">
<SOAP-ENV:Header/>
<ns0:Body>
<ns1:GetListItems>
<ns1:listName>MyCalendar</ns1:listName>
<query>
<Where>
<Eq>
<FieldRef Name="_ows_ID">15</FieldRef>
</Eq>
</Where>
</query>
<viewFields>
<FieldRef Name="Description"/>
<FieldRef Name="EventDate"/>
</viewFields>
</ns1:GetListItems>
</ns0:Body>
</SOAP-ENV:Envelope>
This appears to conform to the WSDL, however, when making the query i get back a full list of items with all the fields(as if i did not pass any query XML at all).
Any suggestions for a SOAP noob?
Also, here is my python code that generated this XML:
query = Element('query')
where = Element('Where')
eq = Element('Eq')
eq.append(Element('FieldRef').append(Attribute('Name', '_ows_ID')).setText('15'))
where.append(eq)
query.append(where)
viewFields = Element('viewFields')
viewFields.append(Element('FieldRef').append(Attribute('Name','Description')))
viewFields.append(Element('FieldRef').append(Attribute('Name','EventDate')))
results = c_lists.service.GetListItems('MyCalendar', None, query, viewFields, None, None)
Any help would be greatly appreciated!
I figured this one out. The problem was the CAML query.
1. You need to wrap the CAML 'Query' in a 'query' element.
2. You need to set the proper 'Value Type' Element and attributes.
See me other posting titled 'Sharepoint Filter for List Items(GetListItems)' for more info and code.
Thanks!
Nick