Python: Making a request with suds - python

i'm testing out SUDS library and I'm trying to make a simple request to an endpoint but i get unusual output. Why?
from suds.client import Client
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG)
url = "http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/Gateway"
client = Client(url)
print client
Output:
Martynass-MacBook-Air:CH martynas$ python ch.py
DEBUG:suds.xsd.schema:loaded:
schema collection
Schema:0x109a7db90
(raw)
<schema/>
(model)
DEBUG:suds.xsd.schema:MERGED:
Schema:0x109a7db90
(raw)
<schema/>
(model)

You can't use suds for this servce, suds is based on SOAP, which is another web service protocol. What you can do is send an xml request and get a response.
import requests
target_url = "http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/Gateway"
headers={'Content-type': 'text/xml'}
print requests.post(target_url, data=xml, headers=headers).text
Where the xml is defined according to their schemes.
http://xmlgw.companieshouse.gov.uk/example_http.html
This is one examaple
xml = ('''
<GovTalkMessage xmlns="http://www.govtalk.gov.uk/schemas/govtalk/govtalkheader"
xmlns:dsig="http://www.w3.org/2000/09/xmldsig#"
xmlns:gt="http://www.govtalk.gov.uk/schemas/govtalk/core"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.govtalk.gov.uk/schemas/govtalk/govtalkheader">
<EnvelopeVersion>1.0</EnvelopeVersion>
<Header>
<MessageDetails>
<Class>CompanyDetails</Class>
<Qualifier>request</Qualifier>
<TransactionID>14456553</TransactionID>
</MessageDetails>
<SenderDetails>
<IDAuthentication>
<SenderID>My_SenderID</SenderID>
<Authentication>
<Method>CHMD5</Method>
<Value>e999e113407884fa410fa2f53bc23952</Value>
</Authentication>
</IDAuthentication>
<EmailAddress>sometest#some.email.address</EmailAddress>
</SenderDetails>
</Header>
<GovTalkDetails>
<Keys/>
</GovTalkDetails>
<Body>
<CompanyDetailsRequest xmlns="http://xmlgw.companieshouse.gov.uk/v1-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlgw.companieshouse.gov.uk/v1-0/schema/CoDets.xsd">
<CompanyNumber>01002361</CompanyNumber>
<GiveMortTotals>1</GiveMortTotals>
</CompanyDetailsRequest>
</Body>
</GovTalkMessage>
''')
<Class>CompanyDetails</Class> What type of info you're getting. kinda what "function" to call
<Authentication>
<Method>CHMD5</Method>
<Value>e999e113407884fa410fa2f53bc23952</Value>
</Authentication>
</IDAuthentication>
Here you would put the login info i guess
<CompanyDetailsRequest xmlns="http://xmlgw.companieshouse.gov.uk/v1-0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlgw.companieshouse.gov.uk/v1-0/schema/CoDets.xsd">
<CompanyNumber>01002361</CompanyNumber>
<GiveMortTotals>1</GiveMortTotals>
</CompanyDetailsRequest>
The "function" call and it's parameters
Now this will give me a response telling me the authorization failed. So if you have an account there, this should work for you.
Here you can find the list of schemes they have for different types of request. Some of them have sample request to help you out.
http://xmlgw.companieshouse.gov.uk/v1-0/xmlgw/SchemaStatusOutput
Here is the complete guide of all their schemes.
http://xmlgw.companieshouse.gov.uk/data_usage_guide_dec_2013.pdf

There aren't any wsdl definitions for that site. Try something like
http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL
for your url
then you can try something like
client.service.GetWeatherInformation()
From the suds document, "You will need to know the url for WSDL for each service used."
An explicit example
from suds.client import Client
import logging
logging.basicConfig(level=logging.INFO)
logging.getLogger('suds.xsd.schema').setLevel(logging.DEBUG)
url = " http://wsf.cdyne.com/WeatherWS/Weather.asmx?WSDL"
client = Client(url)
client.service.GetWeatherInformation()
Outputs a ton of data.

