[ Index ]

PHP Cross Reference of Phabricator

title

Body

[close]

/src/applications/doorkeeper/worker/ -> DoorkeeperFeedWorker.php (source)

   1  <?php
   2  
   3  /**
   4   * Publish events (like comments on a revision) to external objects which are
   5   * linked through Doorkeeper (like a linked JIRA or Asana task).
   6   *
   7   * These workers are invoked by feed infrastructure during normal task queue
   8   * operations. They read feed stories and publish information about them to
   9   * external systems, generally mirroring comments and updates in Phabricator
  10   * into remote systems by making API calls.
  11   *
  12   * @task publish  Publishing Stories
  13   * @task context  Story Context
  14   * @task internal Internals
  15   */
  16  abstract class DoorkeeperFeedWorker extends FeedPushWorker {
  17  
  18    private $publisher;
  19    private $feedStory;
  20    private $storyObject;
  21  
  22  
  23  /* -(  Publishing Stories  )------------------------------------------------- */
  24  
  25  
  26    /**
  27     * Actually publish the feed story. Subclasses will generally make API calls
  28     * to publish some version of the story into external systems.
  29     *
  30     * @return void
  31     * @task publish
  32     */
  33    abstract protected function publishFeedStory();
  34  
  35  
  36    /**
  37     * Enable or disable the worker. Normally, this checks configuration to
  38     * see if Phabricator is linked to applicable external systems.
  39     *
  40     * @return bool True if this worker should try to publish stories.
  41     * @task publish
  42     */
  43    abstract public function isEnabled();
  44  
  45  
  46  /* -(  Story Context  )------------------------------------------------------ */
  47  
  48  
  49    /**
  50     * Get the @{class:PhabricatorFeedStory} that should be published.
  51     *
  52     * @return PhabricatorFeedStory The story to publish.
  53     * @task context
  54     */
  55    protected function getFeedStory() {
  56      if (!$this->feedStory) {
  57        $story = $this->loadFeedStory();
  58        $this->feedStory = $story;
  59      }
  60      return $this->feedStory;
  61    }
  62  
  63  
  64    /**
  65     * Get the viewer for the act of publishing.
  66     *
  67     * NOTE: Publishing currently uses the omnipotent viewer because it depends
  68     * on loading external accounts. Possibly we should tailor this. See T3732.
  69     * Using the actor for most operations might make more sense.
  70     *
  71     * @return PhabricatorUser Viewer.
  72     * @task context
  73     */
  74    protected function getViewer() {
  75      return PhabricatorUser::getOmnipotentUser();
  76    }
  77  
  78  
  79    /**
  80     * Get the @{class:DoorkeeperFeedStoryPublisher} which handles this object.
  81     *
  82     * @return DoorkeeperFeedStoryPublisher Object publisher.
  83     * @task context
  84     */
  85    protected function getPublisher() {
  86      return $this->publisher;
  87    }
  88  
  89  
  90    /**
  91     * Get the primary object the story is about, like a
  92     * @{class:DifferentialRevision} or @{class:ManiphestTask}.
  93     *
  94     * @return object Object which the story is about.
  95     * @task context
  96     */
  97    protected function getStoryObject() {
  98      if (!$this->storyObject) {
  99        $story = $this->getFeedStory();
 100        try {
 101          $object = $story->getPrimaryObject();
 102        } catch (Exception $ex) {
 103          throw new PhabricatorWorkerPermanentFailureException(
 104            $ex->getMessage());
 105        }
 106        $this->storyObject = $object;
 107      }
 108      return $this->storyObject;
 109    }
 110  
 111  
 112  /* -(  Internals  )---------------------------------------------------------- */
 113  
 114  
 115    /**
 116     * Load the @{class:DoorkeeperFeedStoryPublisher} which corresponds to this
 117     * object. Publishers provide a common API for pushing object updates into
 118     * foreign systems.
 119     *
 120     * @return DoorkeeperFeedStoryPublisher Publisher for the story's object.
 121     * @task internal
 122     */
 123    private function loadPublisher() {
 124      $story = $this->getFeedStory();
 125      $viewer = $this->getViewer();
 126      $object = $this->getStoryObject();
 127  
 128      $publishers = id(new PhutilSymbolLoader())
 129        ->setAncestorClass('DoorkeeperFeedStoryPublisher')
 130        ->loadObjects();
 131  
 132      foreach ($publishers as $publisher) {
 133        if (!$publisher->canPublishStory($story, $object)) {
 134          continue;
 135        }
 136  
 137        $publisher
 138          ->setViewer($viewer)
 139          ->setFeedStory($story);
 140  
 141        $object = $publisher->willPublishStory($object);
 142        $this->storyObject = $object;
 143  
 144        $this->publisher = $publisher;
 145        break;
 146      }
 147  
 148      return $this->publisher;
 149    }
 150  
 151  
 152  /* -(  Inherited  )---------------------------------------------------------- */
 153  
 154  
 155    /**
 156     * Doorkeeper workers set up some context, then call
 157     * @{method:publishFeedStory}.
 158     */
 159    final protected function doWork() {
 160      if (!$this->isEnabled()) {
 161        $this->log("Doorkeeper worker '%s' is not enabled.\n", get_class($this));
 162        return;
 163      }
 164  
 165      $publisher = $this->loadPublisher();
 166      if (!$publisher) {
 167        $this->log("Story is about an unsupported object type.\n");
 168        return;
 169      } else {
 170        $this->log("Using publisher '%s'.\n", get_class($publisher));
 171      }
 172  
 173      $this->publishFeedStory();
 174    }
 175  
 176  
 177    /**
 178     * By default, Doorkeeper workers perform a small number of retries with
 179     * exponential backoff. A consideration in this policy is that many of these
 180     * workers are laden with side effects.
 181     */
 182    public function getMaximumRetryCount() {
 183      return 4;
 184    }
 185  
 186  
 187    /**
 188     * See @{method:getMaximumRetryCount} for a description of Doorkeeper
 189     * retry defaults.
 190     */
 191    public function getWaitBeforeRetry(PhabricatorWorkerTask $task) {
 192      $count = $task->getFailureCount();
 193      return (5 * 60) * pow(8, $count);
 194    }
 195  
 196  }


Generated: Sun Nov 30 09:20:46 2014 Cross-referenced by PHPXref 0.7.1