예제 #1
0
class JiraIntegration(object):

    def signIn(self, jiraurl, username, password):
        try:
            self.__authed_jira = JIRA(server=jiraurl,
                                      basic_auth=(username, password),
                                      logging=True,
                                      max_retries=3)
            return True
        except Exception:
            return False

    def signOut(self):
        try:
            self.__authed_jira.kill_session()
            return True
        except Exception:
            return False

    def getOpenIssues(self):
        issues = self.__authed_jira.search_issues("""assignee = currentUser() 
            and sprint in openSprints () 
            order by priority desc""", maxResults=5)
        return issues

    def getTitlesForMany(self, issue_id_list):
        issues = self.__authed_jira.search_issues(' key in (%s)' % issue_id_list.join(','))
        return issues

    def getTitleFor(self, issue_id):
        issues = self.__authed_jira.search_issues(' key in (%s)' % issue_id)
        return issues
예제 #2
0
def update_description(username, password, issue, new_description):
    try:
        Jira = JIRA(JIRA_URL, basic_auth=(username, password), max_retries=0)
        existing_issue = Jira.issue(issue)
        existing_issue.update(description=new_description)
        Jira.kill_session()
    except JIRAError as e:
        raise JIRAError()
예제 #3
0
def create_ticket(
    username,
    password,
    project,
    title,
    description,
    reporter,
    assignee=None,
    watchers=None,
):
    '''
    Creates a JIRA Ticket
    Returns the string URL of the JIRA Ticket
    '''
    try:
        Jira = JIRA(JIRA_URL, basic_auth=(username, password), max_retries=0)
        issue_dict = {
            'project': {
                'id': int(project)
            },
            'summary': title,
            'description': description,
            'issuetype': {
                'name': 'Task'
            },
            'reporter': {
                'name': reporter
            },
            'assignee': {
                'name': assignee
            },
        }
        new_issue = Jira.create_issue(fields=issue_dict)
        if watchers is not None:
            for watcher in watchers:
                jira.add_watcher(new_issue.id, watcher)
        Jira.kill_session()
        return str(new_issue)
    except JIRAError as e:
        raise JIRAError()
