mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 08:42:41 +01:00
Add a link to the main Asana task from Differential
Summary: Ref T2852. When a Differential revision is linked to an Asana task, show the related task in Differential. Test Plan: {F49234} Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2852 Differential Revision: https://secure.phabricator.com/D6387
This commit is contained in:
parent
e5f200c654
commit
13e2489739
6 changed files with 118 additions and 23 deletions
|
@ -305,6 +305,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialAffectedPath' => 'applications/differential/storage/DifferentialAffectedPath.php',
|
||||
'DifferentialApplyPatchFieldSpecification' => 'applications/differential/field/specification/DifferentialApplyPatchFieldSpecification.php',
|
||||
'DifferentialArcanistProjectFieldSpecification' => 'applications/differential/field/specification/DifferentialArcanistProjectFieldSpecification.php',
|
||||
'DifferentialAsanaRepresentationFieldSpecification' => 'applications/differential/field/specification/DifferentialAsanaRepresentationFieldSpecification.php',
|
||||
'DifferentialAuditorsFieldSpecification' => 'applications/differential/field/specification/DifferentialAuditorsFieldSpecification.php',
|
||||
'DifferentialAuthorFieldSpecification' => 'applications/differential/field/specification/DifferentialAuthorFieldSpecification.php',
|
||||
'DifferentialAuxiliaryField' => 'applications/differential/storage/DifferentialAuxiliaryField.php',
|
||||
|
@ -2239,6 +2240,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialAffectedPath' => 'DifferentialDAO',
|
||||
'DifferentialApplyPatchFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialArcanistProjectFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialAsanaRepresentationFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialAuditorsFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialAuthorFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialAuxiliaryField' => 'DifferentialDAO',
|
||||
|
|
|
@ -32,6 +32,7 @@ final class DifferentialDefaultFieldSelector
|
|||
new DifferentialDateCreatedFieldSpecification(),
|
||||
new DifferentialAuditorsFieldSpecification(),
|
||||
new DifferentialDiffViewPolicyFieldSpecification(),
|
||||
new DifferentialAsanaRepresentationFieldSpecification(),
|
||||
);
|
||||
|
||||
return $fields;
|
||||
|
|
|
@ -0,0 +1,76 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialAsanaRepresentationFieldSpecification
|
||||
extends DifferentialFieldSpecification {
|
||||
|
||||
public function shouldAppearOnRevisionView() {
|
||||
return (bool)PhabricatorEnv::getEnvConfig('asana.workspace-id');
|
||||
}
|
||||
|
||||
public function renderLabelForRevisionView() {
|
||||
return pht('In Asana:');
|
||||
}
|
||||
|
||||
public function renderValueForRevisionView() {
|
||||
$viewer = $this->getUser();
|
||||
$src_phid = $this->getRevision()->getPHID();
|
||||
$edge_type = PhabricatorEdgeConfig::TYPE_PHOB_HAS_ASANATASK;
|
||||
|
||||
$query = id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs(array($src_phid))
|
||||
->withEdgeTypes(array($edge_type))
|
||||
->needEdgeData(true);
|
||||
|
||||
$edges = $query->execute();
|
||||
if (!$edges) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$edge = head($edges[$src_phid][$edge_type]);
|
||||
|
||||
if (!empty($edge['data']['gone'])) {
|
||||
return phutil_tag(
|
||||
'em',
|
||||
array(),
|
||||
pht('Asana Task Deleted'));
|
||||
}
|
||||
|
||||
$ref = id(new DoorkeeperImportEngine())
|
||||
->setViewer($viewer)
|
||||
->withPHIDs(array($edge['dst']))
|
||||
->needLocalOnly(true)
|
||||
->executeOne();
|
||||
|
||||
if (!$ref) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$tag_id = celerity_generate_unique_node_id();
|
||||
$xobj = $ref->getExternalObject();
|
||||
$href = $xobj->getObjectURI();
|
||||
|
||||
Javelin::initBehavior(
|
||||
'doorkeeper-tag',
|
||||
array(
|
||||
'tags' => array(
|
||||
array(
|
||||
'id' => $tag_id,
|
||||
'ref' => array(
|
||||
$ref->getApplicationType(),
|
||||
$ref->getApplicationDomain(),
|
||||
$ref->getObjectType(),
|
||||
$ref->getObjectID(),
|
||||
),
|
||||
),
|
||||
),
|
||||
));
|
||||
|
||||
return id(new PhabricatorTagView())
|
||||
->setID($tag_id)
|
||||
->setName($href)
|
||||
->setHref($href)
|
||||
->setType(PhabricatorTagView::TYPE_OBJECT)
|
||||
->setExternal(true);
|
||||
}
|
||||
|
||||
}
|
|
@ -5,6 +5,7 @@ final class DoorkeeperImportEngine extends Phobject {
|
|||
private $viewer;
|
||||
private $refs = array();
|
||||
private $phids = array();
|
||||
private $localOnly;
|
||||
|
||||
public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
|
@ -30,6 +31,11 @@ final class DoorkeeperImportEngine extends Phobject {
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function needLocalOnly($local_only) {
|
||||
$this->localOnly = $local_only;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function execute() {
|
||||
$refs = $this->getRefs();
|
||||
$viewer = $this->getViewer();
|
||||
|
@ -64,32 +70,38 @@ final class DoorkeeperImportEngine extends Phobject {
|
|||
}
|
||||
}
|
||||
|
||||
$bridges = id(new PhutilSymbolLoader())
|
||||
->setAncestorClass('DoorkeeperBridge')
|
||||
->loadObjects();
|
||||
if (!$this->localOnly) {
|
||||
$bridges = id(new PhutilSymbolLoader())
|
||||
->setAncestorClass('DoorkeeperBridge')
|
||||
->loadObjects();
|
||||
|
||||
foreach ($bridges as $key => $bridge) {
|
||||
if (!$bridge->isEnabled()) {
|
||||
unset($bridges[$key]);
|
||||
}
|
||||
$bridge->setViewer($viewer);
|
||||
}
|
||||
|
||||
$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]);
|
||||
foreach ($bridges as $key => $bridge) {
|
||||
if (!$bridge->isEnabled()) {
|
||||
unset($bridges[$key]);
|
||||
}
|
||||
$bridge->setViewer($viewer);
|
||||
}
|
||||
if ($bridge_refs) {
|
||||
$bridge->pullRefs($bridge_refs);
|
||||
|
||||
$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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -238,6 +238,8 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
|
||||
$extra_data = array();
|
||||
if ($main_edge) {
|
||||
$extra_data = $main_edge['data'];
|
||||
|
||||
$refs = id(new DoorkeeperImportEngine())
|
||||
->setViewer($possessed_user)
|
||||
->withPHIDs(array($main_edge['dst']))
|
||||
|
@ -289,8 +291,6 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
"Skipping main task update, cursor is ahead of the story.\n");
|
||||
}
|
||||
}
|
||||
|
||||
$extra_data = $main_edge['data'];
|
||||
} else {
|
||||
$parent = $this->makeAsanaAPICall(
|
||||
$oauth_token,
|
||||
|
|
|
@ -19,8 +19,12 @@ final class PhabricatorTaskmasterDaemon extends PhabricatorDaemon {
|
|||
$task = $task->executeTask();
|
||||
$ex = $task->getExecutionException();
|
||||
if ($ex) {
|
||||
$this->log("Task {$id} failed!");
|
||||
throw $ex;
|
||||
if ($ex instanceof PhabricatorWorkerPermanentFailureException) {
|
||||
$this->log("Task {$id} failed permanently.");
|
||||
} else {
|
||||
$this->log("Task {$id} failed!");
|
||||
throw $ex;
|
||||
}
|
||||
} else {
|
||||
$this->log("Task {$id} complete! Moved to archive.");
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue