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