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):
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 126 @return: a dictionary containing the required fields and None values for each. Returns empty dict if 127 search failed. 128 @rtype: dict 129 """ 130 131 req_fields = {} 132 133 try: 134 # Get a list of fields that are required to supply values for so that an issue can be created 135 meta = self.client.createmeta(projectKeys=project_key, expand='projects.issuetypes.fields') 136 137 fields = meta['projects'][0]['issuetypes'][0]['fields'] 138 139 for field in fields: 140 if fields[field]['required']: 141 req_fields[field] = None 142 143 except JIRAError, e: 144 self.logger.error("Could not get required fields for Jira project " + project_key + " because " + str(e)) 145 except IndexError, e: 146 self.logger.error("Could not get required fields for Jira project " + project_key + " because " + str(e)) 147 148 return req_fields
149
150 - def create_issue(self, data):
151 """ 152 Creates an issue in Jira with the dictionary of data supplied. Be sure that data contains all required 153 fields before using this. 154 155 @param data: dictionary of required fields and valid values 156 @type data: dict 157 158 @return: returns True if issues was created and False if there was a failure 159 @rtype: bool 160 """ 161 162 try: 163 self.client.create_issue(fields=data) 164 success = True 165 166 except JIRAError, e: 167 success = False 168 self.logger.error("Issue was not created :" + str(e)) 169 170 return success
171
172 - def reopen_bug(self, issue, proj_key, version):
173 """ 174 Transitions a specified jira Bug from any resolved state back to In Review and assigns it to the 175 project lead with comments 176 177 @param issue: an issue object that came from Jira. Use searchForIssue first before reopening issues 178 @type issue: issue 179 @param proj_key: the Jira project acronym from Jira 180 @type proj_key: str 181 @param version: the build number currently under test where the bug was rediscovered 182 @type version: str 183 184 @return: returns False if we could not reopen issue and True if we could 185 @rtype: bool 186 """ 187 cur_state = issue.fields.status.name 188 189 transitions = self.client.transitions(issue) 190 191 project = self.client.project(proj_key) 192 proj_lead = project.lead.name 193 194 version = version 195 196 comment = "This issue has reoccured in the latest version %s" % version 197 198 try: 199 if cur_state == "Closed": 200 self.client.transition_issue(issue, self.get_transition_id(transitions, "Re-Open"), 201 assignee={'name': proj_lead}) 202 self.client.add_comment(issue, comment) 203 elif cur_state == "Ready for QA": 204 self.client.transition_issue(issue, self.get_transition_id(transitions, "Back to In Development"), 205 assignee={'name': proj_lead}) 206 self.client.add_comment(issue, comment) 207 elif cur_state == "In Testing": 208 self.client.transition_issue(issue, self.get_transition_id(transitions, "Fail"), 209 assignee={'name': proj_lead}) 210 self.client.add_comment(issue, comment) 211 elif cur_state == "Ready to Deploy": 212 self.client.transition_issue(issue, self.get_transition_id(transitions, "Back to In Testing")) 213 transitions = self.client.transitions(issue) 214 self.client.transition_issue(issue, self.get_transition_id(transitions, "Fail"), 215 assignee={'name': proj_lead}) 216 self.client.add_comment(issue, comment) 217 except IndexError: 218 self.logger.error("Could not find a transition to reopen the issue '" + issue.key + "'") 219 return False 220 except JIRAError, e: 221 self.logger.error("Jira returned error when modifying issue '" + issue.key + "' because " + str(e)) 222 return False 223 224 return True
225 226 #noinspection PyMethodMayBeStatic
227 - def get_transition_id(self, trans_dict, trans_name):
228 """ 229 Fetch the id for a transition's name 230 231 @param trans_dict: a dictionary of transitions fetched from Jira for a given issue 232 @type trans_dict: dict 233 @param trans_dict: name of the Jira transition you would like the id for 234 @type trans_name: str 235 236 @return: a numeric id associtated to the transition name 237 @rtype: str 238 """ 239 240 id_dict = [element['id'] for element in trans_dict if element['name'] == trans_name] 241 return id_dict[0]
242 243 #noinspection PyMethodMayBeStatic
244 - def prepare_issue_data(self, req_fields, test_id, project, summary, description, component, severity, version):
245 """ 246 Constructs a properly formatted dictionary of data to supply to Jira for opening an issue. Creates a bug 247 in the specified project After construction, it will validate the dictionary by checking if all required 248 fields are filled 249 250 @param req_fields: dictionary of jira project required fields. Construct the dict with getRequiredFields 251 @type req_fields: dict 252 @param test_id: the name of the test cases found in the test case's decoreator in python or the name in Spiratest 253 @type test_id: str 254 @param project: the Jira project acronym for the project you wish to open a bug inspect 255 @type project: str 256 @param summary: the summary of the bug you wish to open 257 @type summary: str 258 @param description: the description of the bug you wish to open 259 @type description: str 260 @param component: the component of the bug you wish to open 261 @type component: str 262 @param severity: the severity of the bug you wish to open 263 @type severity: str 264 @param version: the affected version of the bug you wish to open 265 @type version: str 266 267 @return: if the dictionary properly complies to all the required fields. 268 Returns emtpy dict if it does not. 269 @rtype: dict 270 """ 271 desc = '''This issue was created by YPG automated Test Case : %(test_id)s. \n \n The error is caused when 272 sending the following parameters to the API method in question : \n %(description)s''' % \ 273 {'description': description, 'test_id': test_id} 274 275 req_fields['project'] = {'key': project} 276 req_fields['summary'] = summary 277 req_fields['description'] = desc 278 req_fields['issuetype'] = {'name': 'Bug'} 279 req_fields['customfield_10411'] = {'value': severity} 280 req_fields['components'] = [{'name': component}] 281 req_fields['versions'] = [{'name': version}] 282 283 # if any of the required fields are not filled erase the dict 284 if None in req_fields.values(): 285 req_fields = {} 286 287 return req_fields
288