I'm trying to connect via json-rpc to the trytond isntance using this class
class HttpClient:
"""HTTP Client to make JSON RPC requests to Tryton server.
User is logged in when an object is created.
"""
def __init__(self, url, database_name, user, passwd):
self._url = '{}/{}/'.format(url, database_name)
self._user = user
self._passwd = passwd
self._login()
self._id = 0
def get_id(self):
self._id += 1
return self._id
def _login(self):
"""
Returns list, where
0 - user id
1 - session token
"""
payload = json.dumps({
'params': [self._user, {'password': self._passwd}],
'jsonrpc': "2.0",
'method': 'common.db.login',
'id': 1,
})
headers = {'content-type': 'application/json'}
result = requests.post(self._url, data=payload, headers=headers)
if result.status_code in [500, 401]:
raise Exception(result.text)
if 'json' in result:
self._session = result.json()['result']
else:
self._session = json.loads(result.text)
return self._session
def call(self, prefix, method, params=[[], {}]):
"""RPC Call
"""
method = '{}.{}'.format(prefix, method)
payload = json.dumps({
'params': params,
'method': method,
'id': self.get_id(),
})
authorization = base64.b64encode(self._session[1].encode('utf-8'))
headers = {
'Content-Type': 'application/json',
'Authorization': b'Session ' + authorization
}
response = requests.post(self._url, data=payload, headers=headers)
if response.status_code in [500, 401]:
raise Exception(response.text)
return response.json()
def model(self, model, method, args=[], kwargs={}):
return self.call('model.%s' % model, method, [args, kwargs])
def system(self, method):
return self.call('system', method, params=[])
and I call it in this way
def notifieToMainServer(self):
url = "http://%s:%s" % (HOST, PORT)
headers = {'content-type': 'application/json'}
client = HttpClient(url, "tryton", CREDENTIALS[0], CREDENTIALS[1])
client.model('main.register',
'ActivateService',
{'code': self.code,
'name': self.nome,
'surname': self.cognome,
'service_type': 'registry',
'service_url': '' # qui andra messa l'url di chimata
}
)
the creatin of the HttpClient works well and I'm able to login, but when I try to call the method ActivateService I recive a 401 responce.
I also add the ActivateService into the rpc class
#classmethod
def __setup__(cls):
super(MainRegister, cls).__setup__()
cls.__rpc__.update({
'ActivateService': RPC(instantiate=0)})
and the function is like
def ActivateService(self,
code,
name,
surname,
service_type,
service_url):
"""
activate the service for the given code and the given service
"""
what I did wrong ?
best regards,
Matteo
The Authorization header should be:
'Authorization': : b'Bearer ' + authorization
At the and I finally solve the problem with this modifies class
class HttpClient:
"""HTTP Client to make JSON RPC requests to Tryton server.
User is logged in when an object is created.
"""
def __init__(self, url, database_name, user, passwd):
self._url = '{}/{}/'.format(url, database_name)
self._user = user
self._passwd = passwd
self._login()
self._id = 0
def get_id(self):
self._id += 1
return self._id
def _login(self):
"""
Returns list, where
0 - user id
1 - session token
"""
payload = json.dumps({
'params': [self._user, {'password': self._passwd}],
'jsonrpc': "2.0",
'method': 'common.db.login',
'id': 1,
})
headers = {'content-type': 'application/json'}
result = requests.post(self._url,
data=payload,
headers=headers)
if result.status_code in [500, 401]:
raise Exception(result.text)
if 'json' in result:
self._session = result.json()['result']
else:
self._session = json.loads(result.text)
return self._session
def call(self, prefix, method, params=[[], {}, {}]):
"""RPC Call
"""
method = '{}.{}'.format(prefix, method)
payload = json.dumps({
'params': params,
'method': method,
'id': self.get_id(),
})
auth = "%s:%s:%s" % (self._user, self._session[0], self._session[1])
authorization = base64.b64encode(auth.encode('utf-8'))
headers = {
'Content-Type': 'application/json',
'Authorization': b'Session ' + authorization
}
response = requests.post(self._url,
data=payload,
headers=headers)
if response.status_code in [500, 401]:
raise Exception(response.text)
return response.json()
def model(self, model, method, args=[], kwargs={}):
return self.call('model.%s' % model, method, (args, kwargs, {}))
def system(self, method):
return self.call('system', method, params=[])
the key point is the call that must be done with the context in order to be executed in the right way !!
def model(self, model, method, args=[], kwargs={}):
return self.call('model.%s' % model, method, (args, kwargs, {}))
hope it helps
regards
Related
I'm trying to figure out how to mock my request for a bearer token in python.
I have a class:
class grab_apitokens(object):
def __init__(self, consumer_key, first_api_url, second_api_user, second_api_password, second_api_url):
self.consumer_key = consumer_key
self.second_api_user = second_api_user
self.second_api_password = second_api_password
self.first_api_url = first_api_url
self.second_api_url = second_api_url
def logintofirstsite(self):
b64val = base64.b64encode(self.consumer_key.encode()).decode()
headers = {"Authorization": "Basic %s" % b64val}
data = {'grant_type': 'client_credentials', 'validity_period': '3600'}
try:
response = requests.post(self.first_api_url, headers=headers, data=data)
decodedresponse = json.loads(response.content.decode())
access_token = decodedresponse['access_token']
return access_token
except:
return None
def logintosecondsite(self):
header = {"accept": "application/json", "Content-Type": "application/x-www-form-urlencoded"}
logindata = {'grant_type': 'password',
'username': "" + self.second_api_user + "", 'password': "" + self.second_api_password + ""
}
try:
returnedfromsite = requests.post(self.second_api_url + '/api/V1/token',
headers=header, data=logindata)
return returnedfromsite.json()['access_token']
except:
return None
What I can't figure out is how to mock that requests call and what it would look like in Python.
My test currently looks like:
class MyTestCase(unittest.TestCase):
def setUp(self) -> None: # PROBLEM BEGINS HERE
self.grab_apitokens = grab_apitokens(actual_key, actual_site1, actual_user, actual_pass, actual_2nd_site)
#patch('grab_apitokens.requests.get')
def test_login(self, mock_get):
mock_get.return_value.ok = True
response = self.grab_apitokens.logintosite()
assert_is_not_none(response)
# self.assertEqual(True, False)
if __name__ == '__main__':
unittest.main()
How would I mock the requests.post functionality?
With the help of a good mentor I figured out that my approach was all wrong. Here's what I ended up with for the unit test:
class MyTestCase(unittest.TestCase):
def setUp(self) -> None:
self.grab_apitokens = grab_apitokens("complete","gibberish","it really doesnt","matter","what is","in","here")
#patch('grab_apitokens.requests.posts')
def test_login(self, mock_get):
mock_json = {'token': 'foo'}
mock_get.return_value = Mock(ok=True)
mock_get.return_value.json.return_value = mock_json
mock_get.return_value.content = b'{"token": "foo"}'
response = self.grab_apitokens.logintofirstsite()
assert_equal(response, "foo")
if __name__ == '__main_
To understand what this does, I needed to know that what we're really mocking isn't the method logintofirstsite(), we're mocking the response that requests.post is making in the method. With mock_get, we're inject requests.posts to always be: {'token': 'foo'} . So everything after
response = requests.post(self.first_api_url, headers=headers, data=data)
in logintofirstsite() is what I'm really testing. Which is:
decodedresponse = json.loads(response.content.decode())
access_token = decodedresponse['token']
return access_token
The setup before the requests.post call doesn't matter one bit. Since with {'token': 'foo'} is what my requests.post call returns, the returned value after that bit of logic is 'foo', and so the assert_equal back in MyTestCase passes.
Currently my API requires to use Token authentication.
POST /api/authorize HTTP/1.1
"version": "20151130", // The version of the REST API you are using
"client_id": "01234567890123456789", // Your 20 char public client id
"client_secret": "0123456789..." // Your 40 char client secret
I get the response:
{
"auth_token": "abcdef...",
"auth_expires": "20180524T062442Z"
}
My current pattern is like this:
I have a list of items I need to pass to the API via POST method.
This is my main function: ProcessProducts which receives a Pandas Dataframe which includes a product per row.
def ProcessProducts(products):
all_results = []
for _, product in products.iterrows():
results = GetProductData(product)
if results:
all_results.extend(results)
return all_results
def GetAuthorizationToken():
payload = {
'version': api_version,
'client_id': api_client_id,
'client_secret': api_client_secret
}
request_url = '%s%s' % (api_host, '/api/authorize')
r = requests.post(request_url, data=payload)
if r.status_code != 200:
raise Exception('Failed to authorize: ' + r.text)
token_data = json.loads(r.text)
api_auth_token = token_data['auth_token']
api_auth_expires = token_data['auth_expires']
return {
"X-API-Version": api_version,
"Authorization": "token %s" % api_auth_token
}
Client function...
def GetProductData(self, product):
"""Gets Product information from API."""
url = '%s%s' % (self.api_url, _COMPANY_DATA)
request = json.dumps({'products': [product]})
form_data = {'request': request, 'start': 1, 'limit': 1000}
logging.info('Looking up: %s', url)
auth_headers = GetAuthorizationToken()
response = _SendApiRequest(url, auth_headers, form_data)
return _HandleResponse(response)
def _SendApiRequest(self, url, auth_headers, form_data):
session = requests.Session()
try:
response = session.post(
url,
timeout=(_CONNECT_TIMEOUT_SECONDS, _READ_TIMEOUT_SECONDS),
headers=auth_headers,
data=form_data,
verify=True) # Check for valid public/signed HTTPS certificate.
response.raise_for_status()
return response
except requests.exceptions.HTTPError as err:
logging.exception(err)
Questions:
API returns the code expiry field "auth_expires", Where may be the best way to check in code when token expires so I can request a new one?
Is there a better Pattern to call API, so I can control the QPS rate as well (Use RateLimiter). Right now I'm creating a Session per request, which may not be ideal.
Im trying to write a REST Client for a closed API. I call a specific function twice but it only works the first time. It's the same command. If i run the script twice with batch It works. Im suspecting Requests keeps the connection alive or caches data. How do i "reset" Requests ?
base_headers = {"Connection":"keep-alive",
"User-Agent":user_agent,
"Accept-Encoding":"gzip",
"Host":"xxxxxxxxxxx",
"Content-Type":"application/json; charset=UTF-8"}
global auth
auth = 'xxxxxx'
def create_header(doAuth = True):
headers = base_headers
if doAuth:
headers['Authorization'] = auth
return headers
def do_get(url):
return requests.get(url, headers=create_header());
def do_put(url, payload):
if payload is None:
return requests.put(url, headers=create_header())
return requests.put(url, data=payload, headers=create_header())
def upvote(postid):
return do_put("xxxxxxxxxxxxx" % postid, None).content
def set_pos(longtitude, latitude, place, country="DE"):
payload = {'xxxxx':'xxxx'}
json_payload = json.dumps(payload)
return do_put("xxxxxxxxxxxxxxx",json_payload).content
def do_post(url, payload, doAuth=True):
return requests.post(url, data=payload, headers=create_header(doAuth=doAuth))
def new_acc(place, latitude, longtitude):
access = get_access_token(place, latitude, longtitude)
print access
global auth
auth = "Bearer %s" % access['access_token']
set_pos(longtitude, latitude, place) # Does a POST
for i in range(1,30):
posts = new_acc(uni['city'], uni['latitude'], uni['longtitude'])
upvote('xxxxxxxxxxxxx')
Basically the first upvote() goes through every time but every succeding does not.
I'm almost positive that keep-alive is not doing this. I would suggest writing some handlers to debug the response if you don't get the expected response code after the request.
Also, might I suggest a slightly different design that avoids global?
class RESTClient(object):
BASE_HEADERS = {"Connection":"keep-alive",
"Accept-Encoding":"gzip",
"Host":"xxxxxxxxxxx",
"Content-Type":"application/json; charset=UTF-8"}
def __init__(self, user_agent, auth = None):
self.headers = dict(self.BASE_HEADERS)
self.headers['User-Agent'] = user_agent
self.auth = auth
def create_header(self):
headers = dict(self.headers)
if auth:
headers['Authorization'] = auth
return headers
def do_get(self, url):
return requests.get(url, headers=self.create_header());
def do_put(self, url, payload = None):
if not payload:
return requests.put(url, headers=self.create_header())
return requests.put(url, data=payload, headers=self.create_header())
def upvote(self, postid):
return do_put("xxxxxxxxxxxxx" % postid).content
def set_pos(self, longtitude, latitude, place, country="DE"):
payload = {'xxxxx':'xxxx'}
json_payload = json.dumps(payload)
return do_put("xxxxxxxxxxxxxxx",json_payload).content
def do_post(self, url, payload, do_auth = None):
return requests.post(url, data=payload, headers=self.create_header())
def new_acc(self, place, latitude, longtitude):
access = get_access_token(place, latitude, longtitude)
print access
self.auth = "Bearer %s" % access['access_token']
set_pos(longtitude, latitude, place)
rc = RESTClient('Mozilla/5.0 ... Firefox/38.0', 'my-auth-token')
rc.upvote(post_id)
etc...
It could be that your use of globals is breaking things. Having not run your code, I can't check, but I would avoid using globals and favor tighter control of your state via objects.
EDIT:
Given that this answer was accepted by the OP, I am assuming the defect was caused by mutation of globals.
Is there an easy way to adapt the django-recaptcha plugin for using the new google re-captcha API?
I already changed the template and the captcha gets displayed in the write way but the validation is not working. Has anybody an idea how I can fix this in an easy way?
Or make your own: here is mine that i made using others as a template/starting point.
import json
import urllib2, urllib
API_SERVER = "https://www.google.com/recaptcha/api/siteverify"
class RecaptchaResponse(object):
def __init__(self, is_valid, error_code=None):
self.is_valid = is_valid
self.error_code = error_code
def __unicode__(self):
print "%s, %s" % (self.is_valid, self.error_code)
def __str__(self):
return self.__unicode__()
def get_error(self):
if self.error_code and len(self.error_code):
return self.error_code[0]
def submit(recaptcha_secret, recaptcha_response, remoteip=''):
if not (recaptcha_secret and recaptcha_response and len(recaptcha_secret) and len(recaptcha_response) ):
return RecaptchaResponse(is_valid=False, error_code='incorrect-captcha-sol')
def encode_if_necessary(s):
if isinstance(s, unicode):
return s.encode('utf-8')
return s
params = urllib.urlencode({
'secret': encode_if_necessary(recaptcha_secret),
'response': encode_if_necessary(recaptcha_response),
'remoteip': encode_if_necessary(remoteip)
})
request = urllib2.Request(
url=API_SERVER,
data=params,
headers={
"Content-type": "application/x-www-form-urlencoded",
"User-agent": "reCAPTCHA Python"
}
)
httpresp = urllib2.urlopen(request)
return_values = json.loads(httpresp.read())
print return_values
if return_values.get('success', False):
return RecaptchaResponse(is_valid=True)
else:
return RecaptchaResponse(is_valid=False, error_code=return_values.get('error-codes', ''))
Same code ported to Python 3. We are using it in production. If you preffer, can be dowloaded from pypi/pip.
'''
NO-CAPTCHA VERSION: 1.0
PYTHON VERSION: 3.x
'''
import json
from urllib.request import Request, urlopen
from urllib.parse import urlencode
VERIFY_SERVER = "www.google.com"
class RecaptchaResponse(object):
def __init__(self, is_valid, error_code=None):
self.is_valid = is_valid
self.error_code = error_code
def __repr__(self):
return "Recaptcha response: %s %s" % (
self.is_valid, self.error_code)
def __str__(self):
return self.__repr__()
def displayhtml(site_key, language=''):
"""Gets the HTML to display for reCAPTCHA
site_key -- The site key
language -- The language code for the widget.
"""
return """<script src="https://www.google.com/recaptcha/api.js?hl=%(LanguageCode)s" async="async" defer="defer"></script>
<div class="g-recaptcha" data-sitekey="%(SiteKey)s"></div>
""" % {
'LanguageCode': language,
'SiteKey': site_key,
}
def submit(response,
secret_key,
remote_ip,
verify_server=VERIFY_SERVER):
"""
Submits a reCAPTCHA request for verification. Returns RecaptchaResponse
for the request
response -- The value of response from the form
secret_key -- your reCAPTCHA secret key
remote_ip -- the user's ip address
"""
if not(response and len(response)):
return RecaptchaResponse(is_valid=False, error_code='incorrect-captcha-sol')
def encode_if_necessary(s):
if isinstance(s, str):
return s.encode('utf-8')
return s
params = urlencode({
'secret': encode_if_necessary(secret_key),
'remoteip': encode_if_necessary(remote_ip),
'response': encode_if_necessary(response),
})
params = params.encode('utf-8')
request = Request(
url="https://%s/recaptcha/api/siteverify" % verify_server,
data=params,
headers={
"Content-type": "application/x-www-form-urlencoded",
"User-agent": "reCAPTCHA Python"
}
)
httpresp = urlopen(request)
return_values = json.loads(httpresp.read().decode('utf-8'))
httpresp.close()
return_code = return_values['success']
if return_code:
return RecaptchaResponse(is_valid=True)
else:
return RecaptchaResponse(is_valid=False, error_code=return_values['error-codes'])
I know your question is old, but Nowadays, you just have to write in settings.py:
NOCAPTCHA = True
And that's it.
I have the following class:
class Connection(object):
defaults = {
'application_key':None,
}
def __init__(self, application_key="None"):
self.application_key = application_key or Connection.defaults.get('application_key')
def make_request(self, params=None, method=None):
url = URL_FORMAT % {
'params': params,
}
headers = {
'Authorization': 'Token token='+ self.application_key,
'content-type': 'application/json',
}
if method == "get":
request = requests.get(url, headers=headers)
if request.status_code == requests.codes.ok:
return request.json()
else:
raise APIError("%s status received (not 200)" % request.status_code)
elif method == "post":
request = requests.post(url, headers=headers)
request.status_code == requests.codes.ok
if request.status_code == requests.codes.ok:
return request.json()
else:
raise APIError("%s status received (not 200)" % request.status_code)
def get_geofence(self):
try:
data = self.make_request('geofences', 'get')
return data
except APIError:
raise GeofenceNotFound("Geofence not found")
def get_geofence_id(self, geofence_id=None):
try:
data = self.make_request('geofences/'+self.geofence_id+'/', 'get')
return data
except APIError:
raise GeofenceNotFound("Geofence not found with id #%s" % self.geofence_id)
The problem line seems to be data = self.make_request('geofences/'+self.geofence_id+'/', 'get') returning AttributeError: 'Connection' object has no attribute 'geofence_id'
I'm pretty stumped here.
geofence_id is not a class attribute, it is a function parameter. Thus, you should just refer to is as geofence_id and not self.geofence_id.