1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-22 14:52:41 +01:00

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
This commit is contained in:
epriestley 2018-04-10 05:50:49 -07:00
parent 580409b562
commit f9c6a69d9c
10 changed files with 316 additions and 1 deletions

View file

@ -0,0 +1,19 @@
CREATE TABLE {$NAMESPACE}_almanac.almanac_interfacetransaction (
id INT UNSIGNED NOT NULL AUTO_INCREMENT PRIMARY KEY,
phid VARBINARY(64) NOT NULL,
authorPHID VARBINARY(64) NOT NULL,
objectPHID VARBINARY(64) NOT NULL,
viewPolicy VARBINARY(64) NOT NULL,
editPolicy VARBINARY(64) NOT NULL,
commentPHID VARBINARY(64) DEFAULT NULL,
commentVersion INT UNSIGNED NOT NULL,
transactionType VARCHAR(32) COLLATE {$COLLATE_TEXT} NOT NULL,
oldValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
newValue LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
contentSource LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
metadata LONGTEXT COLLATE {$COLLATE_TEXT} NOT NULL,
dateCreated INT UNSIGNED NOT NULL,
dateModified INT UNSIGNED NOT NULL,
UNIQUE KEY `key_phid` (`phid`),
KEY `key_object` (`objectPHID`)
) ENGINE=InnoDB, COLLATE {$COLLATE_TEXT};

View file

@ -57,12 +57,19 @@ phutil_register_library_map(array(
'AlmanacDrydockPoolServiceType' => 'applications/almanac/servicetype/AlmanacDrydockPoolServiceType.php',
'AlmanacEditor' => 'applications/almanac/editor/AlmanacEditor.php',
'AlmanacInterface' => 'applications/almanac/storage/AlmanacInterface.php',
'AlmanacInterfaceAddressTransaction' => 'applications/almanac/xaction/AlmanacInterfaceAddressTransaction.php',
'AlmanacInterfaceDatasource' => 'applications/almanac/typeahead/AlmanacInterfaceDatasource.php',
'AlmanacInterfaceDeleteController' => 'applications/almanac/controller/AlmanacInterfaceDeleteController.php',
'AlmanacInterfaceDeviceTransaction' => 'applications/almanac/xaction/AlmanacInterfaceDeviceTransaction.php',
'AlmanacInterfaceEditController' => 'applications/almanac/controller/AlmanacInterfaceEditController.php',
'AlmanacInterfaceEditor' => 'applications/almanac/editor/AlmanacInterfaceEditor.php',
'AlmanacInterfaceNetworkTransaction' => 'applications/almanac/xaction/AlmanacInterfaceNetworkTransaction.php',
'AlmanacInterfacePHIDType' => 'applications/almanac/phid/AlmanacInterfacePHIDType.php',
'AlmanacInterfacePortTransaction' => 'applications/almanac/xaction/AlmanacInterfacePortTransaction.php',
'AlmanacInterfaceQuery' => 'applications/almanac/query/AlmanacInterfaceQuery.php',
'AlmanacInterfaceTableView' => 'applications/almanac/view/AlmanacInterfaceTableView.php',
'AlmanacInterfaceTransaction' => 'applications/almanac/storage/AlmanacInterfaceTransaction.php',
'AlmanacInterfaceTransactionType' => 'applications/almanac/xaction/AlmanacInterfaceTransactionType.php',
'AlmanacKeys' => 'applications/almanac/util/AlmanacKeys.php',
'AlmanacManageClusterServicesCapability' => 'applications/almanac/capability/AlmanacManageClusterServicesCapability.php',
'AlmanacManagementRegisterWorkflow' => 'applications/almanac/management/AlmanacManagementRegisterWorkflow.php',
@ -5243,13 +5250,21 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface',
'PhabricatorDestructibleInterface',
'PhabricatorExtendedPolicyInterface',
'PhabricatorApplicationTransactionInterface',
),
'AlmanacInterfaceAddressTransaction' => 'AlmanacNetworkTransactionType',
'AlmanacInterfaceDatasource' => 'PhabricatorTypeaheadDatasource',
'AlmanacInterfaceDeleteController' => 'AlmanacDeviceController',
'AlmanacInterfaceDeviceTransaction' => 'AlmanacNetworkTransactionType',
'AlmanacInterfaceEditController' => 'AlmanacDeviceController',
'AlmanacInterfaceEditor' => 'PhabricatorApplicationTransactionEditor',
'AlmanacInterfaceNetworkTransaction' => 'AlmanacNetworkTransactionType',
'AlmanacInterfacePHIDType' => 'PhabricatorPHIDType',
'AlmanacInterfacePortTransaction' => 'AlmanacNetworkTransactionType',
'AlmanacInterfaceQuery' => 'AlmanacQuery',
'AlmanacInterfaceTableView' => 'AphrontView',
'AlmanacInterfaceTransaction' => 'PhabricatorModularTransaction',
'AlmanacInterfaceTransactionType' => 'AlmanacTransactionType',
'AlmanacKeys' => 'Phobject',
'AlmanacManageClusterServicesCapability' => 'PhabricatorPolicyCapability',
'AlmanacManagementRegisterWorkflow' => 'AlmanacManagementWorkflow',

View file

@ -0,0 +1,22 @@
<?php
final class AlmanacInterfaceEditor
extends PhabricatorApplicationTransactionEditor {
public function getEditorApplicationClass() {
return 'PhabricatorAlmanacApplication';
}
public function getEditorObjectsDescription() {
return pht('Almanac Interface');
}
public function getCreateObjectTitle($author, $object) {
return pht('%s created this interface.', $author);
}
public function getCreateObjectTitleForFeed($author, $object) {
return pht('%s created %s.', $author, $object);
}
}

View file

@ -5,7 +5,8 @@ final class AlmanacInterface
implements
PhabricatorPolicyInterface,
PhabricatorDestructibleInterface,
PhabricatorExtendedPolicyInterface {
PhabricatorExtendedPolicyInterface,
PhabricatorApplicationTransactionInterface {
protected $devicePHID;
protected $networkPHID;
@ -154,4 +155,26 @@ final class AlmanacInterface
$this->delete();
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new AlmanacInterfaceEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new AlmanacInterfaceTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
}

View file

@ -0,0 +1,22 @@
<?php
final class AlmanacInterfaceTransaction
extends PhabricatorModularTransaction {
public function getApplicationName() {
return 'almanac';
}
public function getApplicationTransactionType() {
return AlmanacInterfacePHIDType::TYPECONST;
}
public function getApplicationTransactionCommentObject() {
return null;
}
public function getBaseTransactionClass() {
return 'AlmanacInterfaceTransactionType';
}
}

View file

@ -0,0 +1,44 @@
<?php
final class AlmanacInterfaceAddressTransaction
extends AlmanacNetworkTransactionType {
const TRANSACTIONTYPE = 'almanac:interface:address';
public function generateOldValue($object) {
return $object->getAddress();
}
public function applyInternalEffects($object, $value) {
$object->setAddress($value);
}
public function getTitle() {
return pht(
'%s changed the address for this interface from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getAddress(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Interfaces must have an address.'));
}
foreach ($xactions as $xaction) {
// NOTE: For now, we don't validate addresses. We generally expect users
// to provide IPv4 addresses, but it's reasonable for them to provide
// IPv6 addresses, and some installs currently use DNS names. This is
// off-label but works today.
}
return $errors;
}
}

