def get_error_list(r): error_list = [] if r.status_code >= 400: if r.status_code == 403 and "x-authentication-denied-reason" in r.headers: error_list = [r.headers["x-authentication-denied-reason"]] elif r.text: try: response = json_loads(r) if 'message' in response: # JIRA 5.1 errors error_list = [response['message']] elif 'errorMessages' in response and len(response['errorMessages']) > 0: # JIRA 5.0.x error messages sometimes come wrapped in this array # Sometimes this is present but empty errorMessages = response['errorMessages'] if isinstance(errorMessages, (list, tuple)): error_list = errorMessages else: error_list = [errorMessages] elif 'errors' in response and len(response['errors']) > 0: # JIRA 6.x error messages are found in this array. error_list = response['errors'].values() else: error_list = [r.text] except ValueError: error_list = [r.text] return error_list
def _load( self, url: str, headers=CaseInsensitiveDict(), params: Optional[Dict[str, str]] = None, path: Optional[str] = None, ): """Load a resource. Args: url (str): url headers (Optional[CaseInsensitiveDict]): headers. Defaults to CaseInsensitiveDict(). params (Optional[Dict[str,str]]): params to get request. Defaults to None. path (Optional[str]): field to get. Defaults to None. Raises: ValueError: If json cannot be loaded """ r = self._session.get(url, headers=headers, params=params) try: j = json_loads(r) except ValueError as e: logging.error(f"{e}:\n{r.text}") raise e if path: j = j[path] self._parse_raw(j)
def get_project_templates(self): url = self.manager._options[ 'server'] + '/rest/project-templates/latest/templates' response = self.manager._session.get(url) json_data = json_loads(response) return _get_template_list(json_data)
def request_types(manager, service_desk, project_key=None, strange_setting=None): """We need to use this function because in the old Jira version issueTypeId field does not exist.""" types = manager.request_types(service_desk) if len(types) and not hasattr(types[0], 'issueTypeId'): if hasattr(service_desk, 'id'): service_desk = service_desk.id url = manager._options[ 'server'] + '/rest/servicedesk/%s/servicedesk/%s/groups/%s/request-types' % ( strange_setting, project_key.lower(), service_desk, ) headers = {'X-ExperimentalApi': 'opt-in'} r_json = json_loads(manager._session.get(url, headers=headers)) types = [ RequestType(manager._options, manager._session, raw_type_json) for raw_type_json in r_json ] list(map(lambda t: setattr(t, 'issueTypeId', t.issueType), types)) return types
def _upload_file(manager, issue, upload_file, filename): # This method will fix original method jira.JIRA.add_attachment (jira/client.py line 591) url = manager._get_url('issue/' + str(issue) + '/attachments') files = { 'file': (filename, upload_file), } headers = { 'X-Atlassian-Token': 'no-check', } req = Request('POST', url, headers=headers, files=files, auth=manager._session.auth) prepped = req.prepare() prepped.body = re.sub(b'filename=.*', b'filename="%s"\r' % filename.encode('utf-8'), prepped.body) r = manager._session.send(prepped) js = utils.json_loads(r) if not js or not isinstance(js, collections.Iterable): raise JIRAError("Unable to parse JSON: %s" % js) attachment = Attachment(manager._options, manager._session, js[0]) if attachment.size == 0: raise JIRAError("Added empty attachment?!: r: %s\nattachment: %s" % (r, attachment)) return attachment
def download_worklogs(jira_connection, issue_ids): print('downloading jira worklogs... ', end='', flush=True) updated = [] since = 0 while True: worklog_ids_json = jira_connection._get_json('worklog/updated', params={'since': since}) updated_worklog_ids = [v['worklogId'] for v in worklog_ids_json['values']] resp = jira_connection._session.post( url=jira_connection._get_url('worklog/list'), data=json.dumps({'ids': updated_worklog_ids}), ) try: worklog_list_json = json_loads(resp) except ValueError: print("Couldn't parse JIRA response as JSON: %s", resp.text) raise updated.extend([wl for wl in worklog_list_json if int(wl['issueId']) in issue_ids]) if worklog_ids_json['lastPage']: break since = worklog_ids_json['until'] print('✓') return {'existing': updated, 'deleted': []}
def get_error_list(r): error_list = [] if r.status_code >= 400: if r.status_code == 403 and "x-authentication-denied-reason" in r.headers: error_list = [r.headers["x-authentication-denied-reason"]] elif r.text: try: response = json_loads(r) if 'message' in response: # JIRA 5.1 errors error_list = [response['message']] elif 'errorMessages' in response and len( response['errorMessages']) > 0: # JIRA 5.0.x error messages sometimes come wrapped in this array # Sometimes this is present but empty errorMessages = response['errorMessages'] if isinstance(errorMessages, (list, tuple)): error_list = errorMessages else: error_list = [errorMessages] elif 'errors' in response and len(response['errors']) > 0: # JIRA 6.x error messages are found in this array. error_list = response['errors'].values() else: error_list = [r.text] except ValueError: error_list = [r.text] return error_list
def get_error_list(r: Response) -> List[str]: error_list = [] if r.status_code >= 400: if r.status_code == 403 and "x-authentication-denied-reason" in r.headers: error_list = [r.headers["x-authentication-denied-reason"]] elif r.text: try: response: Dict[str, Any] = json_loads(r) if "message" in response: # Jira 5.1 errors error_list = [response["message"]] elif "errorMessages" in response and len( response["errorMessages"]) > 0: # Jira 5.0.x error messages sometimes come wrapped in this array # Sometimes this is present but empty errorMessages = response["errorMessages"] if isinstance(errorMessages, (list, tuple)): error_list = list(errorMessages) else: error_list = [errorMessages] elif "errors" in response and len(response["errors"]) > 0: # Jira 6.x error messages are found in this array. error_list = response["errors"].values() else: error_list = [r.text] except ValueError: error_list = [r.text] return error_list
def _load(self, url, headers=CaseInsensitiveDict(), params=None, path=None): r = self._session.get(url, headers=headers, params=params) try: j = json_loads(r) except ValueError as e: logging.error("%s:\n%s" % (e, r.text)) raise e if path: j = j[path] self._parse_raw(j)
def _load(self, url, headers=CaseInsensitiveDict(), params=None, path=None): r = self._session.get(url, headers=headers, params=params) try: j = json_loads(r) except ValueError as e: logging.error("%s:\n%s" % (e, r.text)) raise e if path: j = j[path] self._parse_raw(j)
def create_customer_request(self, fields=None, prefetch=True, use_old_api=False, **fieldargs): """The code for this function is almost completely copied from function create_customer_request of the JIRA library""" data = fields p = data['serviceDeskId'] service_desk = None if isinstance(p, str) or isinstance(p, int): service_desk = self.service_desk(p) elif isinstance(p, ServiceDesk): service_desk = p data['serviceDeskId'] = service_desk.id p = data['requestTypeId'] if isinstance(p, int): data['requestTypeId'] = p elif isinstance(p, str): data['requestTypeId'] = self.request_type_by_name(service_desk, p).id requestParticipants = data.pop('requestParticipants', None) url = self._options['server'] + '/rest/servicedeskapi/request' headers = {'X-ExperimentalApi': 'opt-in'} r = self._session.post(url, headers=headers, data=json.dumps(data)) raw_issue_json = json_loads(r) if 'issueKey' not in raw_issue_json: raise JIRAError(r.status_code, request=r) if requestParticipants: url = (self._options['server'] + '/rest/servicedeskapi/request/%s/participant' % raw_issue_json['issueKey']) headers = {'X-ExperimentalApi': 'opt-in'} if use_old_api: data = {'usernames': requestParticipants} else: data = {'accountIds': requestParticipants} r = self._session.post(url, headers=headers, json=data) if r.status_code != status.HTTP_200_OK: raise JIRAError(r.status_code, request=r) if prefetch: return self.issue(raw_issue_json['issueKey']) else: return Issue(self._options, self._session, raw=raw_issue_json)
def _add_comment(self, issue, body, is_internal): data = { 'body': body, 'properties': [{'key': 'sd.public.comment', 'value': {'internal': is_internal}}, ] } url = self.manager._get_url('issue/{0}/comment'.format(issue)) response = self.manager._session.post(url, data=json.dumps(data)) comment = Comment(self.manager._options, self.manager._session, raw=json_loads(response)) return comment
def create_webhook(self, name=None, url=None, events=None, jql='', exclude_body=False): assert name and url and events data = {'name': name, 'url': url, 'events': events, 'jqlFilter': jql, 'excludeIssueDetails': exclude_body, } url = self.client._get_url('webhook', base=self.webhook_base_path) response = self.client._session.post(url, data=json.dumps(data)) return json_loads(response)
def update_board_name(self, id, name): payload = { 'id': id, 'name': name, } url = self._get_url( 'rapidviewconfig/name', base=self.AGILE_BASE_URL ) response = self._session.put( url, data=json.dumps(payload) ) return json_loads(response)
def _download_jira_issues_page( jira_connection, jira_issue_ids_segment, field_spec, start_at, batch_size ): ''' Returns a tuple: (issues_downloaded, num_issues_apparently_deleted) ''' get_changelog = True while batch_size > 0: search_params = { 'jql': f"id in ({','.join(str(iid) for iid in jira_issue_ids_segment)}) order by id asc", 'fields': field_spec, 'expand': ['renderedFields'], 'startAt': start_at, 'maxResults': batch_size, } if get_changelog: search_params['expand'].append('changelog') try: resp_json = json_loads( jira_connection._session.post( url=jira_connection._get_url('search'), data=json.dumps(search_params) ) ) return _expand_changelog(resp_json['issues'], jira_connection), 0 except (json.decoder.JSONDecodeError, JIRAError) as e: if hasattr(e, 'status_code') and e.status_code == 429: # This is rate limiting ("Too many requests") raise batch_size = int(batch_size / 2) agent_logging.log_and_print_error_or_warning( logger, logging.WARNING, msg_args=[e, batch_size], error_code=3052, exc_info=True, ) if batch_size == 0: if re.match(r"A value with ID .* does not exist for the field 'id'", e.text): return [], 1 elif not get_changelog: agent_logging.log_and_print_error_or_warning( logger, logging.WARNING, msg_args=[search_params], error_code=3062, ) return [], 0 else: get_changelog = False batch_size = 1
def _load(self, url, headers=CaseInsensitiveDict(), params=None, path=None): """ Load a resource. :type url: str :type headers: CaseInsensitiveDict :type params: Optional[Dict[str,str]] :type path: Optional[str] """ r = self._session.get(url, headers=headers, params=params) try: j = json_loads(r) except ValueError as e: logging.error("%s:\n%s" % (e, r.text)) raise e if path: j = j[path] self._parse_raw(j)
def create_customer(self, email, displayName): """Create a new customer and return an issue Resource for it.""" url = self._options['server'] + '/rest/servicedeskapi/customer' headers = {'X-ExperimentalApi': 'opt-in'} r = self._session.post( url, headers=headers, data=json.dumps({ 'email': email, 'fullName': displayName, # different property for the server one }), ) raw_customer_json = json_loads(r) if r.status_code != 201: raise JIRAError(r.status_code, request=r) return Customer(self._options, self._session, raw=raw_customer_json)
def create_shared(self, key=None, name=None, shared_key=None, lead=None): assert key and name and shared_key # There is no public method for creating a shared project: # https://jira.atlassian.com/browse/JRA-45929 # People found a private method for doing so, which is explained on: # https://jira.atlassian.com/browse/JRA-27256?src=confmacro&_ga=1.162710906.750569280.1479368101 try: project = self.read(shared_key) project_id = project['id'] except JIRAError as err: if err.status_code == 404: raise exceptions.UserError( _('Project template with key "%s" not found.') % shared_key) else: raise url = (self.client._options['server'] + '/rest/project-templates/1.0/createshared/%s' % project_id) payload = { 'name': name, 'key': key, 'lead': lead, } r = self.client._session.post(url, data=json.dumps(payload)) if r.status_code == 200: r_json = json_loads(r) return r_json f = tempfile.NamedTemporaryFile( suffix='.html', prefix='python-jira-error-create-shared-project-', delete=False) f.write(r.text) if self.logging: logging.error( "Unexpected result while running create shared project." "Server response saved in %s for further investigation " "[HTTP response=%s]." % (f.name, r.status_code)) return False
def download_worklogs(jira_connection): print(f'downloading worklogs... ', end='', flush=True) updated = [] while True: worklog_ids_json = jira_connection._get_json('worklog/updated', params={'since': 0}) updated_worklog_ids = [ v['worklogId'] for v in worklog_ids_json['values'] ] resp = jira_connection._session.post( url=jira_connection._get_url('worklog/list'), data=json.dumps({'ids': updated_worklog_ids})) try: worklog_list_json = json_loads(resp) except ValueError as e: logger.exception("Couldn't parse JIRA response as JSON: %s", resp.text) raise e updated.extend(worklog_list_json) if worklog_ids_json['lastPage']: break print('✓') print(f'Finding old worklogs that have been deleted... ', end='', flush=True) deleted = [] while True: worklog_ids_json = jira_connection._get_json('worklog/deleted', params={'since': 0}) deleted.extend(worklog_ids_json['values']) if worklog_ids_json['lastPage']: break print('✓') return { 'existing': updated, 'deleted': deleted, }
def create_poker_session( self, board_id: int, name: str, issues: list[str], participants: list[str], scrum_masters: list[str], send_invitations: bool = True, ) -> Poker: """ Create a new agile poker session. :param board_id: The board to get sessions from. :param name: Name of the session. :param scrum_masters: A list of participants with scrum masters permissions. :param participants: A list of standard participants of the session. :param issues: A list of issues to be estimated in a session. :param send_invitations: Whether or not to notify participants about the update via email. (Default: True). :return: A new session. """ response = self._session.post( url=self._get_url(f'session/async?boardId={board_id}', self.AGILE_POKER_URL), headers={'content-type': 'application/json'}, data=json.dumps({ 'estimationFieldId': self.issue_fields[settings.JIRA_FIELDS_STORY_POINTS], 'invitationMessage': settings.SPRINT_ASYNC_POKER_NEW_SESSION_MESSAGE, 'name': name, 'issueIds': issues, 'participantsUserKeys': participants, 'scrumMastersUserKeys': scrum_masters, 'sendInvitations': send_invitations, }), ) return Poker(self._options, self._session, json_loads(response))
def close_poker_session(self, session_id: int, send_notifications: bool = True) -> Poker: """ Close an agile poker session. :param session_id: The ID of the session to close. :param send_notifications: Whether or not to notify participants about the update via email. (Default: True). :return: A closed session. """ response = self._session.put( url=self._get_url(f'session/async/{session_id}/rounds/latest', self.AGILE_POKER_URL), headers={'content-type': 'application/json'}, data=json.dumps({ 'closeRound': True, 'sendCloseNotifications': send_notifications, }), ) return Poker(self._options, self._session, json_loads(response))
def update_swimlane(self, board_id, swimlane_id, name=None, query=None, description=None, is_default=None): payload = { 'id': swimlane_id, } if name: payload['name'] = name if query: payload['query'] = query if description: payload['description'] = description if is_default is not None: payload['isDefault'] = is_default url = self._get_url( 'swimlanes/{}/{}'.format(board_id, swimlane_id), base=self.AGILE_BASE_URL ) response = self._session.put( url, data=json.dumps(payload) ) return json_loads(response)
def createIssue(self, content, data, client): """ JIRA create issue API is not working, use the below code to replace """ url = 'https://jiradc.ext.net.nokia.com/secure/QuickCreateIssue.jspa?decorator=none' pid = client.project(data.get('project')).id issuetype_id = client.issue_type_by_name(data.get('issuetype')).id token = client._session.cookies.values()[1] content['pid'] = pid content['issuetype'] = issuetype_id content['atl_token'] = token headers = { 'Origin': 'https://jiradc.ext.net.nokia.com', 'X-AUSERNAME': '******', 'Connection': 'keep-alive', 'Accept-Language': 'zh-CN,zh;q=0.8,zh-TW;q=0.7,zh-HK;q=0.5,en-US;q=0.3,en;q=0.2', 'Referer': 'https://jiradc.ext.net.nokia.com/secure/RapidBoard.jspa?rapidView=13285', 'X-Requested-With': 'XMLHttpRequest', 'Content-Type': 'application/x-www-form-urlencoded; charset=UTF-8', 'Accept': '*/*', 'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0' } url_labels = self.combine_url_labels(url, data) response = client._session.post(url_labels, data=content, headers=headers) return json_loads(response)['issueKey']
def delete_webhook(self, id_): url = self.client._get_url('webhook/%s' % id_, base=self.webhook_base_path) return json_loads(self.client._session.delete(url))