Monday, September 5, 2016

Adding an external file to JIRA

As a part of my companies personal project time, a yearly occurrance they call Off The Grid, I was part of a project to add external files to a JIRA ticket.  JIRA is used to track EVERYTHING in the company.

This is the python script I hobbled together.

#!/usr/bin/env python

from stat import S_ISREG, ST_CTIME, ST_MODE
import os,sys,re,requests
from requests.auth import HTTPBasicAuth
import urllib3
urllib3.disable_warnings()
def openFile(location,key,filetype,fileKey):
    """
    Tests that the file location exists and searches through each file for a specific keyword
    :param location:
    :param key:
    :param filetype:
    :param fileKey
    :return: outputFile:
    """
    outputFile = filetype + "output.txt"
    try:
        os.remove(outputFile)
    except OSError:
        pass
    f = open(outputFile,'w')
    print >> f, "Looking in %s" % location
    for dirpath, dirnames, files in os.walk(location):
        if not files:
            print dirpath, 'is empty'
            exit()
    entriesf = (os.path.join(location, fn) for fn in os.listdir(location))
    entries = ((os.stat(path), path) for path in entriesf)
    entries = ((stat[ST_CTIME], path)
               for stat, path in entries if S_ISREG(stat[ST_MODE]))
    # for cdate, path in sorted(entries):
    #    print time.ctime(cdate), os.path.basename(path)
    check_file = re.compile(fileKey)
    for file in entriesf:
        if os.path.isfile(file): # and check_file.search(file):
            for i,line in enumerate(open(file)):
                #if re.finditer(key,line):
                match = re.search(key,line)
                if match:
                    print >> f, "Found in %s \n %s" % (file, line)
                else:
                    print >> f, "No entries that matched keyword %s" % fileKey
        else:
            print >> f, "No files that matched %s" % fileKey
    f.close()
    return outputFile

def addSqlTraceToJira(location,ticket):
    """
    Add a SQL Trace file to the Jira Ticket
    :param location:
    :param ticket:
    :return:
    """
    entriesf = (os.path.join(location, fn) for fn in os.listdir(location))
    check_file = re.compile('trc$')
    check_ticket = re.compile(ticket)
    for file in entriesf:
        if os.path.isfile(file) and check_file.search(file) and check_ticket.search(file):
            attachFileToJira(file,ticket)
        else:
            print "No files found with %s" % ticket
def attachFileToJira(logfile,ticket):
    """
    Attach the output file to the Jira ticket
    :param logfile:
    :param ticket:
    :return:
    """
    url = "https://jira-test.fakedomain.comnet/rest/api/2/issue/%s/attachments" % ticket
    headers = {'X-Atlassian-Token':'no-check'}
    files = {'file': open(logfile, 'r')}
    auth = HTTPBasicAuth('username', 'password')
    # print "Sending %s to ticket %s" % (logfile,url)
    r = requests.post(url,headers=headers,files=files,auth=auth,verify=False)
    print 'Jira file attach sent me a %s with \n %s' % (r.status_code,r.text)

def addCommentToJira(keyword,ticket):
    """
    This attaches a comment about the search parameter that was added to the ticket
    :param keyword:
    :param tickiet:
    :return:
    """
    url = "https://jira-test.fakedomain.comnet/rest/api/2/issue/%s/comment" % ticket
    headers = {'X-Atlassian-Token': 'no-check'}
    auth = HTTPBasicAuth('username', 'password')
    comment = "Just added an attached log file about the results for the %s search." % keyword
    json = {"body": comment}
    r = requests.post(url,headers=headers,json=json,auth=auth,verify=False)
    print 'Jira adding a comment sent me a %s with \n %s' % (r.status_code, r.text)

def main():
    if len(sys.argv) < 5:
        from os.path import basename
        print ('Usage: %s location key type ticket' % basename(__file__))
    else:
        # fileLocation
        LOCATION = sys.argv[1]
        # searchKey
        SEARCHKEY = sys.argv[2]
        # type
        FILETYPE = sys.argv[3]
        # Jira Ticket
        TICKET = sys.argv[4]
        # File Keyword
        FILEKEY = sys.argv[5]
        #timeStart
        #TIMESTART = sys.argv[3]
        #timeend
        #TIMEEND = sys.argv[4]
    # logfile = openFile(LOCATION,SEARCHKEY,FILETYPE,FILEKEY)
    # attachFileToJira(logfile,TICKET)
    # addCommentToJira(SEARCHKEY,TICKET)
    addSqlTraceToJira(LOCATION,TICKET)
if __name__ == '__main__':
    main()

I claim some responsibility for it, mostly by hacking together a lot of python and doing a lot of TDD as I built out the functions.


Friday, July 15, 2016

Robot Framework and RESTful APIs

Been awhile.  Yup, it has.  Just a short one this time, more to get something down that vexed me for a little bit.

Dealing with a RESTful API that returned JSON in the body to my Robot Framework test caused me no end of grief.  Since the response always came back with the following:
\xef\xbb\xbf{
        "account_organizations": [
Originally I thought, it's just a JSON response, I can deal with it.

Oh no.

Not at all.

Turns out what I was getting ended up being byte order markers that were messing up the JSON compare I was trying to do.  Knowing what I was to get back was one thing, comparing that with the body of the response was another thing since the byte order markers did not behave like a regular list as I thought it was.  This turned out to be a dict, since Robot Framework basically is Python we have to be Pythonic here.

So, treating that as a dict we get the 3rd index from the list:
${l_response_body[3:]}
THAT now pulls the right values for comparison.

I also run this against a make unicode Python library, just in case.  So in Robot I use the following to cover it all:

${l_response_body} Get Response Body
${l_decoded_body} = make_unicode        ${l_response_body[3:]}

So all is good with the world, for now.