Querying JIRA via REST API and possible bad values in query - python

I have a portal users can access built on cherrypy which has some forms which can be submitted that will be sent to JIRA via the REST api for tracking purposes. Once it has been submitted I then take the information from the user supplied information on the form and that JIRA Issue ID and send them to an oracle DB.
As well, I then extended the functionality of the portal to be able to view the user submissions via a list page and then select a record to view what is stored in the DB for that submission. I had the idea to then use the REST API for JIRA to get what the status and assignee is for the Issue within JIRA. Converting my code to submit to the API to instead query it with the necessary JQL statement was fairly simple and can be seen below.
def jira_status_check(jira_id):
if jira_id != "No JIRA Issue":
try:
search_url = "https://myjirainstance.atlassian.net/rest/api/2/search/?jql=issue=" + jira_id + "&fields=status,assignee,resolution"
print search_url
username = 'some_user'
password = 'some_password'
request = urllib2.Request(search_url)
base64string = base64.encodestring('%s:%s' % (username, password)).replace('\n', '')
request.add_header("Authorization", "Basic %s" % base64string)
request.add_header("Content-Type", "application/json")
result = urllib2.urlopen(request).read()
json_results = json.loads(result)
print json_results
jira_status = json_results["issues"][0]["fields"]["status"]["name"]
if json_results["issues"][0]["fields"]["resolution"] is None:
tmp = "tmp"
if json_results["issues"][0]["fields"]["resolution"] is not None:
jira_status = jira_status + " - " + json_results["issues"][0]["fields"]["resolution"]["name"]
# assignee_name = "TEST"
# assignee_NT = "TEST"
if json_results["issues"][0]["fields"]["assignee"] is None:
assignee_name = "Unassigned"
assignee_NT = "Unassigned"
if json_results["issues"][0]["fields"]["assignee"] is not None:
assignee_name = json_results["issues"][0]["fields"]["assignee"]["displayName"]
assignee_NT = json_results["issues"][0]["fields"]["assignee"]["name"]
# if json_results["issues"][0]["fields"]["assignee"]["displayName"] is not None:
# assignee_name = json_results["issues"][0]["fields"]["assignee"]["displayName"]
# if json_results["issues"][0]["fields"]["assignee"] is None:
# assignee_NT = "Unassigned"
# if json_results["issues"][0]["fields"]["assignee"]["name"] is not None:
# assignee_NT = json_results["issues"][0]["fields"]["assignee"]["name"]
print jira_status
print assignee_name
print assignee_NT
output = [jira_status, assignee_name, assignee_NT]
except:
jira_status = "No JIRA Issue by that number or JIRA inaccessible"
assignee_name = "No JIRA Issue by that number or JIRA inaccessible"
assignee_NT = "No JIRA Issue by that number or JIRA inaccessible"
output = [jira_status, assignee_name, assignee_NT]
else:
jira_status = "No JIRA Issue"
assignee_name = "No JIRA Issue"
assignee_NT = "No JIRA Issue"
output = [jira_status, assignee_name, assignee_NT]
return output
However it was limited to searching a single record at a time, which works when you are only viewing the single record, but I was hoping to extend this possibly to my list page and searching many at once with one api query rather than tons of single issue queries. I am capable of using jql and the rest API to search with multiple Issue numbers at a link like this https://myjirainstance.atlassian.net/rest/api/2/search/?jql=Issue%3DSPL-3284%20OR%20Issue%3DSPL-3285&fields=status,assignee,resolution
But then I was thinking about what if somehow a bad Issue ID is saved and queried as a part of the massive query. Previously it was handled with the except statement in my jira_status_check function when it was a single record query. When I try to query the rest api with a link like the last one shared I instead get
{"errorMessages":["An issue with key 'SPL-6666' does not exist for field 'Issue'."],"warningMessages":[]}
I tried to build a query from an advanced search of issues but when I do something like Issue=SPL-3284 OR Issue=SPL-3285 OR Issue=SPL-6666 I get a response of An issue with key 'SPL-6666' does not exist for field 'Issue'.
Is there a correct way to search via JQL with multiple Issue numbers and give back no values for the fields for ones without matching issue numbers?
Or am I stuck with doing a ton of single issue queries to the api to cover my bases? This would be less than ideal, and might cause me to just limit the api queries to when a single record is viewed rather than the list page for usability.
Would I be better off moving my function to query JIRA to javascript/jquery that can populate the list of submissions after the page is rendered?

I ended up reaching out to Atlassian with my question about JQL and then was given the following rest api documentation and told about the validateQuery parameter to add to my JQL to achieve my search. https://docs.atlassian.com/jira/REST/6.1.7/
When I now use a query similar to this on my rest api link with my additional parameter
jql=Issue%3DSPL-3284 OR Issue%3DSPL-3285&fields=status,assignee,resolution&validateQuery=true
I get back a JSON with actual content for the issues which are valid and then a separate warningMessages object with any that are bad. An example JSON is below, but obviously $CONTENT would be actual results from the query
{
"expand": "schema,names",
"startAt": 0,
"maxResults": 50,
"total": 2,
"issues": [
{
$CONTENT
},
{
$CONTENT
}
],
"warningMessages": [
"An issue with key 'SPL-6666' does not exist for field 'Issue'."
]
}
Hopefully someone else will find this helpful in the future

Related

How to stop API feed from disconnecting without showing errors

I have constructed a news feed from a news agency which reads headlines as they're published (I'd rather not say which one for privacy reasons). One of the drawbacks of this API is that, unlike the filtered-stream from the Twitter API, it explicitly says in the documentation for this news agency's API that one needs to continuously fetch individual headlines in order to "simulate a wire feed". Thus, I have constructed a while loop that continuously fetches headlines and prints them to the terminal.
import config # The .py file that contains the username and password for my account with this service.
username = config.username
password = config.password
values = {
'username': username,
'password': password,
'format':'json'
}
# Functions
# The API requires fetching an authentication token using your username and password, which is regenerated every 24 hours.
def get_token():
token = ''
parameters = values
url = '<URL>'
response = requests.get(url, params=parameters)
token = response.json()['authToken']['authToken'].replace('=', '')
return token
# The API has multiple possible channels (though my subscription only has access to one),
#so I fetch the available channels using the authentication token obtained by the previous function.
def get_channel():
token = get_token()
parameters = {'token': token}
url = '<URL>'
response = requests.get(url, params=parameters)
dict_data = xmltodict.parse(response.content)
channel = dict_data['availableChannels']['channelInformation']['alias']
return channel
def get_headline():
url = '<URL>'
channel = get_channel()
token = get_token()
values = {'channel':channel, 'token':token, 'limit':1, 'maxAge':'10s'}
response = requests.get(url, params=values)
if response.status_code != 200:
raise Exception(
f"Cannot get stream (Error {response.status_code}): {response.text}"
dict_data = xmltodict.parse(response.content)
return dict_data
# According to the docs, the only way to simulate an actual live feed is to make constant requests for headlines,
#so I constructed a while loop that fetches headlines and then adds each headline to a set
#and compares each new headline that is fetched with the set to see if it has already been fetched.
#If it has, then the headline is printed.
def api_stream():
print('Connected to Stream!')
new_headline = ''
data = set()
while True:
dict_data = get_headline()
try:
# News headlines are classified on a scale of priority from 1-4 (1 being highest priority).
# Since I am only interested in headlines for breaking news, I only headlines of priorities 1, or 2.
if int(dict_data['results']['result']['priority']) < 3:
new_headline = dict_data['results']['result']['headline']
else:
pass
if new_headline not in data and new_headline != '':
print(new_headline)
data.add(new_headline)
except KeyError:
continue
# The get_headline() function fetches headlines that are only 10 seconds old, so if the most recent headline is older than it, it will return a KeyError saying that ['result'] is not a key in the dictionary, so I handle the exception this way.
This code usually works throughout the day but overnight starting at around midnight until around 7:30am, it will stop printing headlines without displaying an error message. I've tried a number of different things, such as containing this in another while loop, adding a second except block which calls the api_stream() function in the event of an error but nothing's worked, it just stops fetching headlines without warning.

Adding methods gives a 'index out of range error'?

When adding a vital component of methods=["POST", "GET"], my code gives the error:
Line 127, in PatientDashboard
""".format(Data[0][0]))
IndexError: list index out of range
I understand what this error normally means but I don't understand how adding methods affect the size of my list.
#app.route("/PatientDashboard.html", methods=["GET", "POST"])
def PatientDashboard():
Username = (request.args.get("Username"))
Connection = sqlite3.connect(DB)
Cursor = Connection.cursor()
Data = Cursor.execute("""
SELECT *
FROM PatientTable
WHERE Username = '{}'
""".format(Username))
Data = Data.fetchall()
AllAppointments = Cursor.execute("""
SELECT Title, Firstname, Surname, TimeSlot, Date, Status
FROM AppointmentTable
INNER JOIN DoctorTable ON AppointmentTable.DoctorID = DoctorTable.DoctorID
WHERE PatientID = '{}'
""".format(Data[0][0]))
AllAppointments = AllAppointments.fetchall()
The SQL statements work perfectly (database isn't empty) and when adding print(Data) after the first SQL statement there is an output of a nested list.
I have tried troubleshooting by looking at various other questions on stackoverflow but with no luck.
Thank you ever so much in advance.
EDIT 1:
Username = (request.args.get("Username"))
print("Username: ", Username)
Gives the correct output, e.g. Username: nx_prv but after using the POST request the output becomes Username: None.
EDIT 2:
I have managed to fix this using flask.sessions. The problem was that the request.args.get("Username") was getting 'reset' every time.
The scenario I envision: the route was tested with a GET method (because there was not methods argument), and everything was fine. The methods argument was added so a POST could be tested, and it "stopped working". But it really didn't stop working, it's just not built to handle a POST request.
From flask doc on request object the two salient attributes are:
form
A MultiDict with the parsed form data from POST or PUT requests. Please keep in mind that file uploads will not end up here, but
instead in the files attribute.
args
A MultiDict with the parsed contents of the query string. (The part in the URL after the question mark).
So a GET request will "populate" args and a POST request, form. Username will be None from this line Username = (request.args.get("Username")) on a POST request.
You can determine which method by interrogating the method attribute of the request object.
method
The current request method (POST, GET etc.)

Querying Cloud Datastore - Returning all submissions from currently logged in user

I am trying to retrieve all entities that a user has submitted and have tried to do this by using the user's email address as the filter, but I receive this error when querying the datastore:
ValueError: Name 'test#example.com' cannot contain period characters
Query:
email = users.get_current_user().email()
q = WorkRequest.query().filter(email)
results = q.fetch(10)
Can anyone help?
You have not structured the query properly. Try this:
mail = users.get_current_user().email()
q = WorkRequest.query(WorkRequest.email == mail)
results = q.fetch(10)

Reading page's messages with Python Facebook SDK

Basically i need to get all messages of a page using facebook SDK in python.
Following some tutorial i arrived to this point:
import facebook
def main():
cfg = {
"page_id" : "MY PAGE ID",
"access_token" : "LONG LIVE ACCESS TOKEN"
}
api = get_api(cfg)
msg = "Hre"
status = api.put_wall_post(msg) #used to post to wall message Hre
x = api.get_object('/'+str(MY PAGE ID)+"/conversations/") #Give actual conversations
def get_api(cfg):
graph = facebook.GraphAPI(cfg['access_token'])
resp = graph.get_object('me/accounts')
page_access_token = None
for page in resp['data']:
if page['id'] == cfg['page_id']:
page_access_token = page['access_token']
graph = facebook.GraphAPI(page_access_token)
return graph
if __name__ == "__main__":
main()
The first problem is that api.get_object('/'+str(MY PAGE ID)+"/conversations/")returns a dictionary containing many informations, but what i would like to see is the messages they sent to me, while for now it print the user id that sent to me a message.
The output look like the following:
{u'paging': {u'next': u'https://graph.facebook.com/v2.4/571499452991432/conversations?access_token=Token&limit=25&until=1441825848&__paging_token=enc_AdCqaKAP3e1NU9MGSsvSdzDPIIDtB2ZCe2hCYfk7ft5ZAjRhsuVEL7eFYOOCdQ8okvuhZA5iQWaYZBBbrZCRNW8uzWmgnKGl69KKt4catxZAvQYCus7gZDZD', u'previous': u'https://graph.facebook.com/v2.4/571499452991432/conversations?access_token=token&limit=25&since=1441825848&__paging_token=enc_AdCqaKAP3e1NU9MGSsvSdzDPIIDtB2ZCe2hCYfk7ft5ZAjRhsuVEL7eFYOOCdQ8okvuhZA5iQWaYZBBbrZCRNW8uzWmgnKGl69KKt4catxZAvQYCus7gZDZD&__previous=1'}, u'data': [{u'link': u'/communityticino/manager/messages/?mercurythreadid=user%3A1055476438&threadid=mid.1441825847634%3Af2e0247f54f5c4d222&folder=inbox', u'id': u't_mid.1441825847634:f2e0247f54f5c4d222', u'updated_time': u'2015-09-09T19:10:48+0000'}]}
which is basically paging and data.
Given this is there a way to read the conversation?
In order to get the messages content you need first to request the single messages in the conversation, accessible with the 'id' field in the dictionary you copied, result of
x = api.get_object('/'+str(MY PAGE ID)+"/conversations/") #Give actual conversations
you can request the messages in the conversation by calling
msg = api.get_object('/'+<message id>)
Here it gets tricky, because following the graph api documentation you should receive back a dictionary with ALL the possible fields, including the 'message' (content) field. The function however returns only the fields 'created_time' and 'id'.
Thanks to this other question Request fields in Python Facebook SDK I found that you can request for those fields by adding a dict with such fields specified in the arguments of the graph.get_object() function. As far as I know this is undocumented in the facebook sdk reference for python.
The correct code is
args = {'fields' : 'message'}
msg = api.get_object('/'+<message id>, **args)
Similar question: Read facebook messages using python sdk

How to get page access token

I create app in facebook and page in my profile. In "Select how your app integrates with Facebook" section I don't select any option because I want only post text to facebook page (maybe this is problem?).
I have this code:
FACEBOOK_APP_ID = 'myappid'
FACEBOOK_APP_SECRET = 'myappsecret'
FACEBOOK_PROFILE_ID = 'myprofileid'
oauth_args = dict(client_id = FACEBOOK_APP_ID,
client_secret = FACEBOOK_APP_SECRET,
scope = 'publish_stream',
grant_type = 'client_credentials'
)
oauth_response = urllib.urlopen('https://graph.facebook.com/oauth/access_token?' + urllib.urlencode(oauth_args)).read()
oauth_response looks good
but when I run:
resp = urllib.urlopen('https://graph.facebook.com/me/accounts?'+oauth_response).read()
I get error:
{"error":{"message":"An active access token must be used to query information about the current user.","type":"OAuthException","code":2500}}
What am I doing wrong? I want to post on page wall some text when, for example, I click button on my website (Django).
UPDATE:
Ok, I get the pages data in json. I parsing it and I get page_access_token, but when I call this:
attach = {
"name": 'Hello world',
"link": 'http://linktosite',
"caption": 'test post',
"description": 'some test'
}
facebook_graph = facebook.GraphAPI(page_access_token)
try:
response = facebook_graph.put_wall_post('', attachment=attach)
except facebook.GraphAPIError as e:
print e
I get error: "The target user has not authorized this action"
This question is basically asking about the same problem, and the answer seems to be what you're looking for: (OAuthException - #2500) An active access token must be used to query information about the current user
If the page_access_token is correct, I guess you (the page admin) have not yet granted permission for your facebook application to post message to facebook page.
Check facebook login function in client side, whether you ask enough permission, the scope option should be 'mange_pages publish_stream photo_upload...' depends on your requirement, rather than only 'mange_pages'

Categories

Resources