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