Python Mocking a request for a bearer token? - python

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.

Related

Mocking an API call python?

I have a class:
class Base(object):
def __init__(self, api_key):
self.api_key = api_key
self.url = 'http://someurl.com/api/'
self.inital_params = {"api_token": self.api_key}
def make_request(self, endpoint, params = None):
if params:
params.update(self.initial_params)
url = self.url + endpoint
r = requests.get(url, params=params)
response = r.json()
data = response.get("data")
return data
Now, I am trying to mock a HTTP request via this class.
import responses
#responses.activate
def test():
responses.add(
responses.GET,
'http://someurl.com/api/endpoint',
json= {"error": "not found"},
status=404
)
base = Base(api_key)
resp = base.make_request(endpoint=endpoint)
assert resp.json() == {"error": "not found"}
But this isn't working. I am getting the following error: requests.exceptions.ConnectionError: Connection refused by Responses - the call doesn't match any registered mock. What url am I supposed to pass in in responses.add()?
Do I have to pass in the exact url I pass in to my requests.get() function within make_request() within my Base class?

How to call tryton from jsonrpc

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

Django TestCase do not pass anymore: wrong url

I develop a DJango project and have develop few tests
One of these tests used to pass but do not anymore and I do not understand why
class IndexPageTestCase(TestCase):
def setUp(self):
self.client = Client()
self.pays = Pays.objects.create(pay_ide = 1,pay_nom = 'Côte d\'Ivoire',pay_abr = 'CIV')
self.region = Region.objects.create(reg_ide = 1,pay = self.pays,reg_nom = 'Region 1',reg_abr = 'RE1')
self.site = Site.objects.create(sit_ide = 1,reg=self.region,sit_typ = 'National',sit_nom = 'PA',sit_abr = 'PA')
self.user = User.objects.create_user('user','user#user.com','password')
self.profile = Profile.objects.create(user=self.user,site = self.site)
def test_index_page(self):
self.client.post('/registration/login/', {'username': 'user', 'password': 'password'})
response = self.client.get(reverse('randomization:index'))
print(response)
self.assertEquals(response.status_code,200)
print show it is not the good url (/accounts/login/?next=/randomization/ instead of /registration/login/):
<HttpResponseRedirect status_code=302, "text/html; charset=utf-8", url="/accounts/login/?next=/randomization/">
The url you pass returns a 302 which means redirect.
Your URL is good but is just redirected. set your assertEquals status code to 302

How to reset Requests?

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.

New Google ReCAPTCHA with django-recaptcha

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.

Categories

Resources