I have a WS (ZOPE/PLONE) that accept some XMLRPC calls.
So, I write a python snippet of code for do a call to WS and do something.
I follow messagge format that I found here, and that's my snippet of code:
import httplib
def queryInventory():
try:
xmlrpc_envelope = '<?xml version="1.0"?>'\
'<methodCall>'\
'<methodName>easyram</methodName>'\
'<params>'\
'<param>'\
'<value>%s</value>'\
'</param>'\
'</params>'\
'</methodCall>'
params = '<EasyRAM>'\
'<authentication><user>EasyRAM</user><pwd>EasyRAM</pwd><hotel>52</hotel></authentication>'\
'<operation type="QueryInventory" rate="master"><date from="2012-03-10" to="2012-03-10" /><date from="2012-03-22" to="2012-03-22" /></operation>'\
'</EasyRAM>'
data = xmlrpc_envelope % params
print data
headers = {"Content-type": "text/xml"}
conn = httplib.HTTPSConnection('myHost')
aa = '/ws/xmlrpc/public/EasyRAM'
conn.request("POST", aa, data, headers)
response = conn.getresponse()
print "EasyRAM.queryInventory() response: status=%s, reason=%s" % (response.status, response.reason)
print "EasyRAM.queryInventory() response=%s" % response.read()
conn.close()
except Exception, ss:
print "EasyRAM.queryInventory() -> Error=%s" % ss
raise
return ''
queryInventory()
The problem is that i receive the following error message:
Invalid request The parameter, params , was omitted from the request. Make sure to specify all required parameters, and try the request again.
Like the parameter isn't passed.
If I modify my snippet by wrapping my parameter (called params) into <string></string> in that way:
xmlrpc_envelope = '<?xml version="1.0"?>'\
'<methodCall>'\
'<methodName>easyram</methodName>'\
'<params>'\
'<param>'\
'<value><string>%s</string></value>'\
'</param>'\
'</params>'\
'</methodCall>'
something happen, but isn't what I want; in fact my parameter result to be empty (or void, if you like).
Any ideas or suggestions?
PS.: I know that exists an xml-rpc library for python called xmlrpclib, but I have to develop in that way, because this is an example for client that can't use directly a library like that
I just resolved.
If I add a function like this:
def escape(s, replace=string.replace):
s = replace(s, "&", "&")
s = replace(s, "<", "<")
return replace(s, ">", ">",)
and before calling the connection method I do something like:
params = escape(params)
Then all goes well.
Hope that could be useful for future purposes
Related
I'm trying to rename a ArangoDB collection using pyArango. This is what I have so far:
connection = pyArango.Connection('http://random-address', username='random-username', password='random-password')
test_db = Database(connection, 'test-db')
collection = test_db["new"]
collection.action("PUT", "rename", name="newname")
The code fails in line 4:
{'error': True, 'code': 400, 'errorNum': 1208, 'errorMessage': 'name
must be non-empty'}
I'm probably using the action method incorrectly but the documentation does not provide any examples. Anybody got an idea?
A JSON object {"name": "newname"} needs to be passed as request body. The new name can not be passed as URL path parameter. The problem is the implementation of collection.action():
def action(self, method, action, **params) :
"a generic fct for interacting everything that doesn't have an assigned fct"
fct = getattr(self.connection.session, method.lower())
r = fct(self.URL + "/" + action, params = params)
return r.json()
The keyword arguments end up as dict called params. This object is passed to the request function fct() as named parameter params. This parameter receives the dict and converts it to URL path parameters, e.g. ?name=newname which is not supported by the HTTP API of the server.
There is unfortunately no way to pass a payload via action(). You can write some custom code however:
from pyArango.connection import *
connection = Connection('http://localhost:8529', username='root', password='')
try:
connection.createDatabase('test-db')
except CreationError:
pass
test_db = Database(connection, 'test-db')
try:
test_db.createCollection(name='new')
except CreationError:
pass
collection = test_db['new']
r = connection.session.put(collection.URL + '/rename', data='{"name":"newname"}')
print(r.text)
collection = test_db['newname']
You can also use a dict for the payload and transform it to JSON if you want:
import json
...put(..., data=json.dumps({"name": "newname"}))
I've fixed it like this:
def rename_collection(arango_uri, username, password, database, collection, new_name):
url = '{}/_db/{}/_api/collection/{}/rename'.format(arango_uri, database, collection)
params = {"name": new_name}
response = requests.put(url, data=json.dumps(params), auth=HTTPBasicAuth(username, password))
return response
this is a test script to request data from Rovi API, provided by the API itself.
test.py
import requests
import time
import hashlib
import urllib
class AllMusicGuide(object):
api_url = 'http://api.rovicorp.com/data/v1.1/descriptor/musicmoods'
key = 'my key'
secret = 'secret'
def _sig(self):
timestamp = int(time.time())
m = hashlib.md5()
m.update(self.key)
m.update(self.secret)
m.update(str(timestamp))
return m.hexdigest()
def get(self, resource, params=None):
"""Take a dict of params, and return what we get from the api"""
if not params:
params = {}
params = urllib.urlencode(params)
sig = self._sig()
url = "%s/%s?apikey=%s&sig=%s&%s" % (self.api_url, resource, self.key, sig, params)
resp = requests.get(url)
if resp.status_code != 200:
# THROW APPROPRIATE ERROR
print ('unknown err')
return resp.content
from another script I import the module:
from roviclient.test import AllMusicGuide
and create an instance of the class inside a mood function:
def mood():
test = AllMusicGuide()
print (test.get('[moodids=moodids]'))
according to documentation, the following is the syntax for requests:
descriptor/musicmoods?apikey=apikey&sig=sig [&moodids=moodids] [&format=format] [&country=country] [&language=language]
but running the script I get the following error:
unknown err
<h1>Gateway Timeout</h1>:
what is wrong?
"504, try once more. 502, it went through."
Your code is fine, this is a network issue. "Gateway Timeout" is a 504. The intermediate host handling your request was unable to complete it. It made its own request to another server on your behalf in order to handle yours, but this request took too long and timed out. Usually this is because of network congestion in the backend; if you try a few more times, does it sometimes work?
In any case, I would talk to your network administrator. There could be any number of reasons for this and they should be able to help fix it for you.
I'm trying to make a universal script in Python that can be used by anybody to import/export all sorts of information from/to Work Etc CRM platform. It has all the documentation here: http://admin.worketc.com/xml.
However, I am now a bit stuck. Authentication works, I can call different API methods, but only the ones without parameters. I am new to Python and that's why I can't figure out how to pass the parameters onto that specific method in the API. Specifically I need to export all time sheets. I'm trying to call this method specifically: http://admin.worketc.com/xml?op=GetDraftTimesheets. For obvious reasons I cannot disclose the login information so it might be a bit hard to test for you.
The code itself:
import xml.etree.ElementTree as ET
import urllib2
import sys
email = 'email#domain.co.uk'
password = 'pass'
#service = 'GetEmployee?EntityID=1658'
#service = 'GetEntryID?EntryID=23354'
#service = ['GetAllCurrenciesWebSafe']
#service = ['GetEntryID', 'EntryID=23354']
service = ['GetDraftTimesheets','2005-08-15T15:52:01+00:00','2014-08-15T15:52:01+00:00' ]
class workEtcUniversal():
sessionkey = None
def __init__(self,url):
if not "http://" in url and not "https://" in url:
url = "http://%s" % url
self.base_url = url
else:
self.base_url = url
def authenticate(self, user, password):
try:
loginurl = self.base_url + email + '&pass=' + password
req = urllib2.Request(loginurl)
response = urllib2.urlopen(req)
the_page = response.read()
root = ET.fromstring(the_page)
sessionkey = root[1].text
print 'Authentication successful!'
try:
f = self.service(sessionkey, service)
except RuntimeError:
print 'Did not perform function!'
except RuntimeError:
print 'Error logging in or calling the service method!'
def service(self, sessionkey, service):
try:
if len(service)<2:
retrieveurl = 'https://domain.worketc.com/xml/' + service[0] + '?VeetroSession=' + sessionkey
else:
retrieveurl = 'https://domain.worketc.com/xml/' + service[0,1,2] + '?VeetroSession=' + sessionkey
except TypeError as err:
print 'Type Error, which means arguments are wrong (or wrong implementation)'
print 'Quitting..'
sys.exit()
try:
responsefile = urllib2.urlopen(retrieveurl)
except urllib2.HTTPError as err:
if err.code == 500:
print 'Internal Server Error: Permission Denied or Object (Service) Does Not Exist'
print 'Quitting..'
sys.exit()
elif err.code == 404:
print 'Wrong URL!'
print 'Quitting..'
sys.exit()
else:
raise
try:
f = open("ExportFolder/worketcdata.xml",'wb')
for line in responsefile:
f.write(line)
f.close()
print 'File has been saved into: ExportFolder'
except (RuntimeError,UnboundLocalError):
print 'Could not write into the file'
client = workEtcUniversal('https://domain.worketc.com/xml/AuthenticateWebSafe?email=')
client.authenticate(email, password)
Writing a code Consuming API requires resolving few questions:
what methods on API are available (get their list with names)
how does a request to such method looks like (find out url, HTTP method to use, requirements to body if used, what headers are expected)
how to build up all the parts to make the request
What methods are available
http://admin.worketc.com/xml lists many of them
How does a request looks like
GetDraftTimesheet is described here http://admin.worketc.com/xml?op=GetDraftTimesheets
and it expects you to create following HTTP request:
POST /xml HTTP/1.1
Host: admin.worketc.com
Content-Type: text/xml; charset=utf-8
Content-Length: length
SOAPAction: "http://schema.veetro.com/GetDraftTimesheets"
<?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/">
<soap:Body>
<GetDraftTimesheets xmlns="http://schema.veetro.com">
<arg>
<FromUtc>dateTime</FromUtc>
<ToUtc>dateTime</ToUtc>
</arg>
</GetDraftTimesheets>
</soap:Body>
</soap:Envelope>
Building up the request
The biggest task it to build properly shaped XML document as shown above and having elements FromUtc and ToUtc filled with proper values. I guess, the values shall be in format of ISO datetime, this you shall find yourself.
You shall be able building such an XML by some Python library, I would use lxml.
Note, that the XML document is using namespaces, you have to handle them properly.
Making POST request with all the headers shall be easy. The library you use to make HTTP requests shall fill in properly Content-Length value, but this is mostly done automatically.
Veerto providing many alternative methods
E.g. for "http://admin.worketc.com/xml?op=FindArticlesWebSafe" there is set of different methods for the same service:
SOAP 1.1
SOAP 1.2
HTTP GET
HTTP POST
Depending on your preferences, pick the one which fits your needs.
The simplest is mostly HTTP GET.
For HTTP requests, I would recommend using requests, which are really easy to use, if you get through tutorial, you will understand what I mean.
I have written a Python script which POSTs data to an apache webserver and the data arrives nicely in $_POST and $_FILES aray. Now I want to implement this same thing in Lua but I can't get it going yet.
My code in Python looks something like this:
try:
wakeup()
socket.setdefaulttimeout(TIMEOUT)
opener = urllib2.build_opener(MultipartPostHandler.MultipartPostHandler)
host = HOST
func = "post_img"
url = "http://{0}{1}?f={2}&nodemac={3}&time={4}".format(host, URI, func, nodemac, timestamp)
if os.path.isfile(filename):
data = {"data":open(filename,"rb")}
print "POST time "+str(time.time())
response = opener.open(url, data, timeout=TIMEOUT)
retval = response.read()
if "SUCCESS" in retval:
return 0
else:
print "RETVAL: "+retval
return 99
except Exception as e:
print "EXCEPTION time "+str(time.time())+" - "+str(e)
return 99
The Lua code I have come up with thus far:
#! /usr/bin/lua
http = require("socket.http")
ltn12 = require("ltn12")
http.request{
url = "localhost/test.php?test=SEMIOS",
method = "POST",
headers = {
["Content-Type"] = "multipart/form-data; boundary=127.0.1.1.1000.17560.1375897994.242.1",
["Content-Length"] = 7333
},
source = ltn12.source.file(io.open("test.gif")),
sink = ltn12.sink.table(response_body)
}
print(response_body[1]) --response to request
but this code keeps getting me this on execution:
$ ./post.lua
/usr/bin/lua: ./post.lua:17: attempt to index global 'response_body' (a nil value)
stack traceback:
./post.lua:17: in main chunk
[C]: ?
reg#DesktopOffice:~$
There are several examples of sending POST data using Lua: from the author of luasocket and SO. This example works directly with files, which is very close to what you are using.
Your description of this question doesn't match the comment you provided.
I have looked and perhaps i missed it. I currently have a file such as the one below:
PUT /URL/TO/SEND/REQUEST
Host: 127.0.0.1
Connection: keep-alive
...
bunch of data here
This file contains the header & the data i want to send over ssl. I know on windows i can use fiddler etc.. to send this raw data BUT i was hoping to use python. I tried looking (may be not hard enough) at urllib2 urllib & httplib to see if i could just send this file as the entire request i don't want to deal with parsing the file etc... Is this possible?
I did notice that in httplib i can use request where "body can be a file object." but from the description seems as though it still sends the header seperately and that file is only for the data being sent.
Thanks
It isn't documented, but it looks like you should be able to use httplib.HTTPConnection.send() for this:
In [13]: httplib.HTTPConnection.send??
Type: instancemethod
String Form:<unbound method HTTPConnection.send>
File: /usr/local/lib/python2.7/httplib.py
Definition: httplib.HTTPConnection.send(self, data)
Source:
def send(self, data):
"""Send `data' to the server."""
if self.sock is None:
if self.auto_open:
self.connect()
else:
raise NotConnected()
if self.debuglevel > 0:
print "send:", repr(data)
blocksize = 8192
if hasattr(data,'read') and not isinstance(data, array):
if self.debuglevel > 0: print "sendIng a read()able"
datablock = data.read(blocksize)
while datablock:
self.sock.sendall(datablock)
datablock = data.read(blocksize)
else:
self.sock.sendall(data)
The request() method combines the header and body and passes it to this function, which looks like it should handle strings or file objects.
Of course you will still need to know the host so that you can create the HTTPConnection object, so your code might look something like this (untested):
import httplib
conn = httplib.HTTPConnection('127.0.0.1')
conn.send(open(filename))
response = conn.getresponse()
edit: It turns out there is some internal state stuff that keeps this from working as is, here is a workaround (full example with google main page), but it is a bit of a hack. Tested using Python 2.6 and 2.7, does not appear to work on 3.x by just replacing httplib with http.client:
import httplib
conn = httplib.HTTPConnection('www.google.com')
conn.send('GET / HTTP/1.1\r\nHost: www.google.com\r\n\r\n')
conn._HTTPConnection__state = httplib._CS_REQ_SENT
response = conn.getresponse()
The key part here is setting conn.__state (mangled name) to the httplib._CS_REQ_SENT after calling send().