@recaptime-dev's working patches + fork for Phorge, a community fork of Phabricator. (Upstream dev and stable branches are at upstream/main and upstream/stable respectively.) hq.recaptime.dev/wiki/Phorge
phorge phabricator

Add skeleton code for Almanac Interfaces to have real transactions

Summary:
Depends on D19322. Ref T13120. Ref T12414.

Currently, `AlmanacDevice` has a bit of a beast of a `TYPE_INTERFACE` transaction that fully creates a complex Interface object. This isn't very flexible or consistent, and Interfaces are complex enough to reasonably have their own object behaviors (for example, they have their own PHIDs).

The complexity of this transaction makes modularizing `AlmanacDevice` transactions tricky. To simplify this, move Interface toward having its own set of normal transactions.

This change just adds some reasonable-looking transactions; it doesn't actually hook them up in the UI or make them reachable. I'll test that they actually work as I swap the UI over.

We may also have some code using the `TYPE_INTERFACE` transaction in Phacility support stuff, so that may need to wait a week to actually phase out.

Test Plan: Ran `bin/storage upgrade` and `arc liberate`. This code isn't reachable yet.

Reviewers: amckinley

Reviewed By: amckinley

Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam

Maniphest Tasks: T13120, T12414

Differential Revision: https://secure.phabricator.com/D19323

