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