mirror of
https://we.phorge.it/source/phorge.git
synced 2025-01-02 19:01:03 +01:00
Generalize Asana-publishable feed story objects
Summary: Ref T2852. Pulls the Differential-specific aspects of the Asana sync out of the worker. Next diff will add a publisher for Audit/Diffusion. Test Plan: Published events, including state changes. Saw them reflected correctly in Asana. Reviewers: btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2852 Differential Revision: https://secure.phabricator.com/D6569
This commit is contained in:
parent
63c33eeb5d
commit
8514a3c739
4 changed files with 189 additions and 68 deletions
|
@ -347,6 +347,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialDiffTestCase' => 'applications/differential/storage/__tests__/DifferentialDiffTestCase.php',
|
||||
'DifferentialDiffViewController' => 'applications/differential/controller/DifferentialDiffViewController.php',
|
||||
'DifferentialDiffViewPolicyFieldSpecification' => 'applications/differential/field/specification/DifferentialDiffViewPolicyFieldSpecification.php',
|
||||
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'applications/differential/doorkeeper/DifferentialDoorkeeperRevisionFeedStoryPublisher.php',
|
||||
'DifferentialException' => 'applications/differential/exception/DifferentialException.php',
|
||||
'DifferentialExceptionMail' => 'applications/differential/mail/DifferentialExceptionMail.php',
|
||||
'DifferentialExportPatchFieldSpecification' => 'applications/differential/field/specification/DifferentialExportPatchFieldSpecification.php',
|
||||
|
@ -541,6 +542,7 @@ phutil_register_library_map(array(
|
|||
'DoorkeeperDAO' => 'applications/doorkeeper/storage/DoorkeeperDAO.php',
|
||||
'DoorkeeperExternalObject' => 'applications/doorkeeper/storage/DoorkeeperExternalObject.php',
|
||||
'DoorkeeperExternalObjectQuery' => 'applications/doorkeeper/query/DoorkeeperExternalObjectQuery.php',
|
||||
'DoorkeeperFeedStoryPublisher' => 'applications/doorkeeper/engine/DoorkeeperFeedStoryPublisher.php',
|
||||
'DoorkeeperFeedWorkerAsana' => 'applications/doorkeeper/worker/DoorkeeperFeedWorkerAsana.php',
|
||||
'DoorkeeperImportEngine' => 'applications/doorkeeper/engine/DoorkeeperImportEngine.php',
|
||||
'DoorkeeperObjectRef' => 'applications/doorkeeper/engine/DoorkeeperObjectRef.php',
|
||||
|
@ -2335,6 +2337,7 @@ phutil_register_library_map(array(
|
|||
'DifferentialDiffTestCase' => 'ArcanistPhutilTestCase',
|
||||
'DifferentialDiffViewController' => 'DifferentialController',
|
||||
'DifferentialDiffViewPolicyFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
'DifferentialDoorkeeperRevisionFeedStoryPublisher' => 'DoorkeeperFeedStoryPublisher',
|
||||
'DifferentialException' => 'Exception',
|
||||
'DifferentialExceptionMail' => 'DifferentialMail',
|
||||
'DifferentialExportPatchFieldSpecification' => 'DifferentialFieldSpecification',
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
<?php
|
||||
|
||||
final class DifferentialDoorkeeperRevisionFeedStoryPublisher
|
||||
extends DoorkeeperFeedStoryPublisher {
|
||||
|
||||
public function canPublishStory(PhabricatorFeedStory $story, $object) {
|
||||
return ($object instanceof DifferentialRevision);
|
||||
}
|
||||
|
||||
public function willPublishStory($object) {
|
||||
return id(new DifferentialRevisionQuery())
|
||||
->setViewer($this->getViewer())
|
||||
->withIDs(array($object->getID()))
|
||||
->needRelationships(true)
|
||||
->executeOne();
|
||||
}
|
||||
|
||||
public function getOwnerPHID($object) {
|
||||
return $object->getAuthorPHID();
|
||||
}
|
||||
|
||||
public function getActiveUserPHIDs($object) {
|
||||
$status = $object->getStatus();
|
||||
if ($status == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
|
||||
return $object->getReviewers();
|
||||
} else {
|
||||
return array();
|
||||
}
|
||||
}
|
||||
|
||||
public function getPassiveUserPHIDs($object) {
|
||||
$status = $object->getStatus();
|
||||
if ($status == ArcanistDifferentialRevisionStatus::NEEDS_REVIEW) {
|
||||
return array();
|
||||
} else {
|
||||
return $object->getReviewers();
|
||||
}
|
||||
}
|
||||
|
||||
public function getCCUserPHIDs($object) {
|
||||
return $object->getCCPHIDs();
|
||||
}
|
||||
|
||||
public function getObjectTitle($object) {
|
||||
$prefix = $this->getTitlePrefix($object);
|
||||
|
||||
$lines = new PhutilNumber($object->getLineCount());
|
||||
$lines = pht('[Request, %d lines]', $lines);
|
||||
|
||||
$id = $object->getID();
|
||||
|
||||
$title = $object->getTitle();
|
||||
|
||||
return "{$prefix} {$lines} D{$id}: {$title}";
|
||||
}
|
||||
|
||||
public function getObjectURI($object) {
|
||||
return PhabricatorEnv::getProductionURI('/D'.$object->getID());
|
||||
}
|
||||
|
||||
public function getObjectDescription($object) {
|
||||
return $object->getSummary();
|
||||
}
|
||||
|
||||
public function isObjectClosed($object) {
|
||||
switch ($object->getStatus()) {
|
||||
case ArcanistDifferentialRevisionStatus::CLOSED:
|
||||
case ArcanistDifferentialRevisionStatus::ABANDONED:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
public function getResponsibilityTitle($object) {
|
||||
$prefix = $this->getTitlePrefix($object);
|
||||
return pht('%s Review Request', $prefix);
|
||||
}
|
||||
|
||||
public function getStoryText($object) {
|
||||
$story = $this->getFeedStory();
|
||||
if ($story instanceof PhabricatorFeedStoryDifferential) {
|
||||
$text = $story->renderForAsanaBridge();
|
||||
} else {
|
||||
$text = $story->renderText();
|
||||
}
|
||||
return $text;
|
||||
}
|
||||
|
||||
private function getTitlePrefix(DifferentialRevision $revision) {
|
||||
$prefix_key = 'metamta.differential.subject-prefix';
|
||||
return PhabricatorEnv::getEnvConfig($prefix_key);
|
||||
}
|
||||
|
||||
}
|
|
@ -0,0 +1,49 @@
|
|||
<?php
|
||||
|
||||
abstract class DoorkeeperFeedStoryPublisher {
|
||||
|
||||
private $feedStory;
|
||||
private $viewer;
|
||||
|
||||
public function setFeedStory(PhabricatorFeedStory $feed_story) {
|
||||
$this->feedStory = $feed_story;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getFeedStory() {
|
||||
return $this->feedStory;
|
||||
}
|
||||
|
||||
public function setViewer(PhabricatorUser $viewer) {
|
||||
$this->viewer = $viewer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getViewer() {
|
||||
return $this->viewer;
|
||||
}
|
||||
|
||||
abstract public function canPublishStory(
|
||||
PhabricatorFeedStory $story,
|
||||
$object);
|
||||
|
||||
/**
|
||||
* Hook for publishers to mutate the story object, particularly by loading
|
||||
* and attaching additional data.
|
||||
*/
|
||||
public function willPublishStory($object) {
|
||||
return $object;
|
||||
}
|
||||
|
||||
abstract public function getOwnerPHID($object);
|
||||
abstract public function getActiveUserPHIDs($object);
|
||||
abstract public function getPassiveUserPHIDs($object);
|
||||
abstract public function getCCUserPHIDs($object);
|
||||
abstract public function getObjectTitle($object);
|
||||
abstract public function getObjectURI($object);
|
||||
abstract public function getObjectDescription($object);
|
||||
abstract public function isObjectClosed($object);
|
||||
abstract public function getResponsibilityTitle($object);
|
||||
abstract public function getStoryText($object);
|
||||
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
||||
|
||||
private $provider;
|
||||
private $publisher;
|
||||
private $workspaceID;
|
||||
private $feedStory;
|
||||
private $storyObject;
|
||||
|
@ -43,6 +44,10 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
return PhabricatorUser::getOmnipotentUser();
|
||||
}
|
||||
|
||||
private function getPublisher() {
|
||||
return $this->publisher;
|
||||
}
|
||||
|
||||
private function getStoryObject() {
|
||||
if (!$this->storyObject) {
|
||||
$story = $this->getFeedStory();
|
||||
|
@ -57,81 +62,38 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
return $this->storyObject;
|
||||
}
|
||||
|
||||
private function isObjectSupported($object) {
|
||||
return ($object instanceof DifferentialRevision);
|
||||
}
|
||||
|
||||
private function getRelatedUserPHIDs($object) {
|
||||
$revision = $object;
|
||||
$revision->loadRelationships();
|
||||
|
||||
$author_phid = $revision->getAuthorPHID();
|
||||
$reviewer_phids = $revision->getReviewers();
|
||||
$cc_phids = $revision->getCCPHIDs();
|
||||
|
||||
switch ($revision->getStatus()) {
|
||||
case ArcanistDifferentialRevisionStatus::NEEDS_REVIEW:
|
||||
$active_phids = $reviewer_phids;
|
||||
$passive_phids = array();
|
||||
break;
|
||||
default:
|
||||
$active_phids = array();
|
||||
$passive_phids = $reviewer_phids;
|
||||
break;
|
||||
}
|
||||
|
||||
return array(
|
||||
$author_phid,
|
||||
$active_phids,
|
||||
$passive_phids,
|
||||
$cc_phids);
|
||||
}
|
||||
|
||||
private function getAsanaTaskData($object) {
|
||||
$revision = $object;
|
||||
$prefix = $this->getTitlePrefix($object);
|
||||
$title = $revision->getTitle();
|
||||
$lines = pht(
|
||||
'[Request, %d lines]',
|
||||
new PhutilNumber($object->getLineCount()));
|
||||
$publisher = $this->getPublisher();
|
||||
|
||||
$name = $prefix.' '.$lines.' D'.$revision->getID().': '.$title;
|
||||
$uri = PhabricatorEnv::getProductionURI('/D'.$revision->getID());
|
||||
$title = $publisher->getObjectTitle($object);
|
||||
$uri = $publisher->getObjectURI($object);
|
||||
$description = $publisher->getObjectDescription($object);
|
||||
$is_completed = $publisher->isObjectClosed($object);
|
||||
|
||||
$notes = array(
|
||||
$revision->getSummary(),
|
||||
$description,
|
||||
$uri,
|
||||
$this->getSynchronizationWarning(),
|
||||
);
|
||||
|
||||
$notes = implode("\n\n", $notes);
|
||||
|
||||
switch ($revision->getStatus()) {
|
||||
case ArcanistDifferentialRevisionStatus::CLOSED:
|
||||
case ArcanistDifferentialRevisionStatus::ABANDONED:
|
||||
$is_completed = true;
|
||||
break;
|
||||
default:
|
||||
$is_completed = false;
|
||||
break;
|
||||
}
|
||||
|
||||
return array(
|
||||
'name' => $name,
|
||||
'name' => $title,
|
||||
'notes' => $notes,
|
||||
'completed' => $is_completed,
|
||||
);
|
||||
}
|
||||
|
||||
private function getAsanaSubtaskData($object) {
|
||||
$revision = $object;
|
||||
$prefix = $this->getTitlePrefix($object);
|
||||
$publisher = $this->getPublisher();
|
||||
|
||||
$name = $prefix.' Review Request';
|
||||
$uri = PhabricatorEnv::getProductionURI('/D'.$revision->getID());
|
||||
$title = $publisher->getResponsibilityTitle($object);
|
||||
$uri = $publisher->getObjectURI($object);
|
||||
$description = $publisher->getObjectDescription($object);
|
||||
|
||||
$notes = array(
|
||||
$revision->getSummary(),
|
||||
$description,
|
||||
$uri,
|
||||
$this->getSynchronizationWarning(),
|
||||
);
|
||||
|
@ -139,7 +101,7 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
$notes = implode("\n\n", $notes);
|
||||
|
||||
return array(
|
||||
'name' => $prefix.' Review Request',
|
||||
'name' => $title,
|
||||
'notes' => $notes,
|
||||
);
|
||||
}
|
||||
|
@ -165,7 +127,25 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
|
||||
$chronological_key = $story->getChronologicalKey();
|
||||
|
||||
if (!$this->isObjectSupported($object)) {
|
||||
$publishers = id(new PhutilSymbolLoader())
|
||||
->setAncestorClass('DoorkeeperFeedStoryPublisher')
|
||||
->loadObjects();
|
||||
foreach ($publishers as $publisher) {
|
||||
if ($publisher->canPublishStory($story, $object)) {
|
||||
$publisher
|
||||
->setViewer($viewer)
|
||||
->setFeedStory($story);
|
||||
|
||||
$object = $publisher->willPublishStory($object);
|
||||
$this->storyObject = $object;
|
||||
|
||||
$this->publisher = $publisher;
|
||||
$this->log("Using publisher '%s'.\n", get_class($publisher));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$this->publisher) {
|
||||
$this->log("Story is about an unsupported object type.\n");
|
||||
return;
|
||||
}
|
||||
|
@ -182,8 +162,10 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
// revision.
|
||||
// - Follow: users who are following the object; generally CCs.
|
||||
|
||||
$phids = $this->getRelatedUserPHIDs($object);
|
||||
list($owner_phid, $active_phids, $passive_phids, $follow_phids) = $phids;
|
||||
$owner_phid = $publisher->getOwnerPHID($object);
|
||||
$active_phids = $publisher->getActiveUserPHIDs($object);
|
||||
$passive_phids = $publisher->getPassiveUserPHIDs($object);
|
||||
$follow_phids = $publisher->getCCUserPHIDs($object);
|
||||
|
||||
$all_phids = array();
|
||||
$all_phids = array_merge(
|
||||
|
@ -499,11 +481,7 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
// because everything else is idempotent, so this is the only effect we
|
||||
// can't safely run more than once.
|
||||
|
||||
if ($story instanceof PhabricatorFeedStoryDifferential) {
|
||||
$text = $story->renderForAsanaBridge();
|
||||
} else {
|
||||
$text = $story->renderText();
|
||||
}
|
||||
$text = $publisher->getStoryText($object);
|
||||
|
||||
$this->makeAsanaAPICall(
|
||||
$oauth_token,
|
||||
|
@ -642,8 +620,4 @@ final class DoorkeeperFeedWorkerAsana extends FeedPushWorker {
|
|||
return (5 * 60) * pow(8, $count);
|
||||
}
|
||||
|
||||
public function getTitlePrefix($object) {
|
||||
return PhabricatorEnv::getEnvConfig('metamta.differential.subject-prefix');
|
||||
}
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue