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

# Source Code for Module tlib.base.JiraModerator

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