phplib/Target/Jira.php

Summary

Maintainability
A
1 hr
Test Coverage
<?php

namespace FOO;

/**
 * Jira Target Class
 * Creates a ticket for an Alert.
 */
class Jira_Target extends Target {
    public static $TYPE = 'jira';

    public static $DESC = 'Create a ticket for this alert and assign to <assignee>.';

    protected static function generateDataSchema() {
        return [
            'project' => [static::T_STR, null, ''],
            'type' => [static::T_NUM, null, 0],
            'assignee' => [static::T_STR, null, ''],
            'watchers' => [static::T_ARR, static::T_STR, []],
        ];
    }

    public function isAccessible() {
        $jiracfg = Config::get('jira');
        return !is_null($jiracfg['host']);
    }

    /**
     * Create ticket.
     *
     * @param Alert An Alert object
     * @param int $date The current date.
     */
    public function process(Alert $alert, $date) {
        $site = SiteFinder::getCurrent();
        $search = SearchFinder::getById($alert['search_id']);
        $issue_key = null;

        $title = sprintf('[%s] %s', $site['name'], $search['name']);
        $desc = [];
        $desc[] = sprintf('Date: %s', gmdate(DATE_RSS, $alert['alert_date']));

        // Don't show the link if this Alert isn't persisted.
        if(!$alert->isNew()) {
            $desc[] = sprintf('[Link to Alert|%s]', $site->urlFor(
                sprintf('alert/%d', $alert['alert_id'])
            ));
            $source = $search->getLink($alert);
            if(!is_null($source)) {
                $desc[] = sprintf('[Source|%s]', $source);
            }
        }
        $desc[] = '';
        $desc[] = '*Alert Data*';
        $desc[] = '||Key||Value||';
        foreach($alert['content'] as $key=>$value) {
            $desc[] = sprintf('|%s|%s|', $key, $value);
        }

        $issue_data = [
            'project' => ['key' => $this->obj['data']['project']],
            'issuetype' => ['id' => $this->obj['data']['type']],
            'summary' => $title,
            'description' => implode("\n", $desc),
            'assignee' => ['name' => $this->obj['data']['assignee']],
        ];
        $createmeta = self::getCreateMeta($this->obj['data']['project'], $this->obj['data']['type'], true);
        $issue_data = array_intersect_key($issue_data, $createmeta['projects'][0]['issuetypes'][0]['fields']);
        list($issue_data) = Hook::call('target.jira.send', [$issue_data]);

        $issue_key = self::createIssue($issue_data);
        if(!is_null($issue_key)) {
            self::addWatchers($issue_key, $this->obj['data']['watchers']);
        }
    }

    /**
     * Create a JIRA ticket.
     * @param mixed[] $issue_data Issue data.
     * @return string|null The issue key or null.
     */
    public static function createIssue($issue_data) {
        $jiracfg = Config::get('jira');
        $curl = self::getCurl();

        $raw_data = $curl->post(
            sprintf('%s/rest/api/2/issue', $jiracfg['host']),
            json_encode(['fields' => $issue_data])
        );

        if($curl->httpStatusCode < 200 || $curl->httpStatusCode >= 300) {
            throw new TargetException(sprintf('Remote server returned %d: %s: %s', $curl->httpStatusCode, $curl->httpErrorMessage, json_encode($raw_data)));
        }

        return $raw_data['key'];
    }

    /**
     * Add a comment to a JIRA ticket.
     * @param string $issue_key Issue key.
     * @param string $comment The comment text.
     */
    public static function addComment($issue_key, $comment) {
        $jiracfg = Config::get('jira');
        $curl = self::getCurl();

        $raw_data = $curl->post(
            sprintf('%s/rest/api/2/issue/%s/comment', $jiracfg['host'], $issue_key),
            json_encode(['body' => $comment])
        );

        if($curl->httpStatusCode < 200 || $curl->httpStatusCode >= 300) {
            throw new TargetException(sprintf('Remote server returned %d: %s: %s', $curl->httpStatusCode, $curl->httpErrorMessage, json_encode($raw_data)));
        }
    }

    /**
     * Add watchers to a JIRA ticket.
     * @param string $issue_key Issue key.
     * @param string[] $watchers An array of watchers.
     */
    public static function addWatchers($issue_key, $watchers) {
        $jiracfg = Config::get('jira');
        $curl = self::getCurl();

        foreach($watchers as $watcher) {
            $raw_data = $curl->post(
                sprintf('%s/rest/api/2/issue/%s/watchers', $jiracfg['host'], $issue_key),
                json_encode($watcher)
            );
        }
    }

    /**
     * Get metadata about issuetypes
     * @param string $project The project key.
     * @param string $type The issue type.
     * @return array
     */
    public static function getCreateMeta($project=null, $type=null, $fields=false) {
        $jiracfg = Config::get('jira');
        $curl = self::getCurl();

        $params = [];
        if(!is_null($project)) {
            $params['projectKeys'] = $project;
        }
        if(!is_null($type)) {
            $params['issuetypeIds'] = $type;
        }
        if($fields) {
            $params['expand'] = 'projects.issuetypes.fields';
        }
        $raw_data = $curl->get(sprintf('%s/rest/api/2/issue/createmeta?%s', $jiracfg['host'], http_build_query($params)));
        if($curl->httpStatusCode < 200 || $curl->httpStatusCode >= 300) {
            return null;
        }

        return $raw_data;
    }

    /**
     * Get user info.
     * @return array
     */
    public static function getUsers() {
        $jiracfg = Config::get('jira');
        $curl = self::getCurl();

        $users = [];
        $offset = 0;
        do {
            $raw_data = $curl->get(sprintf('%s/rest/api/2/user/search?username=%%&maxResults=1000&startAt=%d', $jiracfg['host'], $offset));
            if($curl->httpStatusCode < 200 || $curl->httpStatusCode >= 300) {
                break;
            }

            $cnt = count($raw_data);
            $users = array_merge($users, $raw_data);
            $offset += $cnt;
        } while($cnt == 1000);

        return $users;
    }

    /**
     * Get a properly initialized curl object.
     * @return Curl
     */
    private static function getCurl() {
        $jiracfg = Config::get('jira');
        if(is_null($jiracfg['host'])) {
            throw new TargetException('Jira not configured');
        }

        $curl = new Curl;
        $curl->setHeader('X-Atlassian-Token', 'nocheck');
        $curl->setHeader('Content-type', 'application/json');
        if(!is_null($jiracfg['user']) && !is_null($jiracfg['pass'])) {
            $curl->setBasicAuthentication($jiracfg['user'], $jiracfg['pass']);
        }

        return $curl;
    }
}