Suds does not make it easy to discover the service, it's better to first test a bit with soapui or generate a human-readable doc of the wsdl with this xslt : http://code.google.com/p/wsdl-viewer/ . So you know the structure of requests and replies, and which services are available.
Requests and responses in soap are xml trees, so once you get the result, you need to access the content of the xml tag that contains the information you're interested in. Here is an example that should work ( I don't have a username, but the result.Status.Success works ).
import suds
client = suds.client.Client("http://webservices.data-8.co.uk/companieshouse.asmx?WSDL")
result = client.service.GetCompanyDetails("username", "password", 1234)
print result.Status.Success
print result.Result.CompanyName

You can not make a request against .xsd. XSD is definition of the exchanged message. You must make a request against webservice Looking here you can find more info about that web service. But also there is pricing page indicating that you must pay to use their service. Probably when you pay you will get username and password to authenticate with the service.

Related

Python Sud's custom soap header where one complex element has no namespace?

The API I am working with requires a custom defined api_key in the soap header. Successful authentication with the following using Sud's Custom Soap Headers docs:
# create auth soap_header
api_key = "abc123"
auth_ns = ('apiKey', 'https://api.redacted.net/2.0/')
api_key = Element('api_key', ns=auth_ns).setText(api_key)
self.client.set_options(soapheaders=api_key)
The API also uses a pager for multiple records defined in the soap header. I am having issues getting it to work in parallel with authentication. Note that the pager and auth are not defined in the WSDL. Also, the pager does not have a namespace (ns) like the auth does.
Documentation Provided:
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:ns="https://api.redacted.net/2.0/">
<soapenv:Header>
<ns:apiKey>
<api_key>***</api_key>
</ns:apiKey>
<pager><page>2</page><per_page>100</per_page></pager>
</soapenv:Header>
<soapenv:Body>
<ns:getCaseUpdates>
<case_id>2038217180</case_id>
</ns:getCaseUpdates>
</soapenv:Body>
</soapenv:Envelope>
This however does not work when including the pager:
# create auth soap_header
api_key = "abc123"
auth_ns = ('apiKey', 'https://api.redacted.net/2.0/')
auth = Element('api_key', ns=auth_ns).setText(CFG.get(f"{self.config_prefix}KEY"))
#self.client.set_options(soapheaders=ns)
# create pager
pager_ns = ('pager') # no namespace, see spec above
page = Element('page', ns=pager_ns).setText(2)
per_page = Element('per_page', ns=pager_ns).setText(50)
# set client options with headers
self.client.set_options(soapheaders=(auth, (page, per_page)))
Providing an empty string or None from pager_ns namespace also fails.
If I could get this to work for an api call, I could abstract the pager out for use only when needed. I have reviewed other SO questions but they do not cover the issue of the missing namespace. The wsdl cannot be contractually provided. Any help would be appreciated. Thank you.

zeep soap12 wsdl+mtom+wsse how to make request?

TPA.wsdl https://pastebin.com/7DBhCHbv
DataService.xsd https://pastebin.com/AFhg64hH
from zeep import Client
import base64
from requests import Session
from zeep.wsse.username import UsernameToken
from zeep.transports import Transport
from zeep.exceptions import Fault
Username = '....'
Password = '....'
sendFile = 'V07_220110.ffdata'
session = Session()
session.verify = False
try:
wsdl = 'TPA.wsdl'
# initialize zeep client
client = Client(
wsdl=wsdl,
wsse=UsernameToken(Username, Password),
transport=Transport(session=session)
)
with open(sendFile, "rb") as pdf_file:
encoded_string = base64.b64encode(pdf_file.read())
with client.options(raw_response=True):
node = client.service.uploadEdasDraft(sendFile, encoded_string )
print(node.content)
except Fault as fault:
parsed_fault_detail = client.wsdl.types.deserialize(fault.detail[0])
Always getting Response
I got error ORA-31011: XML parsing failed
From SOAPUI everything sending ok with Enable MTOM settings
So how to make request to it, and how debug sending requests?
Based on that WSDL file, a code like this:
from zeep import Client
from zeep.wsse.username import UsernameToken
username = 'username'
password = 'p#$$word'
file_name = 'test.txt'
client = Client(wsdl = 'TPA.wsdl',
wsse = UsernameToken(username, password))
with open(file_name, "rb") as f:
content = f.read()
client.service.uploadEdasDraft(file_name, content)
should produce something like this:
<soap-env:Envelope xmlns:soap-env="http://www.w3.org/2003/05/soap-envelope">
<soap-env:Header>
<wsse:Security xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd">
<wsse:UsernameToken>
<wsse:Username>username</wsse:Username>
<wsse:Password Type="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-username-token-profile-1.0#PasswordText">p#$$word</wsse:Password>
</wsse:UsernameToken>
</wsse:Security>
</soap-env:Header>
<soap-env:Body>
<ns0:uploadEdasDraft xmlns:ns0="http://types.data.external.ws.edas.sodra.epr.lt">
<fileName>test.txt</fileName>
<document>dGhpcyBpcyBhIHRlc3QNCnRoaXMgaXMgYSB0ZXN0DQp0aGlzIGlzIGEgdGVzdA0KdGhpcyBpcyBhIHRlc3QNCg==</document>
</ns0:uploadEdasDraft>
</soap-env:Body>
</soap-env:Envelope>
Since the document type is marked as xsd:base64Binary zeep should handle the base64 encoding for you (your code seems to be doing the encoding twice).
In the example above I'm using a text file, but I assume your file named V07_220110.ffdata is an XML file, since that's what this attribute says: xmime:expectedContentTypes="application/xml". Server will probably complain if you don't send a file with this content type. This may also be a possible cause for that "ORA-31011: XML parsing failed" message, together with the double encoding (server is expecting XML in the document but finds another base64 string).
From SOAPUI everything sending ok with Enable MTOM settings
When using MTOM, your file is not encoded within the SOAP message as text, but it's attached as binary next to it, and you get references to that part of the message. See an explanation here: How does MTOM work?
Your document element might change to something like:
<document>
<inc:Include href="cid:123456789" xmlns:inc="http://www.w3.org/2004/08/xop/include"/>
</document>
where 123456789 is a reference to the content id in the multipart message.
Does your call work from SoapUI only if you enable MTOM? Have you tried sending a base64 encoded file string within the SOAP message using SoapUI?
If your call only works with MTOM then you have a problem, because I'm not sure zeep can handle it out of the box.
The documentation (https://docs.python-zeep.org/en/master/attachments.html) mentions only a multipart response and how you can read the file from the response, but says nothing about making requests. See for example these items:
https://github.com/mvantellingen/python-zeep/issues/599
https://github.com/mvantellingen/python-zeep/pull/314
https://github.com/remaudcorentin-dev/python-zeep-adv
The last project might help you with some code samples (https://github.com/remaudcorentin-dev/python-zeep-adv/blob/master/src/zeep/transport_with_attach.py) but do consider the caveat:
This has been developed for a specific usage and this code should probably not be used (has it) for other puposes.
(or at your own risks ;) )
So it seems you might have to build your own multipart request from scratch or from that sample in the project.
[...] and how debug sending requests?
You might use the HistoryPlugin just to see what messages get exchanged between your client and server, but since you might need to see all of the request, I suggest Wireshark, TcpMon (old, defunct, but still useful), or SoapUI with TcpMon.
This might not be the answer you are looking for, but hope that at least it leaves you more equipped in figuring out how to make the call. Zeep unfortunately is a small fish client in a large pond of WS specifications.

I need to create a python script which calls SOAP GET method using ZEEP or REQUEST modules

I am new to python. I need to fetch data from Oracle fusion cloud. I want to run a BI publisher report on Oracle fusion cloud instance using SOAP API call and get the data into CSV file.
I have tried with python ZEEP and REQUESTS modules but I am not getting expected results.
For example:
My WSDL: https://xxx.yy.us6.oraclecloud.com/xmlpserver/services/ExternalReportWSSService?wsdl
The operation I need to use is from above WSDL is 'runReport'
When I am running this request from SOAP UI for 'runReport' operation I am getting expected result like below:
This screenshot is from SOAP UI where I am getting encoded data which is expected
I am using below code in python (Python 3.5) to call this API. I have used REQUESTS and ZEEP both:
1. REQUESTS Module:
from requests.auth import HTTPBasicAuth
from xml.etree import ElementTree
url="https://xxxx.yyyy.us6.oraclecloud.com/xmlpserver/services/ExternalReportWSSService?wsdl"
#headers = {'content-type': 'application/soap+xml'}
headers = {'content-type': 'text/xml'}
body = """<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:pub="http://xmlns.oracle.com/oxp/service/PublicReportService">
<soap:Header/>
<soap:Body>
<pub:runReport>
<pub:reportRequest>
<pub:attributeFormat>csv</pub:attributeFormat>
<!-- Flatten XML should always be false when we have XML type of output to display the XML tags as mentioned in BIP Data Model and display XML structure in as expected format -->
<pub:flattenXML>false</pub:flattenXML>
<pub:parameterNameValues>
<!--1st Parameter of BIP Report-->
<pub:item>
<pub:name>p_name</pub:name>
<pub:values>
<pub:item>tapan</pub:item>
</pub:values>
</pub:item>
<!--2nd Parameter of BIP Report-->
<!--<pub:item>
<pub:name>p_to_date</pub:name>
<pub:values>
<pub:item>10-15-2019</pub:item>
</pub:values>
</pub:item>-->
</pub:parameterNameValues>
<pub:reportAbsolutePath>/Custom/Integration/test_data_rpt.xdo</pub:reportAbsolutePath>
<!-- Setting sizeOfDataChunkDownload to -1 will return the output to the calling client -->
<pub:sizeOfDataChunkDownload>-1</pub:sizeOfDataChunkDownload>
</pub:reportRequest>
</pub:runReport>
</soap:Body>
</soap:Envelope>"""
response = requests.get(url,data=body,headers=headers,auth=HTTPBasicAuth('XXXX', 'XXXX'))
print (response.text)
Above code is just giving me the list of operations available in the WSDL
2. ZEEP Module
from zeep import Client
from requests import Session
from requests.auth import HTTPBasicAuth
from zeep.transports import Transport
wsdl = "https://XXXX.XXXX.us6.oraclecloud.com/xmlpserver/services/ExternalReportWSSService?wsdl"
session = Session()
session.auth = HTTPBasicAuth('XXXX', 'XXXX')
#An additional argument 'transport' is passed with the authentication details
client = Client(wsdl, transport=Transport(session=session))
request_payload= """<soap:Envelope xmlns:soap="http://www.w3.org/2003/05/soap-envelope" xmlns:pub="http://xmlns.oracle.com/oxp/service/PublicReportService">
<soap:Header/>
<soap:Body>
<pub:runReport>
<pub:reportRequest>
<pub:attributeFormat>csv</pub:attributeFormat>
<!-- Flatten XML should always be false when we have XML type of output to display the XML tags as mentioned in BIP Data Model and display XML structure in as expected format -->
<pub:flattenXML>false</pub:flattenXML>
<pub:parameterNameValues>
<!--1st Parameter of BIP Report-->
<pub:item>
<pub:name>p_name</pub:name>
<pub:values>
<pub:item>tapan</pub:item>
</pub:values>
</pub:item>
<!--2nd Parameter of BIP Report-->
<!--<pub:item>
<pub:name>p_to_date</pub:name>
<pub:values>
<pub:item>10-15-2019</pub:item>
</pub:values>
</pub:item>-->
</pub:parameterNameValues>
<pub:reportAbsolutePath>/Custom/Integration/test_data_rpt.xdo</pub:reportAbsolutePath>
<!-- Setting sizeOfDataChunkDownload to -1 will return the output to the calling client -->
<pub:sizeOfDataChunkDownload>-1</pub:sizeOfDataChunkDownload>
</pub:reportRequest>
</pub:runReport>
</soap:Body>
</soap:Envelope>"""
response = client.service.runReport(request_payload)
#Here 'request_data' is the request parameter dictionary.
#Assuming that the operation named 'runReport' is defined in the passed wsdl.
Above code is not working because I am not sure how to pass the request payload using ZEEP module.
Please help me!!
I've used the requests module to dynamically schedule out reports from our Oracle Fusion cloud instance to UCM (slightly different than your request) but noticed the following differences in the content type distinction in the header and the method used for the response:
headers = {
"content-type" : "application/soap+xml"
}
response = requests.post(url, data=body, headers=headers)

Receive and process a SOAP message

Got a service provider (Safaricom) that has decided to use SOAP to send mobile money payment notifications to businesses. When the mobile user pays (either through USSD or via a web interface) the mobile money service will send a SOAP message that we are supposed to consume.
<soapenv:Envelope xmlns:soapenv="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:c2b="http://cps.huawei.com/cpsinterface/c2bpayment">
<soapenv:Header/>
<soapenv:Body>
<c2b:C2BPaymentValidationRequest>
<TransactionType>PayBill</TransactionType>
<TransID>1234560000007031</TransID>
<TransTime>20140227082020</TransTime>
<TransAmount>123.00</TransAmount>
<BusinessShortCode>12345</BusinessShortCode>
<BillRefNumber></BillRefNumber>
<InvoiceNumber></InvoiceNumber>
<MSISDN>254722703614</MSISDN>
<KYCInfo>
<KYCName>[Personal Details][First Name]</KYCName>
<KYCValue>Hoiyor</KYCValue>
</KYCInfo>
<KYCInfo>
<KYCName>[Personal Details][Middle Name]</KYCName>
<KYCValue>G</KYCValue>
</KYCInfo>
<KYCInfo>
<KYCName>[Personal Details][Last Name]</KYCName>
<KYCValue>Chen</KYCValue>
</KYCInfo>
</c2b:C2BPaymentValidationRequest>
</soapenv:Body> </soapenv:Envelope>
Don't worry the above details are public information
Question is, using a framework like bottle (or even Django) how do I "accept" this message and how do I extract the details from within the message.
I've used suds-jurko to consume Soap Services but I've never been on the receiving end of a SOAP call.
At minimum though am able to get the message using payment_data = request.body.read()
from bottle import request
payment_data = request.body.read()
print(payment_data)
From there though I've tried using XML parsers in python but its getting complicated. Is there a way for suds (or zeep) to allow me to get the data from the xml object?
I hope it's not late for an answer:
For the C2B transactions there is a project on github https://github.com/kn9ts/project-mulla it takes the request from the checkout in POST form, converts it to a SOAP request, send it to safaricom, receives the response from safaricom and gives a response in Json format.

SOAP client in python, how to replicate with XML

I am using suds to send XML and I got my request working, but I'm really confused by how to replicate my results using XML. I have the XML request that my suds client is sending by using:
from suds.client import Client
ulr = "xxxxxxx"
client = Client(url)
...
client.last_received.str()
but I'm not sure where I would send that request to if I was using the requests library. How would I replicate the request from the suds client in a python request?
Most SOAP APIs are just over plain HTTP, use POST - and therefore are easily mimicked with any standard HTTP client such as Requests.
First look here to see how to view the headers and body that suds is sending - it is then a matter of replicating these headers/XML body and passing them into the Requests library.
One defining characteristic in 99% of all HTTP SOAP API's is that your request is going to the same end-point for each request (for example 'http://yyy.com:8080/Posting/LoadPosting.svc), and the actual action is specified in the header using SOAPAction header). Contrast this to a RESTful API where the action is implied with the verb + end-point you call (POST /user, GET /menu etc.)

Categories

Resources