mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-22 13:30:55 +01:00
Add a "status" property to Almanac devices
Summary: Ref T13641. Add a "status" property with most of the relevant support code. This currently has no impact on use of the device or bindings by Diffusion or Drydock: they ignore the status of devices bound to services. Test Plan: - Created a new device. - Changed the status of a device via web and API. - Queried for devices via API. - Searched for active and disabled devices. - Viewed UI in list view, detail view. - Used typeahead to add a new binding to an interface on a disabled device, got disabled hint in typeahead UI. Subscribers: PHID-OPKG-gm6ozazyms6q6i22gyam Maniphest Tasks: T13641 Differential Revision: https://secure.phabricator.com/D21627
This commit is contained in:
parent
9003a45369
commit
5d64fb1815
13 changed files with 274 additions and 2 deletions
|
@ -0,0 +1,2 @@
|
|||
ALTER TABLE {$NAMESPACE}_almanac.almanac_device
|
||||
ADD status VARCHAR(32) NOT NULL COLLATE {$COLLATE_TEXT};
|
|
@ -0,0 +1,2 @@
|
|||
UPDATE {$NAMESPACE}_almanac.almanac_device
|
||||
SET status = 'active' WHERE status = '';
|
|
@ -62,6 +62,8 @@ phutil_register_library_map(array(
|
|||
'AlmanacDeviceSearchConduitAPIMethod' => 'applications/almanac/conduit/AlmanacDeviceSearchConduitAPIMethod.php',
|
||||
'AlmanacDeviceSearchEngine' => 'applications/almanac/query/AlmanacDeviceSearchEngine.php',
|
||||
'AlmanacDeviceSetPropertyTransaction' => 'applications/almanac/xaction/AlmanacDeviceSetPropertyTransaction.php',
|
||||
'AlmanacDeviceStatus' => 'applications/almanac/constants/AlmanacDeviceStatus.php',
|
||||
'AlmanacDeviceStatusTransaction' => 'applications/almanac/xaction/AlmanacDeviceStatusTransaction.php',
|
||||
'AlmanacDeviceTransaction' => 'applications/almanac/storage/AlmanacDeviceTransaction.php',
|
||||
'AlmanacDeviceTransactionQuery' => 'applications/almanac/query/AlmanacDeviceTransactionQuery.php',
|
||||
'AlmanacDeviceTransactionType' => 'applications/almanac/xaction/AlmanacDeviceTransactionType.php',
|
||||
|
@ -6089,6 +6091,8 @@ phutil_register_library_map(array(
|
|||
'AlmanacDeviceSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
|
||||
'AlmanacDeviceSearchEngine' => 'PhabricatorApplicationSearchEngine',
|
||||
'AlmanacDeviceSetPropertyTransaction' => 'AlmanacDeviceTransactionType',
|
||||
'AlmanacDeviceStatus' => 'Phobject',
|
||||
'AlmanacDeviceStatusTransaction' => 'AlmanacDeviceTransactionType',
|
||||
'AlmanacDeviceTransaction' => 'AlmanacModularTransaction',
|
||||
'AlmanacDeviceTransactionQuery' => 'PhabricatorApplicationTransactionQuery',
|
||||
'AlmanacDeviceTransactionType' => 'AlmanacTransactionType',
|
||||
|
|
89
src/applications/almanac/constants/AlmanacDeviceStatus.php
Normal file
89
src/applications/almanac/constants/AlmanacDeviceStatus.php
Normal file
|
@ -0,0 +1,89 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacDeviceStatus
|
||||
extends Phobject {
|
||||
|
||||
const ACTIVE = 'active';
|
||||
const DISABLED = 'disabled';
|
||||
|
||||
private $value;
|
||||
|
||||
public static function newStatusFromValue($value) {
|
||||
$status = new self();
|
||||
$status->value = $value;
|
||||
return $status;
|
||||
}
|
||||
|
||||
public function getValue() {
|
||||
return $this->value;
|
||||
}
|
||||
|
||||
public function getName() {
|
||||
$name = $this->getDeviceStatusProperty('name');
|
||||
|
||||
if ($name === null) {
|
||||
$name = pht('Unknown Almanac Device Status ("%s")', $this->getValue());
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
|
||||
public function getIconIcon() {
|
||||
return $this->getDeviceStatusProperty('icon.icon');
|
||||
}
|
||||
|
||||
public function getIconColor() {
|
||||
return $this->getDeviceStatusProperty('icon.color');
|
||||
}
|
||||
|
||||
public function isDisabled() {
|
||||
return ($this->getValue() === self::DISABLED);
|
||||
}
|
||||
|
||||
public function hasStatusTag() {
|
||||
return ($this->getStatusTagIcon() !== null);
|
||||
}
|
||||
|
||||
public function getStatusTagIcon() {
|
||||
return $this->getDeviceStatusProperty('status-tag.icon');
|
||||
}
|
||||
|
||||
public function getStatusTagColor() {
|
||||
return $this->getDeviceStatusProperty('status-tag.color');
|
||||
}
|
||||
|
||||
public static function getStatusMap() {
|
||||
$result = array();
|
||||
|
||||
foreach (self::newDeviceStatusMap() as $status_value => $ignored) {
|
||||
$result[$status_value] = self::newStatusFromValue($status_value);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function getDeviceStatusProperty($key, $default = null) {
|
||||
$map = self::newDeviceStatusMap();
|
||||
$properties = idx($map, $this->getValue(), array());
|
||||
return idx($properties, $key, $default);
|
||||
}
|
||||
|
||||
private static function newDeviceStatusMap() {
|
||||
return array(
|
||||
self::ACTIVE => array(
|
||||
'name' => pht('Active'),
|
||||
'icon.icon' => 'fa-server',
|
||||
'icon.color' => 'green',
|
||||
),
|
||||
self::DISABLED => array(
|
||||
'name' => pht('Disabled'),
|
||||
'icon.icon' => 'fa-times',
|
||||
'icon.color' => 'grey',
|
||||
'status-tag.icon' => 'fa-times',
|
||||
'status-tag.color' => 'indigo',
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
}
|
|
@ -31,6 +31,14 @@ final class AlmanacDeviceViewController
|
|||
->setPolicyObject($device)
|
||||
->setHeaderIcon('fa-server');
|
||||
|
||||
$status = $device->getStatusObject();
|
||||
if ($status->hasStatusTag()) {
|
||||
$header->setStatus(
|
||||
$status->getStatusTagIcon(),
|
||||
$status->getStatusTagColor(),
|
||||
$status->getName());
|
||||
}
|
||||
|
||||
$issue = null;
|
||||
if ($device->isClusterDevice()) {
|
||||
$issue = $this->addClusterMessage(
|
||||
|
|
|
@ -76,6 +76,8 @@ final class AlmanacDeviceEditEngine
|
|||
}
|
||||
|
||||
protected function buildCustomEditFields($object) {
|
||||
$status_map = $this->getDeviceStatusMap($object);
|
||||
|
||||
return array(
|
||||
id(new PhabricatorTextEditField())
|
||||
->setKey('name')
|
||||
|
@ -84,7 +86,32 @@ final class AlmanacDeviceEditEngine
|
|||
->setTransactionType(AlmanacDeviceNameTransaction::TRANSACTIONTYPE)
|
||||
->setIsRequired(true)
|
||||
->setValue($object->getName()),
|
||||
id(new PhabricatorSelectEditField())
|
||||
->setKey('status')
|
||||
->setLabel(pht('Status'))
|
||||
->setDescription(pht('Device status.'))
|
||||
->setTransactionType(AlmanacDeviceStatusTransaction::TRANSACTIONTYPE)
|
||||
->setOptions($status_map)
|
||||
->setValue($object->getStatus()),
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
private function getDeviceStatusMap(AlmanacDevice $device) {
|
||||
$status_map = AlmanacDeviceStatus::getStatusMap();
|
||||
|
||||
// If the device currently has an unknown status, add it to the list for
|
||||
// the dropdown.
|
||||
$status_value = $device->getStatus();
|
||||
if (!isset($status_map[$status_value])) {
|
||||
$status_map = array(
|
||||
$status_value => AlmanacDeviceStatus::newStatusFromValue($status_value),
|
||||
) + $status_map;
|
||||
}
|
||||
|
||||
$status_map = mpull($status_map, 'getName');
|
||||
|
||||
return $status_map;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -34,7 +34,8 @@ final class AlmanacInterfacePHIDType extends PhabricatorPHIDType {
|
|||
|
||||
$id = $interface->getID();
|
||||
|
||||
$device_name = $interface->getDevice()->getName();
|
||||
$device = $interface->getDevice();
|
||||
$device_name = $device->getName();
|
||||
$address = $interface->getAddress();
|
||||
$port = $interface->getPort();
|
||||
$network = $interface->getNetwork()->getName();
|
||||
|
@ -48,6 +49,10 @@ final class AlmanacInterfacePHIDType extends PhabricatorPHIDType {
|
|||
|
||||
$handle->setObjectName(pht('Interface %d', $id));
|
||||
$handle->setName($name);
|
||||
|
||||
if ($device->isDisabled()) {
|
||||
$handle->setStatus(PhabricatorObjectHandle::STATUS_CLOSED);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ final class AlmanacDeviceQuery
|
|||
private $namePrefix;
|
||||
private $nameSuffix;
|
||||
private $isClusterDevice;
|
||||
private $statuses;
|
||||
|
||||
public function withIDs(array $ids) {
|
||||
$this->ids = $ids;
|
||||
|
@ -35,6 +36,11 @@ final class AlmanacDeviceQuery
|
|||
return $this;
|
||||
}
|
||||
|
||||
public function withStatuses(array $statuses) {
|
||||
$this->statuses = $statuses;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function withNameNgrams($ngrams) {
|
||||
return $this->withNgramsConstraint(
|
||||
new AlmanacDeviceNameNgrams(),
|
||||
|
@ -103,6 +109,13 @@ final class AlmanacDeviceQuery
|
|||
(int)$this->isClusterDevice);
|
||||
}
|
||||
|
||||
if ($this->statuses !== null) {
|
||||
$where[] = qsprintf(
|
||||
$conn,
|
||||
'device.status IN (%Ls)',
|
||||
$this->statuses);
|
||||
}
|
||||
|
||||
return $where;
|
||||
}
|
||||
|
||||
|
|
|
@ -16,6 +16,9 @@ final class AlmanacDeviceSearchEngine
|
|||
}
|
||||
|
||||
protected function buildCustomSearchFields() {
|
||||
$status_options = AlmanacDeviceStatus::getStatusMap();
|
||||
$status_options = mpull($status_options, 'getName');
|
||||
|
||||
return array(
|
||||
id(new PhabricatorSearchTextField())
|
||||
->setLabel(pht('Name Contains'))
|
||||
|
@ -25,6 +28,11 @@ final class AlmanacDeviceSearchEngine
|
|||
->setLabel(pht('Exact Names'))
|
||||
->setKey('names')
|
||||
->setDescription(pht('Search for devices with specific names.')),
|
||||
id(new PhabricatorSearchCheckboxesField())
|
||||
->setLabel(pht('Statuses'))
|
||||
->setKey('statuses')
|
||||
->setDescription(pht('Search for devices with given statuses.'))
|
||||
->setOptions($status_options),
|
||||
id(new PhabricatorSearchThreeStateField())
|
||||
->setLabel(pht('Cluster Device'))
|
||||
->setKey('isClusterDevice')
|
||||
|
@ -50,6 +58,10 @@ final class AlmanacDeviceSearchEngine
|
|||
$query->withIsClusterDevice($map['isClusterDevice']);
|
||||
}
|
||||
|
||||
if ($map['statuses']) {
|
||||
$query->withStatuses($map['statuses']);
|
||||
}
|
||||
|
||||
return $query;
|
||||
}
|
||||
|
||||
|
@ -99,6 +111,19 @@ final class AlmanacDeviceSearchEngine
|
|||
$item->addIcon('fa-sitemap', pht('Cluster Device'));
|
||||
}
|
||||
|
||||
if ($device->isDisabled()) {
|
||||
$item->setDisabled(true);
|
||||
}
|
||||
|
||||
$status = $device->getStatusObject();
|
||||
$icon_icon = $status->getIconIcon();
|
||||
$icon_color = $status->getIconColor();
|
||||
$icon_label = $status->getName();
|
||||
|
||||
$item->setStatusIcon(
|
||||
"{$icon_icon} {$icon_color}",
|
||||
$icon_label);
|
||||
|
||||
$list->addItem($item);
|
||||
}
|
||||
|
||||
|
|
|
@ -17,6 +17,7 @@ final class AlmanacDevice
|
|||
protected $nameIndex;
|
||||
protected $viewPolicy;
|
||||
protected $editPolicy;
|
||||
protected $status;
|
||||
protected $isBoundToClusterService;
|
||||
|
||||
private $almanacProperties = self::ATTACHABLE;
|
||||
|
@ -25,6 +26,7 @@ final class AlmanacDevice
|
|||
return id(new AlmanacDevice())
|
||||
->setViewPolicy(PhabricatorPolicies::POLICY_USER)
|
||||
->setEditPolicy(PhabricatorPolicies::POLICY_ADMIN)
|
||||
->setStatus(AlmanacDeviceStatus::ACTIVE)
|
||||
->attachAlmanacProperties(array())
|
||||
->setIsBoundToClusterService(0);
|
||||
}
|
||||
|
@ -35,6 +37,7 @@ final class AlmanacDevice
|
|||
self::CONFIG_COLUMN_SCHEMA => array(
|
||||
'name' => 'text128',
|
||||
'nameIndex' => 'bytes12',
|
||||
'status' => 'text32',
|
||||
'isBoundToClusterService' => 'bool',
|
||||
),
|
||||
self::CONFIG_KEY_SCHEMA => array(
|
||||
|
@ -100,6 +103,18 @@ final class AlmanacDevice
|
|||
return $this->getIsBoundToClusterService();
|
||||
}
|
||||
|
||||
public function getStatusObject() {
|
||||
return $this->newStatusObject();
|
||||
}
|
||||
|
||||
private function newStatusObject() {
|
||||
return AlmanacDeviceStatus::newStatusFromValue($this->getStatus());
|
||||
}
|
||||
|
||||
public function isDisabled() {
|
||||
return $this->getStatusObject()->isDisabled();
|
||||
}
|
||||
|
||||
|
||||
/* -( AlmanacPropertyInterface )------------------------------------------- */
|
||||
|
||||
|
@ -263,12 +278,22 @@ final class AlmanacDevice
|
|||
->setKey('name')
|
||||
->setType('string')
|
||||
->setDescription(pht('The name of the device.')),
|
||||
id(new PhabricatorConduitSearchFieldSpecification())
|
||||
->setKey('status')
|
||||
->setType('map<string, wild>')
|
||||
->setDescription(pht('Device status information.')),
|
||||
);
|
||||
}
|
||||
|
||||
public function getFieldValuesForConduit() {
|
||||
$status = $this->getStatusObject();
|
||||
|
||||
return array(
|
||||
'name' => $this->getName(),
|
||||
'status' => array(
|
||||
'value' => $status->getValue(),
|
||||
'name' => $status->getName(),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -46,9 +46,16 @@ final class AlmanacInterfaceDatasource
|
|||
|
||||
$results = array();
|
||||
foreach ($handles as $handle) {
|
||||
if ($handle->isClosed()) {
|
||||
$closed = pht('Disabled');
|
||||
} else {
|
||||
$closed = null;
|
||||
}
|
||||
|
||||
$results[] = id(new PhabricatorTypeaheadResult())
|
||||
->setName($handle->getName())
|
||||
->setPHID($handle->getPHID());
|
||||
->setPHID($handle->getPHID())
|
||||
->setClosed($closed);
|
||||
}
|
||||
|
||||
return $results;
|
||||
|
|
|
@ -0,0 +1,61 @@
|
|||
<?php
|
||||
|
||||
final class AlmanacDeviceStatusTransaction
|
||||
extends AlmanacDeviceTransactionType {
|
||||
|
||||
const TRANSACTIONTYPE = 'almanac:device:status';
|
||||
|
||||
public function generateOldValue($object) {
|
||||
return $object->getStatus();
|
||||
}
|
||||
|
||||
public function applyInternalEffects($object, $value) {
|
||||
$object->setStatus($value);
|
||||
}
|
||||
|
||||
public function getTitle() {
|
||||
$old_value = $this->getOldValue();
|
||||
$new_value = $this->getNewValue();
|
||||
|
||||
$old_status = AlmanacDeviceStatus::newStatusFromValue($old_value);
|
||||
$new_status = AlmanacDeviceStatus::newStatusFromValue($new_value);
|
||||
|
||||
$old_name = $old_status->getName();
|
||||
$new_name = $new_status->getName();
|
||||
|
||||
return pht(
|
||||
'%s changed the status of this device from %s to %s.',
|
||||
$this->renderAuthor(),
|
||||
$this->renderValue($old_name),
|
||||
$this->renderValue($new_name));
|
||||
}
|
||||
|
||||
public function validateTransactions($object, array $xactions) {
|
||||
$errors = array();
|
||||
|
||||
$status_map = AlmanacDeviceStatus::getStatusMap();
|
||||
|
||||
$old_value = $this->generateOldValue($object);
|
||||
foreach ($xactions as $xaction) {
|
||||
$new_value = $xaction->getNewValue();
|
||||
|
||||
if ($new_value === $old_value) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (!isset($status_map[$new_value])) {
|
||||
$errors[] = $this->newInvalidError(
|
||||
pht(
|
||||
'Almanac device status "%s" is unrecognized. Valid status '.
|
||||
'values are: %s.',
|
||||
$new_value,
|
||||
implode(', ', array_keys($status_map))),
|
||||
$xaction);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
return $errors;
|
||||
}
|
||||
|
||||
}
|
|
@ -197,6 +197,10 @@ final class PhabricatorObjectHandle
|
|||
return $this->status;
|
||||
}
|
||||
|
||||
public function isClosed() {
|
||||
return ($this->status === self::STATUS_CLOSED);
|
||||
}
|
||||
|
||||
public function setFullName($full_name) {
|
||||
$this->fullName = $full_name;
|
||||
return $this;
|
||||
|
|
Loading…
Reference in a new issue