+316 -1
+19
resources/sql/autopatches/20180410.almanac.01.iface.xaction.sql
··· 1 + CREATE TABLE {$NAMESPACE}_almanac.almanac_interfacetransaction ( 2 + id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY, 3 + phid VARBINARY(64) NOT NULL, 4 + authorPHID VARBINARY(64) NOT NULL, 5 + objectPHID VARBINARY(64) NOT NULL, 6 + viewPolicy VARBINARY(64) NOT NULL, 7 + editPolicy VARBINARY(64) NOT NULL, 8 + commentPHID VARBINARY(64) DEFAULT NULL, 9 + commentVersion INT UNSIGNED NOT NULL, 10 + transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL, 11 + oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 12 + newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 13 + contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 14 + metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL, 15 + dateCreated INT UNSIGNED NOT NULL, 16 + dateModified INT UNSIGNED NOT NULL, 17 + UNIQUE KEY `key_phid` (`phid`), 18 + KEY `key_object` (`objectPHID`) 19 + ) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};
+15
src/__phutil_library_map__.php
··· 57 57 'AlmanacDrydockPoolServiceType' => 'applications/almanac/servicetype/AlmanacDrydockPoolServiceType.php', 58 58 'AlmanacEditor' => 'applications/almanac/editor/AlmanacEditor.php', 59 59 'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php', 60 + 'AlmanacInterfaceAddressTransaction' => 'applications/almanac/xaction/AlmanacInterfaceAddressTransaction.php', 60 61 'AlmanacInterfaceDatasource' => 'applications/almanac/typeahead/AlmanacInterfaceDatasource.php', 61 62 'AlmanacInterfaceDeleteController' => 'applications/almanac/controller/AlmanacInterfaceDeleteController.php', 63 + 'AlmanacInterfaceDeviceTransaction' => 'applications/almanac/xaction/AlmanacInterfaceDeviceTransaction.php', 62 64 'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php', 65 + 'AlmanacInterfaceEditor' => 'applications/almanac/editor/AlmanacInterfaceEditor.php', 66 + 'AlmanacInterfaceNetworkTransaction' => 'applications/almanac/xaction/AlmanacInterfaceNetworkTransaction.php', 63 67 'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php', 68 + 'AlmanacInterfacePortTransaction' => 'applications/almanac/xaction/AlmanacInterfacePortTransaction.php', 64 69 'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php', 65 70 'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php', 71 + 'AlmanacInterfaceTransaction' => 'applications/almanac/storage/AlmanacInterfaceTransaction.php', 72 + 'AlmanacInterfaceTransactionType' => 'applications/almanac/xaction/AlmanacInterfaceTransactionType.php', 66 73 'AlmanacKeys' => 'applications/almanac/util/AlmanacKeys.php', 67 74 'AlmanacManageClusterServicesCapability' => 'applications/almanac/capability/AlmanacManageClusterServicesCapability.php', 68 75 'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php', ··· 5243 5250 'PhabricatorPolicyInterface', 5244 5251 'PhabricatorDestructibleInterface', 5245 5252 'PhabricatorExtendedPolicyInterface', 5253 + 'PhabricatorApplicationTransactionInterface', 5246 5254 ), 5255 + 'AlmanacInterfaceAddressTransaction' => 'AlmanacNetworkTransactionType', 5247 5256 'AlmanacInterfaceDatasource' => 'PhabricatorTypeaheadDatasource', 5248 5257 'AlmanacInterfaceDeleteController' => 'AlmanacDeviceController', 5258 + 'AlmanacInterfaceDeviceTransaction' => 'AlmanacNetworkTransactionType', 5249 5259 'AlmanacInterfaceEditController' => 'AlmanacDeviceController', 5260 + 'AlmanacInterfaceEditor' => 'PhabricatorApplicationTransactionEditor', 5261 + 'AlmanacInterfaceNetworkTransaction' => 'AlmanacNetworkTransactionType', 5250 5262 'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType', 5263 + 'AlmanacInterfacePortTransaction' => 'AlmanacNetworkTransactionType', 5251 5264 'AlmanacInterfaceQuery' => 'AlmanacQuery', 5252 5265 'AlmanacInterfaceTableView' => 'AphrontView', 5266 + 'AlmanacInterfaceTransaction' => 'PhabricatorModularTransaction', 5267 + 'AlmanacInterfaceTransactionType' => 'AlmanacTransactionType', 5253 5268 'AlmanacKeys' => 'Phobject', 5254 5269 'AlmanacManageClusterServicesCapability' => 'PhabricatorPolicyCapability', 5255 5270 'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',
+22
src/applications/almanac/editor/AlmanacInterfaceEditor.php
··· 1 + <?php 2 + 3 + final class AlmanacInterfaceEditor 4 + extends PhabricatorApplicationTransactionEditor { 5 + 6 + public function getEditorApplicationClass() { 7 + return 'PhabricatorAlmanacApplication'; 8 + } 9 + 10 + public function getEditorObjectsDescription() { 11 + return pht('Almanac Interface'); 12 + } 13 + 14 + public function getCreateObjectTitle($author, $object) { 15 + return pht('%s created this interface.', $author); 16 + } 17 + 18 + public function getCreateObjectTitleForFeed($author, $object) { 19 + return pht('%s created %s.', $author, $object); 20 + } 21 + 22 + }
+24 -1
src/applications/almanac/storage/AlmanacInterface.php
··· 5 5 implements 6 6 PhabricatorPolicyInterface, 7 7 PhabricatorDestructibleInterface, 8 - PhabricatorExtendedPolicyInterface { 8 + PhabricatorExtendedPolicyInterface, 9 + PhabricatorApplicationTransactionInterface { 9 10 10 11 protected $devicePHID; 11 12 protected $networkPHID; ··· 152 153 } 153 154 154 155 $this->delete(); 156 + } 157 + 158 + 159 + /* -( PhabricatorApplicationTransactionInterface )------------------------- */ 160 + 161 + 162 + public function getApplicationTransactionEditor() { 163 + return new AlmanacInterfaceEditor(); 164 + } 165 + 166 + public function getApplicationTransactionObject() { 167 + return $this; 168 + } 169 + 170 + public function getApplicationTransactionTemplate() { 171 + return new AlmanacInterfaceTransaction(); 172 + } 173 + 174 + public function willRenderTimeline( 175 + PhabricatorApplicationTransactionView $timeline, 176 + AphrontRequest $request) { 177 + return $timeline; 155 178 } 156 179 157 180 }
+22
src/applications/almanac/storage/AlmanacInterfaceTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacInterfaceTransaction 4 + extends PhabricatorModularTransaction { 5 + 6 + public function getApplicationName() { 7 + return 'almanac'; 8 + } 9 + 10 + public function getApplicationTransactionType() { 11 + return AlmanacInterfacePHIDType::TYPECONST; 12 + } 13 + 14 + public function getApplicationTransactionCommentObject() { 15 + return null; 16 + } 17 + 18 + public function getBaseTransactionClass() { 19 + return 'AlmanacInterfaceTransactionType'; 20 + } 21 + 22 + }
+44
src/applications/almanac/xaction/AlmanacInterfaceAddressTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacInterfaceAddressTransaction 4 + extends AlmanacNetworkTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'almanac:interface:address'; 7 + 8 + public function generateOldValue($object) { 9 + return $object->getAddress(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $object->setAddress($value); 14 + } 15 + 16 + public function getTitle() { 17 + return pht( 18 + '%s changed the address for this interface from %s to %s.', 19 + $this->renderAuthor(), 20 + $this->renderOldValue(), 21 + $this->renderNewValue()); 22 + } 23 + 24 + public function validateTransactions($object, array $xactions) { 25 + $errors = array(); 26 + 27 + if ($this->isEmptyTextTransaction($object->getAddress(), $xactions)) { 28 + $errors[] = $this->newRequiredError( 29 + pht('Interfaces must have an address.')); 30 + } 31 + 32 + foreach ($xactions as $xaction) { 33 + 34 + // NOTE: For now, we don't validate addresses. We generally expect users 35 + // to provide IPv4 addresses, but it's reasonable for them to provide 36 + // IPv6 addresses, and some installs currently use DNS names. This is 37 + // off-label but works today. 38 + 39 + } 40 + 41 + return $errors; 42 + } 43 + 44 + }
+60
src/applications/almanac/xaction/AlmanacInterfaceDeviceTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacInterfaceDeviceTransaction 4 + extends AlmanacNetworkTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'almanac:interface:device'; 7 + 8 + public function generateOldValue($object) { 9 + return $object->getDevicePHID(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $object->setDevicePHID($value); 14 + } 15 + 16 + public function getTitle() { 17 + return pht( 18 + '%s changed the device for this interface from %s to %s.', 19 + $this->renderAuthor(), 20 + $this->renderOldHandle(), 21 + $this->renderNewHandle()); 22 + } 23 + 24 + public function validateTransactions($object, array $xactions) { 25 + $errors = array(); 26 + 27 + if ($this->isEmptyTextTransaction($object->getAddress(), $xactions)) { 28 + $errors[] = $this->newRequiredError( 29 + pht('Interfaces must have a device.')); 30 + } 31 + 32 + foreach ($xactions as $xaction) { 33 + if (!$this->isNewObject()) { 34 + $errors[] = $this->newInvalidError( 35 + pht( 36 + 'The device for an interface can not be changed once it has '. 37 + 'been created.'), 38 + $xaction); 39 + continue; 40 + } 41 + 42 + $device_phid = $xaction->getNewValue(); 43 + $devices = id(new AlmanacDeviceQuery()) 44 + ->setViewer($this->getActor()) 45 + ->withPHIDs(array($device_phid)) 46 + ->execute(); 47 + if (!$devices) { 48 + $errors[] = $this->newInvalidError( 49 + pht( 50 + 'You can not attach an interface to a nonexistent or restricted '. 51 + 'device.'), 52 + $xaction); 53 + continue; 54 + } 55 + } 56 + 57 + return $errors; 58 + } 59 + 60 + }
+53
src/applications/almanac/xaction/AlmanacInterfaceNetworkTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacInterfaceNetworkTransaction 4 + extends AlmanacNetworkTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'almanac:interface:network'; 7 + 8 + public function generateOldValue($object) { 9 + return $object->getNetworkPHID(); 10 + } 11 + 12 + public function applyInternalEffects($object, $value) { 13 + $object->setNetworkPHID($value); 14 + } 15 + 16 + public function getTitle() { 17 + return pht( 18 + '%s changed the network for this interface from %s to %s.', 19 + $this->renderAuthor(), 20 + $this->renderOldHandle(), 21 + $this->renderNewHandle()); 22 + } 23 + 24 + public function validateTransactions($object, array $xactions) { 25 + $errors = array(); 26 + 27 + $network_phid = $object->getNetworkPHID(); 28 + if ($this->isEmptyTextTransaction($network_phid, $xactions)) { 29 + $errors[] = $this->newRequiredError( 30 + pht('Interfaces must have a network.')); 31 + } 32 + 33 + foreach ($xactions as $xaction) { 34 + $network_phid = $xaction->getNewValue(); 35 + 36 + $networks = id(new AlmanacNetworkQuery()) 37 + ->setViewer($this->getActor()) 38 + ->withPHIDs(array($network_phid)) 39 + ->execute(); 40 + if (!$networks) { 41 + $errors[] = $this->newInvalidError( 42 + pht( 43 + 'You can not put an interface on a nonexistent or restricted '. 44 + 'network.'), 45 + $xaction); 46 + continue; 47 + } 48 + } 49 + 50 + return $errors; 51 + } 52 + 53 + }
+53
src/applications/almanac/xaction/AlmanacInterfacePortTransaction.php
··· 1 + <?php 2 + 3 + final class AlmanacInterfacePortTransaction 4 + extends AlmanacNetworkTransactionType { 5 + 6 + const TRANSACTIONTYPE = 'almanac:interface:port'; 7 + 8 + public function generateOldValue($object) { 9 + $port = $object->getPort(); 10 + 11 + if ($port !== null) { 12 + $port = (int)$port; 13 + } 14 + 15 + return $port; 16 + } 17 + 18 + public function applyInternalEffects($object, $value) { 19 + $object->setPort((int)$value); 20 + } 21 + 22 + public function getTitle() { 23 + return pht( 24 + '%s changed the port for this interface from %s to %s.', 25 + $this->renderAuthor(), 26 + $this->renderOldValue(), 27 + $this->renderNewValue()); 28 + } 29 + 30 + public function validateTransactions($object, array $xactions) { 31 + $errors = array(); 32 + 33 + if ($this->isEmptyTextTransaction($object->getName(), $xactions)) { 34 + $errors[] = $this->newRequiredError( 35 + pht('Interfaces must have a port number.')); 36 + } 37 + 38 + foreach ($xactions as $xaction) { 39 + $port = $xaction->getNewValue(); 40 + 41 + $port = (int)$port; 42 + if ($port < 1 || $port > 65535) { 43 + $errors[] = $this->newInvalidError( 44 + pht('Port numbers must be between 1 and 65535, inclusive.'), 45 + $xaction); 46 + continue; 47 + } 48 + } 49 + 50 + return $errors; 51 + } 52 + 53 + }
+4
src/applications/almanac/xaction/AlmanacInterfaceTransactionType.php
··· 1 + <?php 2 + 3 + abstract class AlmanacInterfaceTransactionType 4 + extends AlmanacTransactionType {}