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.
Related
I was following tutorial https://towardsdatascience.com/using-the-strava-api-and-pandas-to-explore-your-activity-data-d94901d9bfde and this github https://github.com/franchyze923/Code_From_Tutorials/blob/master/Strava_Api/strava_api.py (proposed in tutorial).
And cannot find solution to problem below, I found something about scope but do not know how to use it, thus I am here seeking help
{'message': 'Authorization Error',
'errors': [{'resource': 'AccessToken',
'field': 'activity:read_permission',
'code': 'missing'}]}
import requests
import urllib3
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
auth_url = "https://www.strava.com/oauth/token"
activites_url = "https://www.strava.com/api/v3/athlete/activities"
payload = {
'client_id': "XXXXXX",
'client_secret': 'XXXXXX',
'refresh_token': 'XXXXXXXXX',
'grant_type': "refresh_token",
'f': 'json'
}
print("Requesting Token...\n")
res = requests.post(auth_url, data=payload, verify=False)
access_token = res.json()['access_token']
print("Access Token = {}\n".format(access_token))
activities_url = f"https://www.strava.com/api/v3/athlete/activities?" \
f"access_token={access_token}"
print('RESTful API:', activities_url)
# Get the response in json format
response = requests.get(activities_url)
activity = response.json()
I tried to change payload and find another solution but results were always as above. I receive my access token though
Access Token = 61766e12XXXX062XXX2a2eXXXXXXXXXX
first of all, scope in strava api is something that represents what types of access are being granted to the app from a strava user account. it can be read user's public activity, read private activity, write etc.
in case of getting authenticated: strava uses oAuth2 and has a very detailed guideline to get started for the first time. You may check their official doc page: (https://developers.strava.com/docs/authentication/#oauthoverview).
the process is explained in very detailed and should be enough for your problem.
In order to generate the token for API Requests, apple outlines the following steps.
The key, kid, and iss have all been verified to work. However in the following python script,
import jwt
import requests
# pseudo, removed secret info
# read the file, currently binary but have tried string too
with open('AuthKey_4..._.p8', 'r+b') as keyfile:
secret = keyfile.read()
expir = round(time.time() + 20 * 60)
# sign the token with the iss, time, key, and kid with the correct alg
token = jwt.encode({'iss': '6...',
'exp': f'{expir}',
'aud': 'appstoreconnect-v1'},
secret, algorithm='ES256',
headers={'alg': 'ES256', 'kid': '4...', 'typ': 'JWT'})
# decode the bytes and create the get request header
s_token = token.decode('utf-8')
headers = {'Authorization': f'Bearer {s_token}'}
# send the get request
r = requests.get('https://api.appstoreconnect.apple.com/v1/salesReports',
headers=headers)#, params=params)
r.json() simply returns
{'errors': [{'status': '401',
'code': 'NOT_AUTHORIZED',
'title': 'Authentication credentials are missing or invalid.',
'detail': 'Provide a properly configured and signed bearer token, and make sure that it has not expired. Learn more about Generating Tokens for API Requests https://developer.apple.com/go/?id=api-generating-tokens'}]}
In addition the link in the error message appears to be broken as well.
I have tried reading in the .p8 file in both binary and the regular string representation. I have tried passing different values in the token, removing certain values etc. I also have tried not passing the payload parameters into the GET request which also results in a 401 error. The payload information is listed here. Any help appreciated.
exp cannot be a string...
import jwt
import requests
# pseudo, removed secret info
# read the file, currently binary but have tried string too
with open('AuthKey_4..._.p8', 'r+b') as keyfile:
secret = keyfile.read()
expir = round(time.time() + 20 * 60)
# sign the token with the iss, time, key, and kid with the correct alg
token = jwt.encode({'iss': '6...',
'exp': expir,
'aud': 'appstoreconnect-v1'},
secret, algorithm='ES256',
headers={'alg': 'ES256', 'kid': '4...', 'typ': 'JWT'})
# decode the bytes and create the get request header
s_token = token.decode('utf-8')
headers = {'Authorization': f'Bearer {s_token}'}
# send the get request
r = requests.get('https://api.appstoreconnect.apple.com/v1/salesReports',
headers=headers)#, params=params)
I am trying to use the foursquare API for the first time and have not been able to get a 200 status code no matter what I tried. Using directly the code foursquare provides in its docs, I attempt to do a userless request, replacing my client ID and secret where necessary.
def places_search(term,street, zipcode):
params = dict(
client_id='MY_CLIENT_ID',
client_secret='MY_CLIENT_SECRET',
v='20181122',
near=street + 'New York, NY ' + zipcode,
query=term,
limit=1
)
url = "https://api.foursquare.com/v2/venues/explore"
resp = requests.get(url=url, params=params)
#grabbing the JSON result
data = json.loads(resp.text)
return data
print(places_search("starbucks","3rd avenue","10009"))
I get the following error code:
{'meta': {'code': 400, 'errorType': 'invalid_auth', 'errorDetail': 'Missing access credentials. See https://developer.foursquare.com/docs/api/configuration/authentication for details.', 'requestId': '5bf6cbb26a607137bc33b7f2'}, 'response': {}}
I find it odd given my request is userless and matches exactly the way they outline it in the docs... Would you have an idea what I am doing wrong?
Thanks,
As per the documentation:
To make a userless request, specify your consumer key’s Client ID and
Secret instead of an auth token in the request URL.
Like this:
https://api.foursquare.com/v2/venues/search?ll=40.7,-74&client_id=CLIENT_ID&client_secret=CLIENT_SECRET&v=YYYYMMDD
Since you are passing the credentials as the request parameters, the server cannot authenticate you.
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.
Lately I've come across a number of questions and articles very briefly covering using urllib, requests, mwapi, poster, and various other tools to either perform an HTTP POST, or working with the API to upload one or more files to a MediaWiki instance. Thus far, nothing has worked.
So, could someone kindly provide a simple code block that will reliably upload a file to such a Wiki? My preference is in Requests and/or Python 3, but at this point I'm pretty desperate and am open to almost anything.
Edit:
Per the request in the comments, below is the last bit of code I attempted. It completes with no errors, but of course no file is uploaded or any change to the Wiki logs.
from urllib.parse import quote
import requests
user = 'username'
passw = quote('password')
baseurl = 'http://127.0.0.1:8020/mediawiki/'
apiurl = baseurl + 'api.php'
login_params = '?action=login&lgname=%s&lgpassword=%s&format=json'% (user, passw)
# Login request
r1 = requests.post(apiurl+login_params)
login_token = r1.json()['login']['token']
# Login confirm
login_params2 = login_params+'&lgtoken=%s'% login_token
r2 = requests.post(apiurl+login_params2, cookies=r1.cookies)
# Get edit token
params3 = '?format=json&action=query&meta=tokens&continue='
r3 = requests.get(apiurl+params3, cookies=r2.cookies)
edit_token = r3.json()['query']['tokens']['csrftoken']
edit_cookie = r2.cookies.copy()
edit_cookie.update(r3.cookies)
# Upload file
with open('91.png', 'rb') as f:
headers = {'content-type': 'multipart/form-data'}
payload = {'action': 'upload', 'filename': 'Image', 'file': '91.png', 'token': edit_token}
files = {'files': f}
r4 = requests.post(apiurl, headers=headers, data=payload, files=files, cookies=edit_cookie)
I'm glad you got mwclient to work, but I think I can answer your preference of using just Python 3 and requests.
I was having major headaches doing the same thing, and finally got the following to work. I also posted it at https://www.mediawiki.org/wiki/API_talk:Upload#Python_with_requests but since this was a question I found while trying to solve my problem, I'll reproduce below...
You probably strictly speaking do not need a BotPassword, but it's a good idea.
import requests
api_url = 'https://project/w/api.php'
USER,PASS=u'BotUsername#Instancename',u'[[Special:BotPasswords]] password'
#Ensure bot instance is permissioned for createeditmovepage, uploadfile, uploadeditmovefile
FILENAME='/path/to/file'
REMOTENAME='remote_filename.ext'
USER_AGENT='Descriptive User Agent per [[:meta:User-Agent_policy]]'
# get login token and log in
payload = {'action': 'query', 'format': 'json', 'utf8': '',
'meta': 'tokens', 'type': 'login'}
r1 = requests.post(api_url, data=payload)
login_token=r1.json()['query']['tokens']['logintoken']
login_payload = {'action': 'login', 'format': 'json', 'utf8': '',
'lgname': USER, 'lgpassword': PASS, 'lgtoken': login_token}
r2 = requests.post(api_url, data=login_payload, cookies=r1.cookies)
cookies=r2.cookies.copy()
# We have now logged in and can request edit tokens thusly:
def get_edit_token(cookies):
edit_token_response=requests.post(api_url, data={'action': 'query',
'format': 'json',
'meta': 'tokens'}, cookies=cookies)
return edit_token_response.json()['query']['tokens']['csrftoken']
# Now actually perform the upload:
upload_payload={'action': 'upload',
'format':'json',
'filename':REMOTENAME,
'comment':'<MY UPLOAD COMMENT>',
'text':'Text on the File: page... description, license, etc.',
'token':get_edit_token(cookies)}
files={'file': (REMOTENAME, open(FILENAME,'rb'))}
headers={'User-Agent': USER_AGENT}
upload_response=requests.post(api_url, data=upload_payload,files=files,cookies=cookies,headers=headers)
To me, it seems weird that your last code didn't error. Assuming that you set up MediaWiki with the standard configuration, api.php is in baseurl/w/api.php, not baseurl/api.php. This means that you requested the wrong page all the time. Try again, but this time replace apiurl = baseurl + 'api.php' with apiurl = baseurl + 'w/api.php'.
It turns out that the one I hadn't tried worked. Using the documented examples for mwclient, uploading was successful.