I have a template for a Python function that calls an API and returns data. I'd like to run load testing against that API using locust.
import requests
proxies = {"http" : None,
"https" : None}
verify = "/data/certs/abc123.crt"
def call_api(par1, par2, par3):
r = requests.post(url = 'https://ABCD123.XYZ.QWERTY:9010/public/api/v1/ABC_TEST/query',
json = {"par1" : par1, "par2" : par2, "par3" : par3}, verify = verify, proxies = proxies)
return r
How could I translate this into a locust class?
I do it this way:
# prepare the headers to send to the remote host
headers = {"key":"value",
"key":"value"}
# prepare the data for the POST body
data = {"key":"value",
"key":"value"}```
with connection_object.client.post(url, headers=headers,
json=data, name=task_name, catch_response=True) as response:
# convert the response to JSON
msg = json.loads(response.content.decode('utf-8'))
if response.status_code == 200:
# we got a 200 OK
response.success()
else:
response.failure("failure text")
In this example, this code runs in a separate function called from the UserBehavior class, so connection_object is "self" if you are doing this inside the task in the class.
In your example though, change
r = requests.post(url = 'https://ABCD123.XYZ.QWERTY:9010/public/api/v1/ABC_TEST/query',
json = {"par1" : par1, "par2" : par2, "par3" : par3}, verify = verify, proxies = proxies)
to
class UserBehavior(SequentialTaskSet):
#task()
def task1(self):
r = self.client.post('https://ABCD123.XYZ.QWERTY:9010/public/api/v1/ABC_TEST/query',
json = {"par1" : par1, "par2" : par2, "par3" : par3},
verify = verify, proxies = proxies)
I am sure you have seen this, which shows "get", but not "post". If you are unfamiliar with the requests library, it can get confusing.
Hope that helps!
Related
I have been attempting to solve this far too longer than id like to admit, I think the problem is how the data is being parsed with json and being interoperated via the API, as I do not have the same issue with the first function, but run into it with the second. Any help will be great.
import urllib, requests, json
def generateUserKey(username, password):
global devKey
return urllib.request.urlopen("https://pastebin.com/api/api_login.php",
urllib.parse.urlencode({"api_dev_key": devKey, "api_user_name": username, "api_user_password": password}).encode()).read()
def paste(userKey, text):
global devKey
datA = json.dumps({"api_dev_key": devKey, "api_paste_code": text, "api_user_key": userKey, "api_paste_name": "lol", "api_paste_format": "none", "api_paste_private": int(1), "api_paste_expire_date": "10M" })
resp = requests.post(url="https://pastebin.com/api/api_post.php", json=datA, data=datA)
print(resp.text)
key = generateUserKey(devKey, userName, passWord)
print(key)
paste(key, testString)
when ran I generate the following:
c0ce26a1c46d5fff3a254e519003ebb0
Bad API request, invalid api_dev_key
None
the dev key isnt invalid as its being used in the previous function to login and obtain a session key, so this is where I am stuck. Any help?
this could help:
import requests # see https://2.python-requests.org/en/master/
import json
def generateUserKey(data):
login = requests.post("https://pastebin.com/api/api_login.php", data=data)
print("Login status: ", login.status_code if login.status_code != 200 else "OK/200")
print("User token: ", login.text)
return login.text
def paste(data):
r = requests.post("https://pastebin.com/api/api_post.php", data)
print("Paste send: ", r.status_code if r.status_code != 200 else "OK/200")
print("Paste URL: ", r.text)
key = 'your key'
text = "hi"
t_title = "title of paste"
login_data = {
'api_dev_key': key,
'api_user_name': 'username',
'api_user_password': 'password'
}
data = {
'api_option': 'paste',
'api_dev_key': key,
'api_paste_code': text,
'api_paste_name': t_title,
'api_user_key': None,
# 'api_paste_expire_date': 'see_https://pastebin.com/api', # optional
# 'api_paste_private': 1, # 0 = public, 1 = unlisted, 2 = private
'api_user_key': generateUserKey(login_data)
# see_https://pastebin.com/api fro all the other arguments you can add
}
# if your data is already in json format, you can use json.dumps(data)
# data = json.dumps(data) # and now its a dict, and it can be feed to the requests.post
paste(data)
if you have any questions don't hesitate to ask
I'm brand new to python and api as well.
I'm trying to use a endpoint we have at work.
We have an API we are using a lot, we also have an UI. But using the UI we can only extract 10.000 records at the time.
There is no limit on the api.
I have found a small piece of code - but i need to add a nextpagetoken.
My code looks like this:
login_url = 'https://api.ubsend.io/v1/auth/login'
username = 'xxxxx'
password = 'xxxxx'
omitClaims = "true"
session = requests.Session()
session.headers['Accept'] = "application/json; charset=UTF-8"
response = session.post(
login_url,
json={'username': username, 'password': password},
headers={'VERSION': '3'},
)
response.raise_for_status()
response_data = response.json()
print(response_data)
This gives me the AccessToken.
Then I call:
getevents = 'https://api.ubsend.io/v1/reporting/shipments?'
data ={'client_id': 13490, 'created_after': '2020-05-01T00:00', 'created_before': '2021-05-02T00:00'} req.prepare_url(getevents, data)
events = requests.get(req.url, headers={'Authorization' : 'Bearer ' + response_data['accessToken'], Content-Type': 'application/json'})
events.json()
Which returns:
'nextPageToken': 'NjA4ZDc3YzNkMjBjODgyYjBhMWVkMTVkLDE2MTk4ODM5NzA3MDE='}
So I want to loop my script - until nextPageToken is blank ....
Any thoughts?
Edit thanks for the update. I think this might be the solution we're looking for. You might have to do some poking around to figure out exactly what the name of the page_token URL parameter should be.
has_next = True
getevents = 'https://api.ubsend.io/v1/reporting/shipments?'
token = None
while has_next:
data ={'client_id': 13490, 'created_after': '2020-05-01T00:00', 'created_before': '2021-05-02T00:00'}
if token:
# I don't know the proper name for this URL parameter.
data['page_token'] = token
req.prepare_url(getevents, data)
events = requests.get(req.url, headers={'Authorization' : 'Bearer ' + response_data['accessToken'], Content-Type: 'application/json'})
token = events.json().get('nextPageToken')
if not token:
has_next = False
I made a slight typo. It should be events.json().get('nextPageToken') I believe.
Let me know if this works.
1) I require a function which should publish a post with the given message and photo.
One can use the page ID and access tokens provided in self.page_id and self.page_access_token
def __init__(self):
self.page_id = Facebook.get_access_token('FACEBOOK_PAGE_ID')
self.page_access_token = Facebook.get_access_token('FACEBOOK_PAGE_ACCESS_TOKEN')
2) To find which API to hit, check out developer section of the Facebook API: https://developers.facebook.com/docs/graph-api/reference/page/photos/#example-2
3) The function should not return anything.
def publish_photo_msg(self, message, image_url):
#your code here
return
Please help.
python
params = (
('access_token', self.page_access_token),
)
Next, let us fix the data dictionary:
python
data = {
'url': image_url,
'caption': 'this is the caption'
}
Now, let’s proceed to the URL. There are many ways to set this. Here is one way:
python
url = 'https://graph.facebook.com/v5.0/' + self.page_id + '/photos?access_token=' + self.page_access_token
Since we have stored the access_token in the params tuple, let’s make use of it in the requests.post() call.
python
url = 'https://graph.facebook.com/v5.0/' + self.page_id + '/photos'
response = requests.post(url=url, params=params, data=data)
Lastly, you can also verify if your requests.post() call was successful by checking the value of the response variable:
python
print response.status_code, response.json()
For easy reference, here is the full implementation of the publish_photo_msg function with all the suggestions incorporated:
python
params = (
('access_token', self.page_access_token),
)
data = {
'url': image_url,
'caption': message
}
url = 'https://graph.facebook.com/v5.0/' + self.page_id + '/photos'
response = requests.post(url=url, params=params, data=data)
print(response.status_code, response.json())
I am using requests 2.12.1 library in Python 2.7 and Django 1.10 for consume web services. When I use a session for save cookies and use persistence, and pass 10 seconds ~ without use any web service, my view regenerates the object requests.Session()...
This makes web service doesn't serve me, because my view has changed the cookies.
This is my Views.py:
client_session = requests.Session()
#watch_login
def loginUI(request):
response = client_session.post(URL_API+'login/', data={'username': username, 'password': password,})
json_login = response.json()
#login_required(login_url="/login/")
def home(request):
response_statistics = client_session.get(URL_API+'statistics/')
log('ClientSession: '+str(client_session))
try:
json_statistics = response_statistics.json()
except ValueError:
log('ExcepcionClientSession: '+str(client_session))
return logoutUI(request)
return render(request, "UI/home.html", {
'phone_calls' : json_statistics['phone_calls'],
'mobile_calls' : json_statistics['mobile_calls'],
'other_calls' : json_statistics['other_calls'],
'top_called_phones' : json_statistics['top_called_phones'],
'call_ranges_week' : json_statistics['call_ranges_week'],
'call_ranges_weekend' : json_statistics['call_ranges_weekend'],
'access_data' : accessData(request.user.username),
})
def userFeaturesFormInit(clientRequest):
log('FeaturesClientSession: '+str(client_session))
response = client_session.get(URL_API+'features/')
try:
json_features = response.json()
except ValueError as e:
log('ExcepcionFeaturesClientSession: '+str(client_session))
raise e
Thank you.
I fixed it specifying cookies manually, and saving it in the request.
client_session = requests.Session()
response = client_session.post(URL_API+'login/', {'username': username, 'password': password,})
request.session['cookiecsrf'] = client_session.cookies['csrftoken']
request.session['cookiesession'] = client_session.cookies['sessionid']
And sending it in the gets/posts:
cookies = {'csrftoken' : request.session['cookiecsrf'], 'sessionid': request.session['cookiesession']}
response = requests.get(URL, cookies=cookies)
I'm having an awfully hard time with Yahoo's authentication/authorization. I've enabled BOSS in my account, set up a payment method, and now I'm trying to run a search using some python code:
import urllib2
import oauth2 as oauth
import time
OAUTH_CONSUMER_KEY = "blahblahblah"
OAUTH_CONSUMER_SECRET = "blah"
def oauth_request(url, params, method="GET"):
params['oauth_version'] = "1.0",
params['oauth_nonce'] = oauth.generate_nonce(),
params['oauth_timestamp'] = int(time.time())
consumer = oauth.Consumer(key=OAUTH_CONSUMER_KEY,
secret=OAUTH_CONSUMER_SECRET)
params['oauth_consumer_key'] = consumer.key
req = oauth.Request(method=method, url=url, parameters=params)
req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None)
return req
if __name__ == "__main__":
url = "http://yboss.yahooapis.com/ysearch/web"
req = oauth_request(url, params={"q": "cats dogs"})
req_url = req.to_url()
print req_url
result = urllib2.urlopen(req_url)
I keep getting a urllib2.HTTPError: HTTP Error 401: Unauthorized exception. I can't figure out whether there's something wrong with my key, or the method of signing, or if I'm somehow tampering with my data after signing, or what the deal is. Anyone have suggestions?
I made some small changes to make your example work. See code for comments.
import urllib2
import oauth2 as oauth
import time
OAUTH_CONSUMER_KEY = "blahblahblah"
OAUTH_CONSUMER_SECRET = "blah"
def oauth_request(url, params, method="GET"):
# Removed trailing commas here - they make a difference.
params['oauth_version'] = "1.0" #,
params['oauth_nonce'] = oauth.generate_nonce() #,
params['oauth_timestamp'] = int(time.time())
consumer = oauth.Consumer(key=OAUTH_CONSUMER_KEY,
secret=OAUTH_CONSUMER_SECRET)
params['oauth_consumer_key'] = consumer.key
req = oauth.Request(method=method, url=url, parameters=params)
req.sign_request(oauth.SignatureMethod_HMAC_SHA1(), consumer, None)
return req
if __name__ == "__main__":
url = "http://yboss.yahooapis.com/ysearch/web"
req = oauth_request(url, params={"q": "cats dogs"})
# This one is a bit nasty. Apparently the BOSS API does not like
# "+" in its URLs so you have to replace "%20" manually.
# Not sure if the API should be expected to accept either.
# Not sure why to_url does not just return %20 instead...
# Also, oauth2.Request seems to store parameters as unicode and forget
# to encode to utf8 prior to percentage encoding them in its to_url
# method. However, it's handled correctly for generating signatures.
# to_url fails when query parameters contain non-ASCII characters. To
# work around, manually utf8 encode the request parameters.
req['q'] = req['q'].encode('utf8')
req_url = req.to_url().replace('+', '%20')
print req_url
result = urllib2.urlopen(req_url)
Here is a Python code snippet that works for me against Yahoo! BOSS:
import httplib2
import oauth2
import time
OAUTH_CONSUMER_KEY = "Blah"
OAUTH_CONSUMER_SECRET = "Blah"
if __name__ == "__main__":
url = "http://yboss.yahooapis.com/ysearch/web?q=cats%20dogs"
consumer = oauth2.Consumer(key=OAUTH_CONSUMER_KEY,secret=OAUTH_CONSUMER_SECRET)
params = {
'oauth_version': '1.0',
'oauth_nonce': oauth2.generate_nonce(),
'oauth_timestamp': int(time.time()),
}
oauth_request = oauth2.Request(method='GET', url=url, parameters=params)
oauth_request.sign_request(oauth2.SignatureMethod_HMAC_SHA1(), consumer, None)
oauth_header=oauth_request.to_header(realm='yahooapis.com')
# Get search results
http = httplib2.Http()
resp, content = http.request(url, 'GET', headers=oauth_header)
print resp
print content
Im using an Authenticate Header to submit the OAuth signature.
So I decided to ditch Python and try Perl, and it Just Worked. Here's a minimal code sample:
use strict;
use Net::OAuth;
use LWP::UserAgent;
my $CC_KEY = "blahblahblah";
my $CC_SECRET = "blah";
my $url = 'http://yboss.yahooapis.com/ysearch/web';
print make_request($url, {q => "cat dog", format => "xml", count => 5});
sub make_request {
my ($url, $args) = #_;
my $request = Net::OAuth->request("request token")
->new(
consumer_key => $CC_KEY,
consumer_secret => $CC_SECRET,
request_url => $url,
request_method => 'GET',
signature_method => 'HMAC-SHA1',
timestamp => time,
nonce => int(rand 10**6),
callback => 'oob',
extra_params => $args,
protocol_version => Net::OAuth::PROTOCOL_VERSION_1_0A,
);
$request->sign;
my $res = LWP::UserAgent->new(env_proxy=>1)->get($request->to_url);
return $res->content if $res->is_success;
die $res->status_line;
}
Here's another solution, this time back in python-land. This was put together by Tom De Smedt, author of the Pattern web-mining kit.
I'll communicate with the author of python-oauth2 to see if it can be fixed.
OAUTH_CONSUMER_KEY = "blahblahblah"
OAUTH_CONSUMER_SECRET = "blah"
import urllib
import hmac
import time
import random
import base64
try:
from hashlib import sha1
from hashlib import md5
except:
import sha as sha1
import md5; md5=md5.new
def hmac_sha1(key, text):
return hmac.new(key, text, sha1).digest()
def oauth_nonce(length=40):
h = "".join([str(random.randint(0, 9)) for i in range(length)])
h = md5(str(time.time()) + h).hexdigest()
return h
def oauth_timestamp():
return str(int(time.time()))
def oauth_encode(s):
return urllib.quote(s, "~")
def oauth_signature(url, data={}, method="get", secret="", token=""):
# Signature base string: http://tools.ietf.org/html/rfc5849#section-3.4.1
base = oauth_encode(method.upper()) + "&"
base += oauth_encode(url.rstrip("?")) + "&"
base += oauth_encode("&".join(["%s=%s" % (k, v) for k, v in sorted(data.items())]))
# HMAC-SHA1 signature algorithm: http://tools.ietf.org/html/rfc5849#section-3.4.2
signature = hmac_sha1(oauth_encode(secret) + "&" + token, base)
signature = base64.b64encode(signature)
return signature
q = "cat"
url = "http://yboss.yahooapis.com/ysearch/" + "web" # web | images | news
data = {
"q": q,
"start": 0,
"count": 50, # 35 for images
"format": "xml",
"oauth_version": "1.0",
"oauth_nonce" : oauth_nonce(),
"oauth_timestamp" : oauth_timestamp(),
"oauth_consumer_key" : OAUTH_CONSUMER_KEY,
"oauth_signature_method" : "HMAC-SHA1",
}
data["oauth_signature"] = oauth_signature(url, data, secret=OAUTH_CONSUMER_SECRET)
complete_url = url + "?" + urllib.urlencode(data)
response = urllib.urlopen(complete_url)
print response.read()
Here is sample code to access Yahoo! BOSS API v2 using with python-oauth as oauth liberary.
OAUTH_CONSUMER_KEY = "<oauth consumer key>"
OAUTH_CONSUMER_SECRET = "<oauth consumer secret>"
URL = "http://yboss.yahooapis.com/ysearch/web"
import urllib
import oauth.oauth as oauth
data = {
"q": "yahoo boss search",
"start":0,
"count":2,
"format":"json"
}
consumer = oauth.OAuthConsumer(OAUTH_CONSUMER_KEY, OAUTH_CONSUMER_SECRET)
signature_method_plaintext = oauth.OAuthSignatureMethod_PLAINTEXT()
signature_method_hmac_sha1 = oauth.OAuthSignatureMethod_HMAC_SHA1()
oauth_request = oauth.OAuthRequest.from_consumer_and_token(consumer, token=None, http_method='GET', http_url=URL, parameters=data)
oauth_request.sign_request(signature_method_hmac_sha1, consumer, "")
complete_url = oauth_request.to_url()
response = urllib.urlopen(complete_url)
print "REQUEST URL => %s" % complete_url
print ""
print "RESPONSE =>"
print response.read()
I stepped into the urllib2.open code using the debugger, and found that the response has this header:
WWW-Authenticate: OAuth oauth_problem="version_rejected", realm="yahooapis.com"
So I guess I'm having some kind of version mismatch of OAuth.