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