Package tlib :: Package base :: Module JiraModerator |
[hide private]
[frames] | no frames]

# Source Code for Module tlib.base.JiraModerator

      1  from jira.client import JIRA
      2  from jira.exceptions import JIRAError
      3  from requests import ConnectionError, HTTPError
      4
      5
      6 -class JiraModerator(object):
      7      """
      8      Helper class to connect and manipulate the data in Jira
      9      """
     10
     11      client = None
     12      clientOptions = {'server': 'https://issues.ypg.com'}    #: conneciton options for Jira
     13      username = "ypgAutoTester"                              #: Automation User username (QA Lead access to Jira)
     14      password = "@ut0t3$t3r"                                 #: Automation User password
     15      logger = None                                           #: logger to send loggin information to.
     16      project = None
     17
     18 -    def __init__(self, logger):
     19          """
     20          Constructor for class
     21
     22          Args :
     23          logger : (logger) instance of a logging object configured in testing project
     24
     25          """
     26          self.logger = logger
     27
     28 -    def connect(self):
     29          """
     30          Establishes a connection to our YPG Jira instance and assignes it to self.client
     31
     32          Returns : (bool) True if we are connected to Jira and False if we are not
     33
     34          """
     35
     36          try:
     37              self.client = JIRA(self.clientOptions, basic_auth=(self.username, self.password))
     38              self.client.session()
     39              success = True
     40
     41          except ConnectionError, e:
     42              self.logger.error("Error Connection to Jira :")
     43              self.logger.error(e)
     44              self.client = None
     45              success = False
     46          except HTTPError, e:
     47              self.logger.error("Error Connection to Jira :")
     48              self.logger.error(e)
     49              self.client = None
     50              success = False
     51
     52          return success
     53
     54 -    def search_for_issue(self, query):
     55          """
     56          Returns a list of issues that were returned by Jira given the query you specified
     57
     58          Args : (str) A string representation of a JQL query.  anything you can enter in Jira advanced search can be
     59                          entered here.
     60
     61          Returns : (ResultList) a list of jira issues
     62          """
     63          result_list = None
     64          try:
     65              result_list = self.client.search_issues(query)
     66
     67          except JIRAError, e:
     68              self.logger.error("Could not search what you are looking for because " + str(e))
     69
     70          if len(result_list) < 1:
     71              issues = None
     72          elif len(result_list) > 1:
     73              self.logger.warn(
     74                  '''Jira found more than one issue with the search %s . You may want to manually verify
     75                          the automated process updated the correct issue." % query)''')
     76              issues = result_list[0]
     77          else:
     78              issues = result_list[0]
     79          return issues
     80
     81 -    def confirm_version(self, proj_key, build_version):
     82          """
     83          Confirms that the project version supplied actually exists in Jira for the specified project
     84
     85          Args :
     86          projKey: (str) the Jira project acronym
     87          buildVersion : (str) the version of the application you are testing
     88
     89          Returns : (bool) True if the version was found in Jira.  False if the version was not found
     90          """
     91
     92          try:
     93              # get all the versions for the specified project from Jira
     94              project = self.client.project(proj_key)
     95              proj_versions = project.versions
     96
     97              # search for the version we are testing.  Is it in Jira?
     98              for version in proj_versions:
     99
    100                  if str(version.name) == str(build_version):
    101                      self.logger.debug("Matched the specidied buildVersion runtime parameter to version in Jira")
    102                      if not version.released and not version.archived:
    103                          self.logger.debug(
    104                              "We are going to start to test build version " + version.name + " for project " + proj_key)
    105                          return True
    106                      else:
    107                          self.logger.warn(
    108                              '''The buildVersion you are searching for has been released or archived in Jira
    109                                 and is not a valid testable build version''')
    110                          return False
    111          except JIRAError:
    112              self.logger.error(
    113                  "Could not retrieve the projects versions.  Check that the project exists or that Jira is not down")
    114              return False
    115
    116 -    def get_required_fields(self, project_key):
    117          """
    118          Fetches a list of fields that require values to open a new issue in a specified project
    119
    120          Args : (str) The Jira project acronym for the project you are querying
    121
    122          Returns : (dict) a dictionary containing the required fields and None values for each.  Returns empty dict if
    123                              search failed.
    124          """
    125
    126          req_fields = {}
    127
    128          try:
    129              # Get a list of fields that are required to supply values for so that an issue can be created
    130              meta = self.client.createmeta(projectKeys=project_key, expand='projects.issuetypes.fields')
    131
    132              fields = meta['projects'][0]['issuetypes'][0]['fields']
    133
    134              for field in fields:
    135                  if fields[field]['required']:
    136                      req_fields[field] = None
    137
    138          except JIRAError, e:
    139              self.logger.error("Could not get required fields for Jira project " + project_key + " because " + str(e))
    140          except IndexError, e:
    141              self.logger.error("Could not get required fields for Jira project " + project_key + " because " + str(e))
    142
    143          return req_fields
    144
    145 -    def create_issue(self, data):
    146          """
    147          Creates an issue in Jira with the dictionary of data supplied.  Be sure that data contains all required
    148              fields before using this.
    149
    150          Args : (dict) dictionary of required fields and valid values
    151
    152          Returns : (bool) returns True if issues was created and False if there was a failure
    153          """
    154
    155          try:
    156              self.client.create_issue(fields=data)
    157              success = True
    158
    159          except JIRAError, e:
    160              success = False
    161              self.logger.error("Issue was not created :" + str(e))
    162
    163          return success
    164
    165 -    def reopen_bug(self, issue, proj_key, version):
    166          """
    167          Transitions a specified jira Bug from any resolved state back to In Review and assigns it to the
    168              project lead with comments
    169
    170          Args :
    171          issue : (issue) an issue object that came from Jira.  Use searchForIssue first before reopening issues
    172          proj_key : (str) the Jira project acronym from Jira
    173          version : (str) the build number currently under test where the bug was rediscovered
    174
    175          Returns : (bool) returns False if we could not reopen issue and True if we could
    176          """
    177          cur_state = issue.fields.status.name
    178
    179          transitions = self.client.transitions(issue)
    180
    181          project = self.client.project(proj_key)
    182          proj_lead = project.lead.name
    183
    184          version = version
    185
    186          comment = "This issue has reoccured in the latest version %s" % version
    187
    188          try:
    189              if cur_state == "Closed":
    190                  self.client.transition_issue(issue, self.get_transition_id(transitions, "Re-Open"),
    191                                               assignee={'name': proj_lead})
    192                  self.client.add_comment(issue, comment)
    193              elif cur_state == "Ready for QA":
    194                  self.client.transition_issue(issue, self.get_transition_id(transitions, "Back to In Development"),
    195                                               assignee={'name': proj_lead})
    196                  self.client.add_comment(issue, comment)
    197              elif cur_state == "In Testing":
    198                  self.client.transition_issue(issue, self.get_transition_id(transitions, "Fail"),
    199                                               assignee={'name': proj_lead})
    200                  self.client.add_comment(issue, comment)
    201              elif cur_state == "Ready to Deploy":
    202                  self.client.transition_issue(issue, self.get_transition_id(transitions, "Back to In Testing"))
    203                  transitions = self.client.transitions(issue)
    204                  self.client.transition_issue(issue, self.get_transition_id(transitions, "Fail"),
    205                                               assignee={'name': proj_lead})
    206                  self.client.add_comment(issue, comment)
    207          except IndexError:
    208              self.logger.error("Could not find a transition to reopen the issue '" + issue.key + "'")
    209              return False
    210          except JIRAError, e:
    211              self.logger.error("Jira returned error when modifying issue '" + issue.key + "' because " + str(e))
    212              return False
    213
    214          return True
    215
    216      #noinspection PyMethodMayBeStatic
    217 -    def get_transition_id(self, trans_dict, trans_name):
    218          """
    219          Fetch the id for a transition's name
    220
    221          Args :
    222          trans_dict : (dict) a dictionary of transitions fetched from Jira for a given issue
    223          trans_name : (str) name of the Jira transition you would like the id for
    224
    225          Returns : (str) a numeric id associtated to the transition name
    226          """
    227
    228          id_dict = [element['id'] for element in trans_dict if element['name'] == trans_name]
    229          return id_dict[0]
    230
    231      #noinspection PyMethodMayBeStatic
    232 -    def prepare_issue_data(self, req_fields, test_id, project, summary, description, component, severity, version):
    233          """
    234          Constructs a properly formatted dictionary of data to supply to Jira for opening an issue.  Creates a bug
    235              in the specified project After construction, it will validate the dictionary by checking if all required
    236              fields are filled
    237
    238          Args :
    239          req_fields : (dict) dictionary of jira project required fields.  Construct the dict with getRequiredFields
    240          test_id : (str)the name of the test cases found in the test case's decoreator in python or the name in Spiratest
    241          project : (str) the Jira project acronym for the project you wish to open a bug inspect
    242          summary : (str) the summary of the bug you wish to open
    243          description : (str) the description of the bug you wish to open
    244          component : (str) the component of the bug you wish to open
    245          severity : (str) the severity of the bug you wish to open
    246          version : (str) the affected version of the bug you wish to open
    247
    248          Returns : (dict) if the dictionary properly complies to all the required fields.
    249                              Returns emtpy dict if it does not.
    250
    251          """
    252          desc = '''This issue was created by YPG automated Test Case : %(test_id)s. \n \n The error is caused when
    253              sending the following parameters to the API method in question : \n %(description)s''' % \
    254              {'description': description, 'test_id': test_id}
    255
    256          req_fields['project'] = {'key': project}
    257          req_fields['summary'] = summary
    258          req_fields['description'] = desc
    259          req_fields['issuetype'] = {'name': 'Bug'}
    260          req_fields['customfield_10411'] = {'value': severity}
    261          req_fields['components'] = [{'name': component}]
    262          req_fields['versions'] = [{'name': version}]
    263
    264          # if any of the required fields are not filled erase the dict
    265          if None in req_fields.values():
    266              req_fields = {}
    267
    268          return req_fields
    269