Path: blob/master/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php
12256 views
<?php12final class DoorkeeperBridgeJIRA extends DoorkeeperBridge {34const APPTYPE_JIRA = 'jira';5const OBJTYPE_ISSUE = 'jira:issue';67public function canPullRef(DoorkeeperObjectRef $ref) {8if ($ref->getApplicationType() != self::APPTYPE_JIRA) {9return false;10}1112$types = array(13self::OBJTYPE_ISSUE => true,14);1516return isset($types[$ref->getObjectType()]);17}1819public function pullRefs(array $refs) {2021$id_map = mpull($refs, 'getObjectID', 'getObjectKey');22$viewer = $this->getViewer();2324$provider = PhabricatorJIRAAuthProvider::getJIRAProvider();25if (!$provider) {26return;27}2829$accounts = id(new PhabricatorExternalAccountQuery())30->setViewer($viewer)31->withUserPHIDs(array($viewer->getPHID()))32->withProviderConfigPHIDs(33array(34$provider->getProviderConfigPHID(),35))36->requireCapabilities(37array(38PhabricatorPolicyCapability::CAN_VIEW,39PhabricatorPolicyCapability::CAN_EDIT,40))41->execute();4243if (!$accounts) {44return $this->didFailOnMissingLink();45}4647// TODO: When we support multiple JIRA instances, we need to disambiguate48// issues (perhaps with additional configuration) or cast a wide net49// (by querying all instances). For now, just query the one instance.50$account = head($accounts);5152$timeout = $this->getTimeout();5354$futures = array();55foreach ($id_map as $key => $id) {56$future = $provider->newJIRAFuture(57$account,58'rest/api/2/issue/'.phutil_escape_uri($id),59'GET');6061if ($timeout !== null) {62$future->setTimeout($timeout);63}6465$futures[$key] = $future;66}6768$results = array();69$failed = array();70foreach (new FutureIterator($futures) as $key => $future) {71try {72$results[$key] = $future->resolveJSON();73} catch (Exception $ex) {74if (($ex instanceof HTTPFutureResponseStatus) &&75($ex->getStatusCode() == 404)) {76// This indicates that the object has been deleted (or never existed,77// or isn't visible to the current user) but it's a successful sync of78// an object which isn't visible.79} else {80// This is something else, so consider it a synchronization failure.81phlog($ex);82$failed[$key] = $ex;83}84}85}8687foreach ($refs as $ref) {88$ref->setAttribute('name', pht('JIRA %s', $ref->getObjectID()));8990$did_fail = idx($failed, $ref->getObjectKey());91if ($did_fail) {92$ref->setSyncFailed(true);93continue;94}9596$result = idx($results, $ref->getObjectKey());97if (!$result) {98continue;99}100101$fields = idx($result, 'fields', array());102103$ref->setIsVisible(true);104$ref->setAttribute(105'fullname',106pht('JIRA %s %s', $result['key'], idx($fields, 'summary')));107108$ref->setAttribute('title', idx($fields, 'summary'));109$ref->setAttribute('description', idx($result, 'description'));110$ref->setAttribute('shortname', $result['key']);111112$obj = $ref->getExternalObject();113if ($obj->getID()) {114continue;115}116117$this->fillObjectFromData($obj, $result);118$this->saveExternalObject($ref, $obj);119}120}121122public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) {123// Convert the "self" URI, which points at the REST endpoint, into a124// browse URI.125$self = idx($result, 'self');126$object_id = $obj->getObjectID();127128$uri = self::getJIRAIssueBrowseURIFromJIRARestURI($self, $object_id);129if ($uri !== null) {130$obj->setObjectURI($uri);131}132}133134public static function getJIRAIssueBrowseURIFromJIRARestURI(135$uri,136$object_id) {137138$uri = new PhutilURI($uri);139140// The JIRA install might not be at the domain root, so we may need to141// keep an initial part of the path, like "/jira/". Find the API specific142// part of the URI, strip it off, then replace it with the web version.143$path = $uri->getPath();144$pos = strrpos($path, 'rest/api/2/issue/');145if ($pos === false) {146return null;147}148149$path = substr($path, 0, $pos);150$path = $path.'browse/'.$object_id;151$uri->setPath($path);152153return (string)$uri;154}155156}157158159