[ Index ] |
PHP Cross Reference of Phabricator |
[Summary view] [Print] [Text view]
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 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
Generated: Sun Nov 30 09:20:46 2014 | Cross-referenced by PHPXref 0.7.1 |