class JiraClient():
    jira_con = None
    board_details = None

    def __init__(self):
        self._get_jira_board_details()
        secrets = _get_secrets_from_netrc()

        if not secrets:
            err_msg = 'Unable to locate or load suitable `.netrc` file for JIRA integration'
            logger.error(err_msg)
            raise ValueError(err_msg)

        try:
            username, account, apikey = secrets.authenticators(
                self.jira_hostname)
        except TypeError:
            err_msg = 'JIRA Connection. Unable to find details for machine "{}" `.netrc` file.'.format(
                self.jira_hostname)
            logger.error(err_msg)
            raise ValueError(err_msg)

        self.jira_con = JIRA(options={'server': account},
                             basic_auth=(username, apikey))
        _check_jira_con(self.jira_con, username)
        logger.debug('JIRA Connection. Details = {}'.format(
            self.jira_con.myself()))

    def _get_jira_board_details(self):
        # TODO read these in from a config file
        self.jira_hostname = 'mapaction.atlassian.net'
        self.project_key = 'PIPET'
        # The target column should be were the column where new issues are created
        self.target_column = '10110'
        self.common_task_fields = {
            'project': self.project_key,
            'issuetype': {
                'id': '10235'
            }
        }

    def __del__(self):
        try:
            self.jira_con.kill_session()
        except (TypeError, AttributeError):
            pass

    def task_handler(self, fail_threshold, msg, task_referal=None):
        logger.debug(
            'JiraClient.task_handler called with status="{}", and msg="{}"'.
            format(fail_threshold, msg))

        assured_referal = self.ensure_task_referal_type(
            task_referal, msg, fail_threshold)

        if not assured_referal:
            logger.debug(
                'JiraClient.task_handler; `None` value passed for task_referal parameter. Nothing to handle.'
            )
            return

        unique_summary = assured_referal.get_task_unique_summary()
        task_desc = assured_referal.get_task_description()
        op_id = assured_referal.get_operation_id()

        j_issue = self.search_issue_by_unique_summary(unique_summary, op_id)

        if j_issue:
            # Update existing card and maybe move it back into "Doing" column
            self.update_jira_issue(j_issue, task_desc, fail_threshold)
        else:
            if fail_threshold > logging.INFO:
                # Create a new task
                self.create_new_jira_issue(unique_summary, task_desc, op_id)

    def ensure_task_referal_type(self, task_referal, msg, fail_threshold):
        """
        Check whether or not the `task_referal` is an instance of TaskReferralBase object. If it is the object is
        then it is returned unchanged. If not then an generic TaskReferralBase will be created and returned. The
        value of `str(task_referal)` will be used.

        @param task_referal: An object that may or may not be a TaskReferralBase object.
        @returns: If the `task_referal` param is an instance of TaskReferralBase object, then `task_referal` is
                  returned.
                  If `task_referal` param is NOT an instance of TaskReferralBase AND fail_threshold is logging.ERROR
                  then a new TaskReferralBase object is created (using `msg` and `str(task_referal)` for context).
                  Else `None` is returned.
        """
        if isinstance(task_referal, TaskReferralBase):
            logger.debug(
                'JiraClient.ensure_task_referal_type found a TaskReferralBase object'
            )
            return task_referal

        if task_referal and (fail_threshold > logging.WARNING):
            logger.debug(
                'JiraClient.ensure_task_referal_type created a new TaskReferralBase object'
            )
            return TaskReferralBase(None, msg=msg, other=str(task_referal))

        logger.debug(
            'JiraClient.ensure_task_referal_type passed "{}" but returned `None`'
            .format(str(task_referal)))
        return None

    def search_issue_by_unique_summary(self, search_summary, op_id):
        # Default if `op_id` is None
        jql_op_id = 'operational_id is EMPTY'
        if op_id:
            jql_op_id = 'operational_id ~ "{}"'.format(op_id)

        jql_str = 'project={} AND {} AND summary ~ "{}"'.format(
            self.project_key, jql_op_id, search_summary)
        found_issues = self.jira_con.search_issues(jql_str, maxResults=2)

        if found_issues:
            if len(found_issues) > 1:
                raise ValueError(
                    'More than one JIRA Issue found with the summary "{}". This suggests that additional'
                    ' issues have been raised manualy on the board "{}". Please ensure that there is exactly'
                    ' one issues with this summary, by deleting those which have not been created by the'
                    ' user "{}"'.format(
                        search_summary, self.project_key,
                        self.jira_con.myself()['emailAddress']))
            else:
                return found_issues[0]
        else:
            return None

    def create_new_jira_issue(self, unique_summary, task_desc, op_id):
        flds = self.common_task_fields.copy()
        flds['summary'] = unique_summary
        flds['description'] = task_desc
        # This is the JIRA API's field ID for operational_id. To work this out execute:
        # ```
        # a = j.jira_con.createmeta(projectKeys=['PIPET'], issuetypeIds=[10235], expand='projects.issuetypes.fields')
        # print(a)
        # ```
        # Then search the output for your custom field name. Doubtless there is a programmatic way to do this.
        flds['customfield_10234'] = op_id

        new_task = self.jira_con.create_issue(fields=flds)
        # new_task.update(fields={'operational_id':op_id})
        # new_task.update(operational_id=op_id)
        print(new_task)
        # print('desc', new_task.fields.description)
        # print('opid', new_task.fields.operational_id)
        # for f in new_task.fields:
        #     print('field itr', f)

    def update_jira_issue(self, j_issue, task_desc, fail_threshold):
        now_utc = pytz.utc.localize(datetime.now())
        time_stamp = now_utc.strftime('%Y-%m-%d %H:%M:%S %Z%z')

        # prev_desc =
        if task_desc != j_issue.fields.description:
            j_issue.update(description=task_desc)

        if fail_threshold > logging.INFO:
            self.jira_con.add_comment(
                j_issue.id,
                'This Issue was still current when MapChef was run at {}'.
                format(time_stamp))