Requests package and API documentation - python

I'm having trouble understanding where to add parameters defined by API documentation. Take BeeBole's documentation for example, which specifies that to get an absence by ID, the following request is required:
{
"service": "absence.get",
"id": "absence_id"
}
They provide only one URL in the documentation:
BeeBole is accepting HTTP POST resquests in a json-doc format to the following URL:
https://beebole-apps.com/api/v2
How would this be implemented in the context of Python requests? The following code I've tried returns 404:
import requests
payload = {
"service": "absence.get",
"id": "absence_id"
}
auth = {
"username": "API_token",
"password": "x"
}
url = "https://beebole-apps.com/api/v2"
req = requests.get(url, params=payload, auth=auth).json()

BeeBole is accepting HTTP POST resquests in a json-doc format to the following URL: https://beebole-apps.com/api/v2
The JSON document format here is the part you missed; you need to pass the information as a JSON encoded body of the request. The params argument you used only sets the URL query string (the ?... part in a URL).
Use
import requests
payload = {
"service": "absence.get",
"id": "absence_id"
}
auth = ("API_token", "x")
url = "https://beebole-apps.com/api/v2"
req = requests.get(url, json=payload, auth=auth).json()
The json= part ensures that the payload dictionary is encoded to JSON and sent as a POST body. This also sets the Content-Type header of the request.
I've also updated the API authentication, all that the auth keyword needs here is a tuple of the username and password. See the Basic Authentication section.
You may want to wait with calling .json() on the response; check if the response was successful first:
req = requests.get(url, json=payload, auth=auth)
if not req.ok:
print('Request not OK, status:', req.status_code, req.reason)
if req.content:
print(req.text)
else:
data = req.json()
if data['status'] == 'error':
print('Request error:', data['message'])
This uses the documented error responses.

From the site documentation it would appear that this particular vendor has chosen an unusual API. Most people use different endpoints to implement different operations, but BeeBole appears to implement everything off the one endpoint, and then selects the operation by examining the "service" key in the request data.
Try
response - request.post('https://beebole-apps.com/api/v2',
json={"service": "company.list"},
headers={"authorization": TOKEN)
From the documentation I can't guarantee that will put the request in the right format, but at least if it doesn't work it should give you some clue as to how to proceed. Establishing the correct value of TOKEN is described under "Authorization" in the BeeBole documentation.
It's an unusual way to offer an API, but it seems workable.

Related

Zoho CRM API: Python request-based POST or GET authentication + insertion of contacts

The Task##
A django application that allows users to sign up and once the user clicks on the account activation link, Zoho CRM is receiving the data and a contact is created in the CRM section.
The Problem
I am currently working on an absolute masterpiece - the ZOHO API.
I am struggling to set up the native Python code that uses POST/GET requests.
Regarding the zcrmsdk 3.0.0, I have completely given up on this solution unless somebody can provide a fully functional example. The support simply blames my code.
The documentation I consulted:
https://www.zoho.com/crm/developer/docs/api/v2/access-refresh.html,
https://www.zoho.com/crm/developer/docs/api/v2/insert-records.html
Since the post request in postman API works fine I do not understand why it does not work in python code
My approach
Generate an self-client API code on: https://api-console.zoho.com/
Insert that code on Postman and retrieve the access or refresh token
Use this access token in an add_user_contact function that is defined in the documentation
It works! Response is success and it is in Zoho CRM
The permsissions scope I am using is: ZohoCRM.modules.contacts.ALL, ZohoCRM.users.ALL, ZohoCRM.modules.deals.ALL, ZohoCRM.modules.attachments.ALL, ZohoCRM.settings.ALL, AAAserver.profile.ALL
Picture of Post Man POST REQUEST
My own Code
def authenticate_crm():
"""
access to response object id:
response_object.get('data')[0].get('details').get('id')
"""
url = 'https://accounts.zoho.com/oauth/v2/token'
headers = {
'Content-Type': 'application/x-www-form-urlencoded'
}
# one time self-client token here -
request_body = {
"code": "1000.aa8abec144835ab79b8f9141fa1fb170.8ab194e4e668b8452847c7080c2dd479",
"redirect_uri": "http://example.com/yourcallback",
"client_id": "1000.H95VDM1H9KCXIADGF05E0E1XSVZKFQ",
"client_secret": "290e505ec52685fa62a640d874e6560f2fc8632e97",
" grant_type": "authorization_code"
}
response = requests.post(url=url, headers=headers, data=json.dumps(request_body).encode('utf-8'))
if response is not None:
print("HTTP Status Code : " + str(response.status_code))
print(response.json())
I am essentially struggling to convert the Postman API request to a Python request to get the token as part of the workflow. What am I doing wrong here?
The documentation states: Note: For security reasons, pass the below parameters in the body of your request as form-data. (access-refresh link) but passing it in postman as form-data breaks the call completely.
According to their own documentation (which is convoluted, contradictory and full of outdated screenshots) the authentication key is needed only once.
Once the request from above runs, I would take the response in the third image and use the refresh key to add the contact.
I am also open to a solution with the SDK 3.0.0, if anybody can help.
I solved it!
I have changed this line:
response = requests.post(url=url, headers=headers, data=json.dumps(request_body).encode('utf-8'))
to this and added some return statement:
payload = '1000.6d9411488dcac999f02304d1f7843ab2.e14190ee4bae175debf00d2f87143b19&' \
'redirect_uri=http%3A%2F%2Fexample.com%2Fyourcallback&' \
'client_id=1000.H95VDM1H9KCXIADGF05E0E1XSVZKFQ&' \
'client_secret=290e505ec52685fa62a640d874e6560f2fc8632e97&'\
'grant_type=authorization_code'
response = requests.request(method="POST", url=url, headers=headers, data=payload)
if response is not None:
print("HTTP Status Code : " + str(response.status_code))
# print(response.text)
print(response.json())
# catch access and refresh token
at = response.json().get('access_token')
rt = response.json().get('refresh_token')
return at, rt
I do not understand why that is different but that fixed it and I could retrieve keys from ZOHO.

Any idea how to open bytes object in new tab?

I am requesting an api to grant access to communicate with application for integration. While requesting I am getting response which is in bytes object that is why i am unable to open it in new tab because I want login to access my application.
header = {
'Content-Type': 'application/x-www-form-urlencoded'
}
response = requests.get(
'https://api.getbase.com/oauth2/authorize',
data='client_id=' + self.client_id + '&response_type=code&redirect_uri='
+ self.redirect_uri, headers=header).content
webbrowser.open_new_tab(response)
just fixing your stated problem, you need to decode the bytes into a string or just use the text attribute.
that said, your code seems unnecessarily fragile and might be nicer as:
response = requests.post('https://api.getbase.com/oauth2/authorize', data={
'client_id': self.client_id,
'response_type': 'code',
'redirect_uri': self.redirect_uri
})
# make sure we throw an exception on failure
response.raise_for_status()
webbrowser.open_new_tab(response.text)
that way you can let requests deal with encoding/escaping the parameters appropriately. the default encoding/data type with an HTTP POST request is application/x-www-form-urlencoded as you need. GET requests don't send a body, so I'm not sure how your previous code was working

Refreshing google access tokens with the refresh tokens

How can I refresh the access tokens in google with the refresh token in python ? I have the refresh token, client id and client secret with me
I have tried with the following code for generating the access token (after expiry)
params = {
"grant_type": "refresh_token",
"client_id": client_id,
"client_secret": client_secret,
"refresh_token": refresh_token
}
authorization_url = "https://www.googleapis.com/oauth2/v4/token"
r = requests.post(authorization_url, data=params)
print(r.text)
if r.ok:
return r.json()['access_token']
else:
return None
I got an error in response like this:
{
"error": "invalid_grant",
"error_description": "Bad Request"
}
But I need to generate the access token.
These are the three top reasons for it not to be working. I am not a Python dev so cant test your code sorry.
Send parms as a string
Check your parms make sure they are sent as a single string separated with &.
Post https://accounts.google.com/o/oauth2/token
client_id={ClientId}&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
content type header
Also i think you need to set the content type header to something like application/x-www-form-urlencoded.
Change endpoint
If that doesnt work
Also try using https://accounts.google.com/o/oauth2/token instead of https://www.googleapis.com/oauth2/v4/token I remember there being some issue with one or the other of them sometimes

Unable to login to website through python

I am trying to login to a website in order to get some data. I have noticed that there is not form-data in the 'post' method but there is a 'request payload'. Furthermore, when I login in I cannot see anymore the login post method. Here is a screenshot of the network post login method:
When I login the next page showed is I use the following code in order to login:
import requests
urlData = 'https://b*********.dk/Account/Market'
urlLogin = 'https://b**********an.dk/
with requests.Session() as c:
urlLogin = 'https://b*************n.dk/Authorization/
c.get(urlLogin)
NetSession = c.cookies['ASP.NET_SessionId']
login_data = {
'ASP.NET_SessionId': NetSession,
'username':"A******",
'Password':"q******",
'remmemberMe': True
}
lol = c.post(urlLogin, data=login_data)
print(lol.text)
Running this code the following is outputed:
{"Processed":true,"Message":"The user name or password provided is incorrect.","NeedResetPassword":false}
When i input a wrong password the Processed value is false, while with correct credentials is true. But it deosnt login. Any idea why this could happen?
As you've already correctly noticed, the original credentials are not sent using form encoding (meaning &user=alice&password=secret), but are JSON encoded (so rather {"user":"alice", "password": "secret"}). You can also see this in the request's Content-Type header, which is application/json where (as opposed to application/x-www-form-urlencoded otherwhise).
For your custom request to work, you propably also need to send JSON-encoded data. This is documented in length in the official Documentation, so I'll just give the short version:
import json
# Build session and request body just like you already did in your question
# ...
headers = {"Content-Type": "application/json"}
lol = c.post(urlLogin, data=json.dumps(login_data), headers=headers)
print(lol.json())

RallyDev: Unable to create defect. Server says "Cannot parse input ..."

Original Post:
I'm trying to create defects programmatically. I am getting a couple errors and having trouble getting any further. Here, essentially, is the code:
import requests, json
rally_auth = ('my_user', 'my_pw')
rally_auth_url = 'https://rally1.rallydev.com/slm/webservice/v2.0/security/authorize'
rally_defect = 'https://rally1.rallydev.com/slm/webservice/v2.0/defect/defect'
workspace_ref = 'https://rally1.rallydev.com/slm/webservice/v2.0/workspace/12345'
fe_project_ref = 'https://rally1.rallydev.com/slm/webservice/v2.0/project/7890'
current_fe_release_ref = "https://rally1.rallydev.com/slm/webservice/v2.0/release/45678"
r = requests.get(rally_auth_url, auth=rally_auth)
token = r.json()['OperationResult']['SecurityToken']
url = rally_defect + '/create?key=' + token
payload = {
'Name': 'My defect',
'State': 'Open',
'Project': fe_project_ref,
'Rank': 120,
'Release': current_fe_release_ref,
'key': token
}
headers = {'content-type': 'application/json'}
r = requests.post(url, data=json.dumps(payload), auth=rally_auth, headers=headers)
You'll notice that i've put the token in both the POST's URL and data. The API docs say that I should have the key in the URL, but if I do not include the key in the POST data I get:
{"CreateResult": {"_rallyAPIMajor": "2", "_rallyAPIMinor": "0", "Errors": ["Not authorized to perform action: Invalid key"], "Warnings": []}}
If I do include the key, the API request falls over differently. It will fail on the first comma.
{"CreateResult": {"_rallyAPIMajor": "2", "_rallyAPIMinor": "0", "Errors": ["Cannot parse input stream due to I/O error as JSON document: Parse error: expected '}' but saw ',' [ chars read = >>>{\"Name\": \"My defect\",<<< ]"], "Warnings": []}}
I am baffled.
Fixed code
Thanks to #nickm
import requests, json
rally_auth = ('my_user', 'my_pw')
rally_auth_url = 'https://rally1.rallydev.com/slm/webservice/v2.0/security/authorize'
rally_defect = 'https://rally1.rallydev.com/slm/webservice/v2.0/defect/defect'
workspace_ref = 'https://rally1.rallydev.com/slm/webservice/v2.0/workspace/12345'
fe_project_ref = 'https://rally1.rallydev.com/slm/webservice/v2.0/project/7890'
current_fe_release_ref = "https://rally1.rallydev.com/slm/webservice/v2.0/release/45678"
s = requests.Session()
r = s.get(rally_auth_url, auth=rally_auth)
token = r.json()['OperationResult']['SecurityToken']
url = rally_defect + '/create?key=' + token
payload = {
'Name': 'My defect',
'State': 'Open',
'Project': fe_project_ref,
'Rank': 120,
'Release': current_fe_release_ref,
}
headers = {'content-type': 'application/json'}
r = s.post(url, data=json.dumps(payload), headers=headers)
If you are using v2.0 of WS API, the token is required for update and create requests, so you are correct by including it in your post request url.
The invalid key error will come up if a token is not appended to the request or if the token is invalid for a specific session. When hitting the endpoints directly we have to maintain an http session with a cookie, otherwise the post happens in the context of a new session - different from the one in which we got the token.
Please see this post. It is not specific to Python, but conceptually it's the same.
I noticed Rank in the payload. Do you have a Rank custom field? There is no such built-in field in v2.0 in Rally. There is a DragAndDropRank, which is not numeric, and setting it by supplying a value of 120 will not work.Also, did you try double quotes instead of single quotes in the payload?
There is a pyral - Rally Python toolkit that provide convenience methods so you do not have to hit endpoints directly. Currently it works with 1.43 of WS API. The toolkit is not officially supported, but I expect that it will be updated to work with v2.0 of WS API before June 2014, when 1.43 is no longer supported (per this schedule).
Security token was introduced in v2.0. This extra authentication layer for post requests does not exist in 1.43 of WS API, and you do not have to deal with the token if using pyral.

Categories