3 final class HeraldWebhook
6 PhabricatorPolicyInterface
,
7 PhabricatorApplicationTransactionInterface
,
8 PhabricatorDestructibleInterface
,
9 PhabricatorProjectInterface
{
12 protected $webhookURI;
13 protected $viewPolicy;
14 protected $editPolicy;
18 const HOOKSTATUS_FIREHOSE
= 'firehose';
19 const HOOKSTATUS_ENABLED
= 'enabled';
20 const HOOKSTATUS_DISABLED
= 'disabled';
22 protected function getConfiguration() {
24 self
::CONFIG_AUX_PHID
=> true,
25 self
::CONFIG_COLUMN_SCHEMA
=> array(
27 'webhookURI' => 'text255',
29 'hmacKey' => 'text32',
31 self
::CONFIG_KEY_SCHEMA
=> array(
32 'key_status' => array(
33 'columns' => array('status'),
36 ) + parent
::getConfiguration();
39 public function getPHIDType() {
40 return HeraldWebhookPHIDType
::TYPECONST
;
43 public static function initializeNewWebhook(PhabricatorUser
$viewer) {
45 ->setStatus(self
::HOOKSTATUS_ENABLED
)
46 ->setViewPolicy(PhabricatorPolicies
::getMostOpenPolicy())
47 ->setEditPolicy($viewer->getPHID())
48 ->regenerateHMACKey();
51 public function getURI() {
52 return '/herald/webhook/view/'.$this->getID().'/';
55 public function isDisabled() {
56 return ($this->getStatus() === self
::HOOKSTATUS_DISABLED
);
59 public static function getStatusDisplayNameMap() {
60 $specs = self
::getStatusSpecifications();
61 return ipull($specs, 'name', 'key');
64 private static function getStatusSpecifications() {
67 'key' => self
::HOOKSTATUS_FIREHOSE
,
68 'name' => pht('Firehose'),
70 'icon' => 'fa-star-o',
73 'key' => self
::HOOKSTATUS_ENABLED
,
74 'name' => pht('Enabled'),
75 'color' => 'bluegrey',
79 'key' => self
::HOOKSTATUS_DISABLED
,
80 'name' => pht('Disabled'),
86 return ipull($specs, null, 'key');
90 private static function getSpecificationForStatus($status) {
91 $specs = self
::getStatusSpecifications();
93 if (isset($specs[$status])) {
94 return $specs[$status];
99 'name' => pht('Unknown ("%s")', $status),
100 'icon' => 'fa-question',
105 public static function getDisplayNameForStatus($status) {
106 $spec = self
::getSpecificationForStatus($status);
107 return $spec['name'];
110 public static function getIconForStatus($status) {
111 $spec = self
::getSpecificationForStatus($status);
112 return $spec['icon'];
115 public static function getColorForStatus($status) {
116 $spec = self
::getSpecificationForStatus($status);
117 return $spec['color'];
120 public function getStatusDisplayName() {
121 $status = $this->getStatus();
122 return self
::getDisplayNameForStatus($status);
125 public function getStatusIcon() {
126 $status = $this->getStatus();
127 return self
::getIconForStatus($status);
130 public function getStatusColor() {
131 $status = $this->getStatus();
132 return self
::getColorForStatus($status);
135 public function getErrorBackoffWindow() {
136 return phutil_units('5 minutes in seconds');
139 public function getErrorBackoffThreshold() {
143 public function isInErrorBackoff(PhabricatorUser
$viewer) {
144 $backoff_window = $this->getErrorBackoffWindow();
145 $backoff_threshold = $this->getErrorBackoffThreshold();
147 $now = PhabricatorTime
::getNow();
149 $window_start = ($now - $backoff_window);
151 $requests = id(new HeraldWebhookRequestQuery())
153 ->withWebhookPHIDs(array($this->getPHID()))
154 ->withLastRequestEpochBetween($window_start, null)
155 ->withLastRequestResults(
157 HeraldWebhookRequest
::RESULT_FAIL
,
161 if (count($requests) >= $backoff_threshold) {
168 public function regenerateHMACKey() {
169 return $this->setHMACKey(Filesystem
::readRandomCharacters(32));
172 /* -( PhabricatorPolicyInterface )----------------------------------------- */
175 public function getCapabilities() {
177 PhabricatorPolicyCapability
::CAN_VIEW
,
178 PhabricatorPolicyCapability
::CAN_EDIT
,
182 public function getPolicy($capability) {
183 switch ($capability) {
184 case PhabricatorPolicyCapability
::CAN_VIEW
:
185 return $this->getViewPolicy();
186 case PhabricatorPolicyCapability
::CAN_EDIT
:
187 return $this->getEditPolicy();
191 public function hasAutomaticCapability($capability, PhabricatorUser
$viewer) {
196 /* -( PhabricatorApplicationTransactionInterface )------------------------- */
199 public function getApplicationTransactionEditor() {
200 return new HeraldWebhookEditor();
203 public function getApplicationTransactionTemplate() {
204 return new HeraldWebhookTransaction();
208 /* -( PhabricatorDestructibleInterface )----------------------------------- */
211 public function destroyObjectPermanently(
212 PhabricatorDestructionEngine
$engine) {
215 $requests = id(new HeraldWebhookRequestQuery())
216 ->setViewer($engine->getViewer())
217 ->withWebhookPHIDs(array($this->getPHID()))
225 foreach ($requests as $request) {