Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridge.php b/src/applications/doorkeeper/bridge/DoorkeeperBridge.php
index 4a4ee2667b..d25d48e857 100644
--- a/src/applications/doorkeeper/bridge/DoorkeeperBridge.php
+++ b/src/applications/doorkeeper/bridge/DoorkeeperBridge.php
@@ -1,79 +1,89 @@
<?php
abstract class DoorkeeperBridge extends Phobject {
private $viewer;
private $context = array();
private $throwOnMissingLink;
+ private $timeout;
+
+ public function setTimeout($timeout) {
+ $this->timeout = $timeout;
+ return $this;
+ }
+
+ public function getTimeout() {
+ return $this->timeout;
+ }
public function setThrowOnMissingLink($throw_on_missing_link) {
$this->throwOnMissingLink = $throw_on_missing_link;
return $this;
}
final public function setViewer($viewer) {
$this->viewer = $viewer;
return $this;
}
final public function getViewer() {
return $this->viewer;
}
final public function setContext($context) {
$this->context = $context;
return $this;
}
final public function getContextProperty($key, $default = null) {
return idx($this->context, $key, $default);
}
public function isEnabled() {
return true;
}
abstract public function canPullRef(DoorkeeperObjectRef $ref);
abstract public function pullRefs(array $refs);
public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) {
return;
}
public function didFailOnMissingLink() {
if ($this->throwOnMissingLink) {
throw new DoorkeeperMissingLinkException();
}
return null;
}
final protected function saveExternalObject(
DoorkeeperObjectRef $ref,
DoorkeeperExternalObject $obj) {
$unguarded = AphrontWriteGuard::beginScopedUnguardedWrites();
try {
$obj->save();
} catch (AphrontDuplicateKeyQueryException $ex) {
// In various cases, we may race another process importing the same
// data. If we do, we'll collide on the object key. Load the object
// the other process created and use that.
$obj = id(new DoorkeeperExternalObjectQuery())
->setViewer($this->getViewer())
->withObjectKeys(array($ref->getObjectKey()))
->executeOne();
if (!$obj) {
throw new PhutilProxyException(
pht('Failed to load external object after collision.'),
$ex);
}
$ref->attachExternalObject($obj);
}
unset($unguarded);
}
}
diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php
index ec604e158e..05ee786337 100644
--- a/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php
+++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeAsana.php
@@ -1,126 +1,131 @@
<?php
final class DoorkeeperBridgeAsana extends DoorkeeperBridge {
const APPTYPE_ASANA = 'asana';
const APPDOMAIN_ASANA = 'asana.com';
const OBJTYPE_TASK = 'asana:task';
public function canPullRef(DoorkeeperObjectRef $ref) {
if ($ref->getApplicationType() != self::APPTYPE_ASANA) {
return false;
}
if ($ref->getApplicationDomain() != self::APPDOMAIN_ASANA) {
return false;
}
$types = array(
self::OBJTYPE_TASK => true,
);
return isset($types[$ref->getObjectType()]);
}
public function pullRefs(array $refs) {
$id_map = mpull($refs, 'getObjectID', 'getObjectKey');
$viewer = $this->getViewer();
$provider = PhabricatorAsanaAuthProvider::getAsanaProvider();
if (!$provider) {
return;
}
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->withAccountTypes(array($provider->getProviderType()))
->withAccountDomains(array($provider->getProviderDomain()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
if (!$accounts) {
return $this->didFailOnMissingLink();
}
// TODO: If the user has several linked Asana accounts, we just pick the
// first one arbitrarily. We might want to try using all of them or do
// something with more finesse. There's no UI way to link multiple accounts
// right now so this is currently moot.
$account = head($accounts);
$token = $provider->getOAuthAccessToken($account);
if (!$token) {
return;
}
$template = id(new PhutilAsanaFuture())
->setAccessToken($token);
+ $timeout = $this->getTimeout();
+ if ($timeout !== null) {
+ $template->setTimeout($timeout);
+ }
+
$futures = array();
foreach ($id_map as $key => $id) {
$futures[$key] = id(clone $template)
->setRawAsanaQuery("tasks/{$id}");
}
$results = array();
$failed = array();
foreach (new FutureIterator($futures) as $key => $future) {
try {
$results[$key] = $future->resolve();
} catch (Exception $ex) {
if (($ex instanceof HTTPFutureResponseStatus) &&
($ex->getStatusCode() == 404)) {
// This indicates that the object has been deleted (or never existed,
// or isn't visible to the current user) but it's a successful sync of
// an object which isn't visible.
} else {
// This is something else, so consider it a synchronization failure.
phlog($ex);
$failed[$key] = $ex;
}
}
}
foreach ($refs as $ref) {
$ref->setAttribute('name', pht('Asana Task %s', $ref->getObjectID()));
$did_fail = idx($failed, $ref->getObjectKey());
if ($did_fail) {
$ref->setSyncFailed(true);
continue;
}
$result = idx($results, $ref->getObjectKey());
if (!$result) {
continue;
}
$ref->setIsVisible(true);
$ref->setAttribute('asana.data', $result);
$ref->setAttribute('fullname', pht('Asana: %s', $result['name']));
$ref->setAttribute('title', $result['name']);
$ref->setAttribute('description', $result['notes']);
$obj = $ref->getExternalObject();
if ($obj->getID()) {
continue;
}
$this->fillObjectFromData($obj, $result);
$this->saveExternalObject($ref, $obj);
}
}
public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) {
$id = $result['id'];
$uri = "https://app.asana.com/0/{$id}/{$id}";
$obj->setObjectURI($uri);
}
}
diff --git a/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php b/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php
index 920f2eeb91..f82bb1ba25 100644
--- a/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php
+++ b/src/applications/doorkeeper/bridge/DoorkeeperBridgeJIRA.php
@@ -1,146 +1,154 @@
<?php
final class DoorkeeperBridgeJIRA extends DoorkeeperBridge {
const APPTYPE_JIRA = 'jira';
const OBJTYPE_ISSUE = 'jira:issue';
public function canPullRef(DoorkeeperObjectRef $ref) {
if ($ref->getApplicationType() != self::APPTYPE_JIRA) {
return false;
}
$types = array(
self::OBJTYPE_ISSUE => true,
);
return isset($types[$ref->getObjectType()]);
}
public function pullRefs(array $refs) {
$id_map = mpull($refs, 'getObjectID', 'getObjectKey');
$viewer = $this->getViewer();
$provider = PhabricatorJIRAAuthProvider::getJIRAProvider();
if (!$provider) {
return;
}
$accounts = id(new PhabricatorExternalAccountQuery())
->setViewer($viewer)
->withUserPHIDs(array($viewer->getPHID()))
->withAccountTypes(array($provider->getProviderType()))
->requireCapabilities(
array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
))
->execute();
if (!$accounts) {
return $this->didFailOnMissingLink();
}
// TODO: When we support multiple JIRA instances, we need to disambiguate
// issues (perhaps with additional configuration) or cast a wide net
// (by querying all instances). For now, just query the one instance.
$account = head($accounts);
+ $timeout = $this->getTimeout();
+
$futures = array();
foreach ($id_map as $key => $id) {
- $futures[$key] = $provider->newJIRAFuture(
+ $future = $provider->newJIRAFuture(
$account,
'rest/api/2/issue/'.phutil_escape_uri($id),
'GET');
+
+ if ($timeout !== null) {
+ $future->setTimeout($timeout);
+ }
+
+ $futures[$key] = $future;
}
$results = array();
$failed = array();
foreach (new FutureIterator($futures) as $key => $future) {
try {
$results[$key] = $future->resolveJSON();
} catch (Exception $ex) {
if (($ex instanceof HTTPFutureResponseStatus) &&
($ex->getStatusCode() == 404)) {
// This indicates that the object has been deleted (or never existed,
// or isn't visible to the current user) but it's a successful sync of
// an object which isn't visible.
} else {
// This is something else, so consider it a synchronization failure.
phlog($ex);
$failed[$key] = $ex;
}
}
}
foreach ($refs as $ref) {
$ref->setAttribute('name', pht('JIRA %s', $ref->getObjectID()));
$did_fail = idx($failed, $ref->getObjectKey());
if ($did_fail) {
$ref->setSyncFailed(true);
continue;
}
$result = idx($results, $ref->getObjectKey());
if (!$result) {
continue;
}
$fields = idx($result, 'fields', array());
$ref->setIsVisible(true);
$ref->setAttribute(
'fullname',
pht('JIRA %s %s', $result['key'], idx($fields, 'summary')));
$ref->setAttribute('title', idx($fields, 'summary'));
$ref->setAttribute('description', idx($result, 'description'));
$ref->setAttribute('shortname', $result['key']);
$obj = $ref->getExternalObject();
if ($obj->getID()) {
continue;
}
$this->fillObjectFromData($obj, $result);
$this->saveExternalObject($ref, $obj);
}
}
public function fillObjectFromData(DoorkeeperExternalObject $obj, $result) {
// Convert the "self" URI, which points at the REST endpoint, into a
// browse URI.
$self = idx($result, 'self');
$object_id = $obj->getObjectID();
$uri = self::getJIRAIssueBrowseURIFromJIRARestURI($self, $object_id);
if ($uri !== null) {
$obj->setObjectURI($uri);
}
}
public static function getJIRAIssueBrowseURIFromJIRARestURI(
$uri,
$object_id) {
$uri = new PhutilURI($uri);
// The JIRA install might not be at the domain root, so we may need to
// keep an initial part of the path, like "/jira/". Find the API specific
// part of the URI, strip it off, then replace it with the web version.
$path = $uri->getPath();
$pos = strrpos($path, 'rest/api/2/issue/');
if ($pos === false) {
return null;
}
$path = substr($path, 0, $pos);
$path = $path.'browse/'.$object_id;
$uri->setPath($path);
return (string)$uri;
}
}
diff --git a/src/applications/doorkeeper/controller/DoorkeeperTagsController.php b/src/applications/doorkeeper/controller/DoorkeeperTagsController.php
index 5a886cba3e..f4b4195f11 100644
--- a/src/applications/doorkeeper/controller/DoorkeeperTagsController.php
+++ b/src/applications/doorkeeper/controller/DoorkeeperTagsController.php
@@ -1,76 +1,77 @@
<?php
final class DoorkeeperTagsController extends PhabricatorController {
public function handleRequest(AphrontRequest $request) {
$viewer = $request->getViewer();
$tags = $request->getStr('tags');
try {
$tags = phutil_json_decode($tags);
} catch (PhutilJSONParserException $ex) {
$tags = array();
}
$refs = array();
foreach ($tags as $key => $tag_spec) {
$tag = $tag_spec['ref'];
$ref = id(new DoorkeeperObjectRef())
->setApplicationType($tag[0])
->setApplicationDomain($tag[1])
->setObjectType($tag[2])
->setObjectID($tag[3]);
$refs[$key] = $ref;
}
$refs = id(new DoorkeeperImportEngine())
->setViewer($viewer)
->setRefs($refs)
+ ->setTimeout(15)
->execute();
$results = array();
foreach ($refs as $key => $ref) {
if (!$ref->getIsVisible()) {
continue;
}
$uri = $ref->getExternalObject()->getObjectURI();
if (!$uri) {
continue;
}
$tag_spec = $tags[$key];
$id = $tag_spec['id'];
$view = idx($tag_spec, 'view');
$is_short = ($view == 'short');
if ($is_short) {
$name = $ref->getShortName();
} else {
$name = $ref->getFullName();
}
$tag = id(new PHUITagView())
->setID($id)
->setName($name)
->setHref($uri)
->setType(PHUITagView::TYPE_OBJECT)
->setExternal(true)
->render();
$results[] = array(
'id' => $id,
'markup' => $tag,
);
}
return id(new AphrontAjaxResponse())->setContent(
array(
'tags' => $results,
));
}
}
diff --git a/src/applications/doorkeeper/engine/DoorkeeperImportEngine.php b/src/applications/doorkeeper/engine/DoorkeeperImportEngine.php
index 8c58f5bd88..02642cb994 100644
--- a/src/applications/doorkeeper/engine/DoorkeeperImportEngine.php
+++ b/src/applications/doorkeeper/engine/DoorkeeperImportEngine.php
@@ -1,129 +1,145 @@
<?php
final class DoorkeeperImportEngine extends Phobject {
private $viewer;
private $refs = array();
private $phids = array();
private $localOnly;
private $throwOnMissingLink;
private $context = array();
+ private $timeout;
public function setViewer(PhabricatorUser $viewer) {
$this->viewer = $viewer;
return $this;
}
public function getViewer() {
return $this->viewer;
}
public function setRefs(array $refs) {
assert_instances_of($refs, 'DoorkeeperObjectRef');
$this->refs = $refs;
return $this;
}
public function getRefs() {
return $this->refs;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function needLocalOnly($local_only) {
$this->localOnly = $local_only;
return $this;
}
public function setContextProperty($key, $value) {
$this->context[$key] = $value;
return $this;
}
+ public function setTimeout($timeout) {
+ $this->timeout = $timeout;
+ return $this;
+ }
+
+ public function getTimeout() {
+ return $this->timeout;
+ }
+
/**
* Configure behavior if remote refs can not be retrieved because an
* authentication link is missing.
*/
public function setThrowOnMissingLink($throw) {
$this->throwOnMissingLink = $throw;
return $this;
}
public function execute() {
$refs = $this->getRefs();
$viewer = $this->getViewer();
$keys = mpull($refs, 'getObjectKey');
if ($keys) {
$xobjs = id(new DoorkeeperExternalObjectQuery())
->setViewer($viewer)
->withObjectKeys($keys)
->execute();
$xobjs = mpull($xobjs, null, 'getObjectKey');
foreach ($refs as $ref) {
$xobj = idx($xobjs, $ref->getObjectKey());
if (!$xobj) {
$xobj = $ref
->newExternalObject()
->setImporterPHID($viewer->getPHID());
// NOTE: Fill the new external object into the object map, so we'll
// reference the same external object if more than one ref is the
// same. This prevents issues later where we double-populate
// external objects when handed duplicate refs.
$xobjs[$ref->getObjectKey()] = $xobj;
}
$ref->attachExternalObject($xobj);
}
}
if ($this->phids) {
$xobjs = id(new DoorkeeperExternalObjectQuery())
->setViewer($viewer)
->withPHIDs($this->phids)
->execute();
foreach ($xobjs as $xobj) {
$ref = $xobj->getRef();
$ref->attachExternalObject($xobj);
$refs[$ref->getObjectKey()] = $ref;
}
}
if (!$this->localOnly) {
$bridges = id(new PhutilClassMapQuery())
->setAncestorClass('DoorkeeperBridge')
->setFilterMethod('isEnabled')
->execute();
+ $timeout = $this->getTimeout();
foreach ($bridges as $key => $bridge) {
- $bridge->setViewer($viewer);
- $bridge->setThrowOnMissingLink($this->throwOnMissingLink);
- $bridge->setContext($this->context);
+ $bridge
+ ->setViewer($viewer)
+ ->setThrowOnMissingLink($this->throwOnMissingLink)
+ ->setContext($this->context);
+
+ if ($timeout !== null) {
+ $bridge->setTimeout($timeout);
+ }
}
$working_set = $refs;
foreach ($bridges as $bridge) {
$bridge_refs = array();
foreach ($working_set as $key => $ref) {
if ($bridge->canPullRef($ref)) {
$bridge_refs[$key] = $ref;
unset($working_set[$key]);
}
}
if ($bridge_refs) {
$bridge->pullRefs($bridge_refs);
}
}
}
return $refs;
}
public function executeOne() {
return head($this->execute());
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Mar 17, 2:19 AM (2 h, 23 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
964258
Default Alt Text
(17 KB)

Event Timeline