openreplay/api/chalicelib/utils/jira_client.py
2022-04-06 16:48:26 +02:00

337 lines
13 KiB
Python

import time
from datetime import datetime
import requests
from jira import JIRA
from jira.exceptions import JIRAError
from requests.auth import HTTPBasicAuth
from starlette import status
from starlette.exceptions import HTTPException
fields = "id, summary, description, creator, reporter, created, assignee, status, updated, comment, issuetype, labels"
class JiraManager:
retries = 0
def __init__(self, url, username, password, project_id=None):
self._config = {"JIRA_PROJECT_ID": project_id, "JIRA_URL": url, "JIRA_USERNAME": username,
"JIRA_PASSWORD": password}
try:
self._jira = JIRA(url, basic_auth=(username, password), logging=True, max_retries=1)
except Exception as e:
print("!!! JIRA AUTH ERROR")
print(e)
raise e
def set_jira_project_id(self, project_id):
self._config["JIRA_PROJECT_ID"] = project_id
def get_projects(self):
try:
projects = self._jira.projects()
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.get_projects()
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
projects_dict_list = []
for project in projects:
projects_dict_list.append(self.__parser_project_info(project))
return projects_dict_list
def get_project(self):
try:
project = self._jira.project(self._config['JIRA_PROJECT_ID'])
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.get_project()
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
return self.__parser_project_info(project)
def get_issues(self, sql: str, offset: int = 0):
jql = "project = " + self._config['JIRA_PROJECT_ID'] \
+ ((" AND " + sql) if sql is not None and len(sql) > 0 else "") \
+ " ORDER BY createdDate DESC"
try:
issues = self._jira.search_issues(jql, maxResults=1000, startAt=offset, fields=fields)
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.get_issues(sql, offset)
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
issue_dict_list = []
for issue in issues:
# print(issue.raw)
issue_dict_list.append(self.__parser_issue_info(issue, include_comments=False))
# return {"total": issues.total, "issues": issue_dict_list}
return issue_dict_list
def get_issue(self, issue_id: str):
try:
# issue = self._jira.issue(issue_id)
issue = self._jira.issue(issue_id, fields=fields)
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.get_issue(issue_id)
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
return self.__parser_issue_info(issue)
def get_issue_v3(self, issue_id: str):
try:
url = f"{self._config['JIRA_URL']}/rest/api/3/issue/{issue_id}?fields={fields}"
auth = HTTPBasicAuth(self._config['JIRA_USERNAME'], self._config['JIRA_PASSWORD'])
issue = requests.get(
url,
headers={
"Accept": "application/json"
},
auth=auth
)
except Exception as e:
self.retries -= 1
if self.retries > 0:
time.sleep(1)
return self.get_issue_v3(issue_id)
print(f"=>Exception {e}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: get issue error")
return self.__parser_issue_info(issue.json())
def create_issue(self, issue_dict):
issue_dict["project"] = {"id": self._config['JIRA_PROJECT_ID']}
try:
issue = self._jira.create_issue(fields=issue_dict)
return self.__parser_issue_info(issue)
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.create_issue(issue_dict)
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
def close_issue(self, issue):
try:
# jira.transition_issue(issue, '5', assignee={'name': 'pm_user'}, resolution={'id': '3'})
self._jira.transition_issue(issue, 'Close')
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.close_issue(issue)
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
def assign_issue(self, issue_id, account_id) -> bool:
try:
return self._jira.assign_issue(issue_id, account_id)
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.assign_issue(issue_id, account_id)
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
def add_comment(self, issue_id: str, comment: str):
try:
comment = self._jira.add_comment(issue_id, comment)
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.add_comment(issue_id, comment)
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
return self.__parser_comment_info(comment)
def add_comment_v3(self, issue_id: str, comment: str):
try:
url = f"{self._config['JIRA_URL']}/rest/api/3/issue/{issue_id}/comment"
auth = HTTPBasicAuth(self._config['JIRA_USERNAME'], self._config['JIRA_PASSWORD'])
comment_response = requests.post(
url,
headers={
"Accept": "application/json"
},
auth=auth,
json={
"body": {
"type": "doc",
"version": 1,
"content": [
{
"type": "paragraph",
"content": [
{
"text": comment,
"type": "text"
}
]
}
]
}
}
)
except Exception as e:
self.retries -= 1
if self.retries > 0:
time.sleep(1)
return self.add_comment_v3(issue_id, comment)
print(f"=>Exception {e}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: comment error")
return self.__parser_comment_info(comment_response.json())
def get_comments(self, issueKey):
try:
comments = self._jira.comments(issueKey)
results = []
for c in comments:
results.append(self.__parser_comment_info(c.raw))
return results
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.get_comments(issueKey)
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
def get_meta(self):
meta = {}
meta['issueTypes'] = self.get_issue_types()
meta['users'] = self.get_assignable_users()
return meta
def get_assignable_users(self):
try:
users = self._jira.search_assignable_users_for_issues(project=self._config['JIRA_PROJECT_ID'], query="*")
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.get_assignable_users()
print(f"=>Exception {e.text}")
if e.status_code == 401:
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail="JIRA: 401 Unauthorized")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
users_dict = []
for user in users:
users_dict.append({
'name': user.displayName,
'email': user.emailAddress,
'id': user.accountId,
'avatarUrls': user.raw["avatarUrls"]
})
return users_dict
def get_issue_types(self):
try:
types = self._jira.issue_types()
except JIRAError as e:
self.retries -= 1
if (e.status_code // 100) == 4 and self.retries > 0:
time.sleep(1)
return self.get_issue_types()
print(f"=>Exception {e.text}")
raise HTTPException(status_code=status.HTTP_400_BAD_REQUEST, detail=f"JIRA: {e.text}")
types_dict = []
for type in types:
if not type.subtask and not type.name.lower() == "epic":
types_dict.append({
'id': type.id,
'name': type.name,
'iconUrl': type.iconUrl,
'description': type.description
})
return types_dict
def __parser_comment_info(self, comment):
if not isinstance(comment, dict):
comment = comment.raw
pattern = '%Y-%m-%dT%H:%M:%S.%f%z'
creation = datetime.strptime(comment['created'], pattern)
# update = datetime.strptime(comment['updated'], pattern)
return {
'id': comment['id'],
'author': comment['author']['accountId'],
'message': comment['body'],
# 'created': comment['created'],
'createdAt': int((creation - creation.utcoffset()).timestamp() * 1000),
# 'updated': comment['updated'],
# 'updatedAt': int((update - update.utcoffset()).timestamp() * 1000)
}
@staticmethod
def __get_closed_status(status):
return status.lower() == "done" or status.lower() == "close" or status.lower() == "closed" or status.lower() == "finish" or status.lower() == "finished"
def __parser_issue_info(self, issue, include_comments=True):
results_dict = {}
if not isinstance(issue, dict):
raw_info = issue.raw
else:
raw_info = issue
fields = raw_info['fields']
results_dict["id"] = raw_info["id"]
results_dict["key"] = raw_info["key"]
# results_dict["ticketNumber"] = raw_info["key"]
results_dict["title"] = fields["summary"]
results_dict["description"] = fields["description"]
results_dict["issueType"] = [fields["issuetype"]["id"]]
# results_dict["assignee"] = None
# results_dict["reporter"] = None
if isinstance(fields["assignee"], dict):
results_dict["assignees"] = [fields["assignee"]["accountId"]]
# if isinstance(fields["reporter"], dict):
# results_dict["reporter"] = fields["reporter"]["accountId"]
if isinstance(fields["creator"], dict):
results_dict["creator"] = fields["creator"]["accountId"]
if "comment" in fields:
if include_comments:
comments_dict = []
for comment in fields["comment"]["comments"]:
comments_dict.append(self.__parser_comment_info(comment))
results_dict['comments'] = comments_dict
results_dict['commentsCount'] = fields["comment"]["total"]
results_dict["status"] = fields["status"]['name']
results_dict["createdAt"] = fields["created"]
# results_dict["updated"] = fields["updated"]
results_dict["labels"] = fields["labels"]
results_dict["closed"] = self.__get_closed_status(fields["status"]['name'])
return results_dict
@staticmethod
def __parser_project_info(project):
results_dict = {}
raw_info = project.raw
results_dict["id"] = raw_info["id"]
results_dict["name"] = raw_info["name"]
results_dict["avatarUrls"] = raw_info["avatarUrls"]
results_dict["description"] = raw_info["description"] if "description" in raw_info else ""
return results_dict