View file

@ -0,0 +1,60 @@
<?php
final class AlmanacInterfaceDeviceTransaction
extends AlmanacNetworkTransactionType {
const TRANSACTIONTYPE = 'almanac:interface:device';
public function generateOldValue($object) {
return $object->getDevicePHID();
}
public function applyInternalEffects($object, $value) {
$object->setDevicePHID($value);
}
public function getTitle() {
return pht(
'%s changed the device for this interface from %s to %s.',
$this->renderAuthor(),
$this->renderOldHandle(),
$this->renderNewHandle());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getAddress(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Interfaces must have a device.'));
}
foreach ($xactions as $xaction) {
if (!$this->isNewObject()) {
$errors[] = $this->newInvalidError(
pht(
'The device for an interface can not be changed once it has '.
'been created.'),
$xaction);
continue;
}
$device_phid = $xaction->getNewValue();
$devices = id(new AlmanacDeviceQuery())
->setViewer($this->getActor())
->withPHIDs(array($device_phid))
->execute();
if (!$devices) {
$errors[] = $this->newInvalidError(
pht(
'You can not attach an interface to a nonexistent or restricted '.
'device.'),
$xaction);
continue;
}
}
return $errors;
}
}

View file

@ -0,0 +1,53 @@
<?php
final class AlmanacInterfaceNetworkTransaction
extends AlmanacNetworkTransactionType {
const TRANSACTIONTYPE = 'almanac:interface:network';
public function generateOldValue($object) {
return $object->getNetworkPHID();
}
public function applyInternalEffects($object, $value) {
$object->setNetworkPHID($value);
}
public function getTitle() {
return pht(
'%s changed the network for this interface from %s to %s.',
$this->renderAuthor(),
$this->renderOldHandle(),
$this->renderNewHandle());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
$network_phid = $object->getNetworkPHID();
if ($this->isEmptyTextTransaction($network_phid, $xactions)) {
$errors[] = $this->newRequiredError(
pht('Interfaces must have a network.'));
}
foreach ($xactions as $xaction) {
$network_phid = $xaction->getNewValue();
$networks = id(new AlmanacNetworkQuery())
->setViewer($this->getActor())
->withPHIDs(array($network_phid))
->execute();
if (!$networks) {
$errors[] = $this->newInvalidError(
pht(
'You can not put an interface on a nonexistent or restricted '.
'network.'),
$xaction);
continue;
}
}
return $errors;
}
}

View file

@ -0,0 +1,53 @@
<?php
final class AlmanacInterfacePortTransaction
extends AlmanacNetworkTransactionType {
const TRANSACTIONTYPE = 'almanac:interface:port';
public function generateOldValue($object) {
$port = $object->getPort();
if ($port !== null) {
$port = (int)$port;
}
return $port;
}
public function applyInternalEffects($object, $value) {
$object->setPort((int)$value);
}
public function getTitle() {
return pht(
'%s changed the port for this interface from %s to %s.',
$this->renderAuthor(),
$this->renderOldValue(),
$this->renderNewValue());
}
public function validateTransactions($object, array $xactions) {
$errors = array();
if ($this->isEmptyTextTransaction($object->getName(), $xactions)) {
$errors[] = $this->newRequiredError(
pht('Interfaces must have a port number.'));
}
foreach ($xactions as $xaction) {
$port = $xaction->getNewValue();
$port = (int)$port;
if ($port < 1 || $port > 65535) {
$errors[] = $this->newInvalidError(
pht('Port numbers must be between 1 and 65535, inclusive.'),
$xaction);
continue;
}
}
return $errors;
}
}

View file

@ -0,0 +1,4 @@
<?php
abstract class AlmanacInterfaceTransactionType
extends AlmanacTransactionType {}