Ben Kilah
Ben Kilah

Reputation: 3475

Python SUDS Error

I'm trying to convert a PHP script over to python but cannot for the life of me figure out why the following is not working.

Results returned from the SOAP service query:

Suds ( https://fedorahosted.org/suds/ )  version: 0.4 GA  build: R699-20100913

Service ( ExternalQueryNameAvailabilityService ) tns="http://asic.gov.au/wsdl/name/availability/external"
Prefixes (6)
  ns0 = "http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"
  ns1 = "http://www.w3.org/2005/05/xmlmime"
  ns2 = "uri:business.document.header.types.asic.gov.au"
  ns3 = "uri:external.query.name.availability.asic.gov.au"
  ns4 = "uri:fss.types.asic.gov.au"
  ns5 = "uri:types.asic.gov.au"
Ports (1):
  (ExternalQueryNameAvailabilityPort)
     Methods (1):
        externalQueryNameAvailability(ns2:businessDocumentHeaderType businessDocumentHeader, ns3:requestDataType businessDocumentBody, )
     Types (113):
        ns0:AttributedDateTime
        ns0:AttributedURI
        ns0:TimestampType
        ns5:abnApplicationReferenceNumberType
        ns5:abnType
        ns5:accountIdentifierType
        ns5:actionType
        ns5:addressType
        ns5:addressTypeType
        ns5:agentNameType
        ns5:agentType
        ns5:amountSignedType
        ns5:amountType
        ns5:applicationStatusType
        ns4:ascotDocumentNoType
        ns5:asicNumericIdType
        ns4:asicPaymentDetailsType
        ns5:asicSignatoryType
        ns2:attachmentType
        ns2:attachmentsType
        ns1:base64Binary
        ns5:birthDetailsType
        ns5:bnReferenceNumberType
        ns5:browserIdentifierType
        ns2:businessDocumentHeaderType
        ns2:businessDocumentRequestHeaderType
        ns5:businessNameIdentifierType
        ns5:codeType
        ns5:creditCardType
        ns4:customerReferenceNumberType
        ns4:debtorType
        ns5:descriptionType
        ns5:distinguishedNameType
        ns5:distinguishedWordType
        ns5:documentIdentifierType
        ns5:documentNoType
        ns5:emailType
        ns5:entityType
        ns5:exceptionListType
        ns5:exceptionType
        ns4:feeType
        ns4:feeWithAmountType
        ns4:feesType
        ns5:flagType
        ns4:fssAccountType
        ns4:fssCustomerType
        ns4:fssItemType
        ns4:fssSimpleAccountType
        ns4:fssTransactionType
        ns2:genericResultType
        ns1:hexBinary
        ns5:inboxIdentifierType
        ns5:intervalStatusType
        ns4:invoiceType
        ns5:itemSearchScopeType
        ns5:itemSummaryType
        ns5:itemTypeType
        ns5:keyType
        ns4:ledgerType
        ns5:lodgementIdentifierType
        ns2:messageEventType
        ns2:messageEventsType
        ns5:messageIdentifierType
        ns2:messageTimestampType
        ns2:messageTimestampsType
        ns5:nameAvailabilityType
        ns5:nameResultType
        ns5:nameResultWithObjectionsType
        ns5:nameType
        ns5:nniNameType
        ns5:nniNumberType
        ns5:objectionType
        ns5:organisationIdentifierType
        ns5:organisationNamePlusIdType
        ns5:originatingChannelType
        ns5:originatingServiceType
        ns5:outboundItemIdentifierType
        ns4:paymentDetailsType
        ns4:paymentMethodType
        ns5:paymentType
        ns5:personNameBirthType
        ns5:personNameType
        ns5:personNameWithRoleType
        ns4:priceType
        ns3:queryNameAvailabilityReplyType
        ns3:queryNameAvailabilityRequestType
        ns5:realmIdentifierType
        ns5:realmQualifierType
        ns4:receiptType
        ns5:referenceNoType
        ns5:rejectedType
        ns3:replyDataType
        ns5:replyType
        ns3:requestDataType
        ns5:requestFailedType
        ns5:requestRejectedType
        ns5:requestType
        ns5:resultType
        ns5:signatoryType
        ns5:soapSoftwareIdentifierType
        ns2:softwareInformationType
        ns5:standardHeaderType
        ns5:standardMessageHeaderType
        ns5:stateTerritoryCodeType
        ns5:statusType
        ns5:streetType
        ns4:suffixType
        ns0:tTimestampFault
        ns5:telephoneNumberType
        ns5:textType
        ns4:transactionType
        ns4:transactionsType
        ns5:trueType

When trying to execute the following:

con = connect('ExternalQueryNameAvailabilityPort', test, {'Content-Type': 'application/soap+xml'})
q_header = con.factory.create('ns2:businessDocumentHeaderType')
q_header.messageType = 'queryNameAvailability'
q_header.messageVersion = '2'
q_header.messageReferenceNumber = '100'
q_header.senderType = 'REGA'
q_header.senderId = '192'
q_body = con.factory.create('ns3:businessDocumentBody')
q_body.proposedName = 'Xtramedia.net PTY LTD'   
q_body.companyNameAvailabilityCheck = 'true'
q_body.bnNameAvailabilityCheck = 'true'
result = con.service.externalQueryNameAvailability(q_header, q_body)

I get the following error:

DEBUG:suds.client:http failed:
<?xml version='1.0' encoding='UTF-8'?>
  <S:Envelope xmlns:S="http://www.w3.org/2003/05/soap-envelope">
   <S:Body>
    <S:Fault xmlns:ns4="http://schemas.xmlsoap.org/soap/envelope/">
     <S:Code>
      <S:Value>S:Receiver</S:V6alue>
     </S:Code>
     <S:Reason>
      <S:Text xml:lang="en">org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'ns1:businessDocumentHeader'. One of '{"uri:business.document.header.types.asic.gov.au":businessDocumentHeader}' is expected
      </S:Text>
     </S:Reason>
    </S:Fault>
   </S:Body>
  </S:Envelope> 

Any ideas why it would be complaining about that element? - I've tried removed the "nsX" part of the element declaration but same thing.

UPDATE: The following is what the PHP Script generates and is successfull;

    <?xml version="1.0" encoding="UTF-8"?>
<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope" xmlns:ns1="uri:business.document.header.types.asic.gov.au" xmlns:ns2="uri:external.query.name.availability.asic.gov.au">
    <env:Body>
     <ns2:request>
      <ns1:businessDocumentHeader>
        <ns1:messageType>queryNameAvailability</ns1:messageType>
        <ns1:messageReferenceNumber>1</ns1:messageReferenceNumber>
        <ns1:messageVersion>2</ns1:messageVersion>
        <ns1:senderId>192</ns1:senderId>
        <ns1:senderType>REGA</ns1:senderType>
      </ns1:businessDocumentHeader>
      <ns2:businessDocumentBody>
        <ns2:proposedName>TEST</ns2:proposedName>
        <ns2:bnNameAvailabilityCheck>true</ns2:bnNameAvailabilityCheck>
      </ns2:businessDocumentBody>
    </ns2:request>
   </env:Body>
  </env:Envelope>

Anyone got any ideas?

UPDATE 2: I had to install the latest version of SUDS to get this working. - Thanks all for your answers much appreciated.

Cheers, Ben

Upvotes: 4

Views: 4017

Answers (2)

jfs
jfs

Reputation: 414345

Update (after the PHP example output)

It is unclear why suds uses {uri:external.query.name.availability.asic.gov.au} instead of {uri:business.document.header.types.asic.gov.au} for businessDocumentHeader element.

The quick and dirty way to fix it is to use suds.plugin e.g.:

from suds.plugin import MessagePlugin

class NsHeaderPlugin(MessagePlugin):
    def sending(self, context):
        context.envelope = context.envelope.replace('ns1:businessDocumentHeader',
                                                    'ns0:businessDocumentHeader')

Or

class NsHeaderPlugin(MessagePlugin):
    def marshalled(self, context):
        hdr = context.envelope.childAtPath('Body/request/businessDocumentHeader')
        hdr.setPrefix('hdr', 'uri:business.document.header.types.asic.gov.au')

The 2nd argument should be ns3:requestDataType, not ns3:businessDocumentBody as you specified.

The general code flow:

from suds.client import Client # pip install suds

#XXX: change envelope namespace
from suds.bindings import binding
binding.envns = (binding.envns[0], 'http://www.w3.org/2003/05/soap-envelope')
del binding

# change content type
headers = {'Content-Type': 'application/soap+xml; charset="UTF-8"'}
client = Client(wsdl_url, headers=headers, plugins=[NsHeaderPlugin()])

header = client.factory.create('{uri:business.document.header.types.asic.gov.au}'
                               'businessDocumentHeaderType')
header.messageType = "queryNameAvailability"
header.messageReferenceNumber = 1
header.messageVersion = 2
header.senderId = 192
header.senderType = "REGA"

body = client.factory.create('{uri:external.query.name.availability.asic.gov.au}'
                             'requestDataType')
body.proposedName = 'TEST'
body.bnNameAvailabilityCheck = 'true' 

# make the call
result = client.service.externalQueryNameAvailability(header, body)
print result # for debugging, to find out what attributes are available

I don't see undefined namespaces so it seems ImportDoctor is not necessary in your case. But, for example, if there were xs:string type used and 'http://schemas.xmlsoap.org/soap/encoding/' is not mentioned then you could fix the wsdl schema:

from suds.xsd.doctor import Import, ImportDoctor

imp = Import('http://schemas.xmlsoap.org/soap/encoding/')
# add namespaces where the type is used (call `imp.filter.add` multiple times)
imp.filter.add("http://asic.gov.au/wsdl/name/availability/external") 
doctor = ImportDoctor(imp)

client = Client(wsdl_url, doctor=doctor)

Upvotes: 2

jathanism
jathanism

Reputation: 33716

The error pretty much tells you exactly where you need to look:

      <S:Text xml:lang="en">org.xml.sax.SAXParseException: cvc-complex-type.2.4.a: Invalid content was found starting with element 'ns1:businessDocumentHeader'. One of '{"uri:business.document.header.types.asic.gov.au":businessDocumentHeader}' is expected

ns1 references the URL http://www.w3.org/2005/05/xmlmime. If visit that in your browser, you can see that there isn't much to it. That further rules out that ns1 is correct.

Since ns1:businessDocumentHeader isn't even in the list of types determined by the WSDL, you might have to make use of the ImportDoctor. I've run into similar issue in the past on a personal project of mine where the WSDL didn't correctly import the types.

I'm sorry this response isn't conclusive, but it's very difficult determine the exact fix since I can't see your WSDL. Try tinkering with the docs for the ImportDoctor and see if that doesn't help.

Try starting with this:

from suds.xsd.doctor import Import, ImportDoctor
imp = Import("http://www.w3.org/2005/05/xmlmime")
imp.filter.add("uri:business.document.header.types.asic.gov.au")
doctor = ImportDoctor(imp)

And then pass doctor=doctor to the client constructor.

Upvotes: 0

Related Questions