1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-18 11:30:55 +01:00

Add very basic routing to Nuance

Summary:
Ref T8783. Sort out some relationships and fields:

  - Make Items 1:1 with Queues: each item is always in exactly one queue. Minor discussion on T8783. I think this is easier to understand and reason about (and implement!) and can't come up with any real cases where it isn't powerful enough.
  - Remove "QueueItem", which allowed items to be in multiple queues at once.
  - Remove "dateNuanced", which is equivalent to "dateCreated" in all cases.

Then add really basic routing:

  - Add "Default Queue" for Sources. New items from the source route into that queue.
  - (Some day there will be routing rules, but for now the rule is "always route into the default queue".)
  - Show queue on items.
  - Show more / more useful edit history and transactions in several UIs.

Test Plan:
{F749445}

{F749446}

{F749447}

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T8783

Differential Revision: https://secure.phabricator.com/D13988
This commit is contained in:
epriestley 2015-08-24 09:21:56 -07:00
parent e55a197dd6
commit c612579854
17 changed files with 288 additions and 72 deletions

View file

@ -0,0 +1 @@
DROP TABLE {$NAMESPACE}_nuance.nuance_queueitem;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_item
ADD queuePHID VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_source
ADD defaultQueuePHID VARBINARY(64) NOT NULL;

View file

@ -0,0 +1,2 @@
ALTER TABLE {$NAMESPACE}_nuance.nuance_item
DROP dateNuanced;

View file

@ -1288,9 +1288,9 @@ phutil_register_library_map(array(
'NuancePhabricatorFormSourceDefinition' => 'applications/nuance/source/NuancePhabricatorFormSourceDefinition.php',
'NuanceQuery' => 'applications/nuance/query/NuanceQuery.php',
'NuanceQueue' => 'applications/nuance/storage/NuanceQueue.php',
'NuanceQueueDatasource' => 'applications/nuance/typeahead/NuanceQueueDatasource.php',
'NuanceQueueEditController' => 'applications/nuance/controller/NuanceQueueEditController.php',
'NuanceQueueEditor' => 'applications/nuance/editor/NuanceQueueEditor.php',
'NuanceQueueItem' => 'applications/nuance/storage/NuanceQueueItem.php',
'NuanceQueueListController' => 'applications/nuance/controller/NuanceQueueListController.php',
'NuanceQueuePHIDType' => 'applications/nuance/phid/NuanceQueuePHIDType.php',
'NuanceQueueQuery' => 'applications/nuance/query/NuanceQueueQuery.php',
@ -5080,9 +5080,9 @@ phutil_register_library_map(array(
'PhabricatorPolicyInterface',
'PhabricatorApplicationTransactionInterface',
),
'NuanceQueueDatasource' => 'PhabricatorTypeaheadDatasource',
'NuanceQueueEditController' => 'NuanceController',
'NuanceQueueEditor' => 'PhabricatorApplicationTransactionEditor',
'NuanceQueueItem' => 'NuanceDAO',
'NuanceQueueListController' => 'NuanceController',
'NuanceQueuePHIDType' => 'PhabricatorPHIDType',
'NuanceQueueQuery' => 'NuanceQuery',

View file

@ -33,10 +33,17 @@ final class NuanceItemEditController extends NuanceController {
->setHeaderText($title)
->addPropertyList($properties);
$timeline = $this->buildTransactionTimeline(
$item,
new NuanceItemTransactionQuery());
$timeline->setShouldTerminate(true);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$timeline,
),
array(
'title' => $title,
@ -62,6 +69,10 @@ final class NuanceItemEditController extends NuanceController {
pht('Source'),
$viewer->renderHandle($item->getSourcePHID()));
$properties->addProperty(
pht('Queue'),
$viewer->renderHandle($item->getQueuePHID()));
$source = $item->getSource();
$definition = $source->requireDefinition();

View file

@ -13,7 +13,7 @@ final class NuanceSourceViewController extends NuanceController {
return new Aphront404Response();
}
$source_phid = $source->getPHID();
$source_id = $source->getID();
$timeline = $this->buildTransactionTimeline(
$source,
@ -34,10 +34,29 @@ final class NuanceSourceViewController extends NuanceController {
$crumbs->addTextCrumb($title);
$can_edit = PhabricatorPolicyFilter::hasCapability(
$viewer,
$source,
PhabricatorPolicyCapability::CAN_EDIT);
$routing_list = id(new PHUIPropertyListView())
->addProperty(
pht('Default Queue'),
$viewer->renderHandle($source->getDefaultQueuePHID()));
$routing_header = id(new PHUIHeaderView())
->setHeader(pht('Routing Rules'));
$routing = id(new PHUIObjectBoxView())
->setHeader($routing_header)
->addPropertyList($routing_list);
return $this->buildApplicationPage(
array(
$crumbs,
$box,
$routing,
$timeline,
),
array(

View file

@ -18,6 +18,7 @@ final class NuanceItemEditor
$types[] = NuanceItemTransaction::TYPE_SOURCE;
$types[] = NuanceItemTransaction::TYPE_REQUESTOR;
$types[] = NuanceItemTransaction::TYPE_PROPERTY;
$types[] = NuanceItemTransaction::TYPE_QUEUE;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
@ -38,6 +39,8 @@ final class NuanceItemEditor
return $object->getSourcePHID();
case NuanceItemTransaction::TYPE_OWNER:
return $object->getOwnerPHID();
case NuanceItemTransaction::TYPE_QUEUE:
return $object->getQueuePHID();
case NuanceItemTransaction::TYPE_PROPERTY:
$key = $xaction->getMetadataValue(
NuanceItemTransaction::PROPERTY_KEY);
@ -56,6 +59,7 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
case NuanceItemTransaction::TYPE_QUEUE:
return $xaction->getNewValue();
}
@ -76,6 +80,9 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_OWNER:
$object->setOwnerPHID($xaction->getNewValue());
break;
case NuanceItemTransaction::TYPE_QUEUE:
$object->setQueuePHID($xaction->getNewValue());
break;
case NuanceItemTransaction::TYPE_PROPERTY:
$key = $xaction->getMetadataValue(
NuanceItemTransaction::PROPERTY_KEY);
@ -93,6 +100,7 @@ final class NuanceItemEditor
case NuanceItemTransaction::TYPE_SOURCE:
case NuanceItemTransaction::TYPE_OWNER:
case NuanceItemTransaction::TYPE_PROPERTY:
case NuanceItemTransaction::TYPE_QUEUE:
return;
}

View file

@ -15,6 +15,7 @@ final class NuanceSourceEditor
$types = parent::getTransactionTypes();
$types[] = NuanceSourceTransaction::TYPE_NAME;
$types[] = NuanceSourceTransaction::TYPE_DEFAULT_QUEUE;
$types[] = PhabricatorTransactions::TYPE_EDGE;
$types[] = PhabricatorTransactions::TYPE_COMMENT;
@ -31,6 +32,8 @@ final class NuanceSourceEditor
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
return $object->getName();
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return $object->getDefaultQueuePHID();
}
return parent::getCustomTransactionOldValue($object, $xaction);
@ -42,6 +45,7 @@ final class NuanceSourceEditor
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return $xaction->getNewValue();
}
@ -56,6 +60,9 @@ final class NuanceSourceEditor
case NuanceSourceTransaction::TYPE_NAME:
$object->setName($xaction->getNewValue());
break;
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
$object->setDefaultQueuePHID($xaction->getNewValue());
break;
}
}
@ -65,6 +72,7 @@ final class NuanceSourceEditor
switch ($xaction->getTransactionType()) {
case NuanceSourceTransaction::TYPE_NAME:
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
return;
}
@ -95,6 +103,19 @@ final class NuanceSourceEditor
$errors[] = $error;
}
break;
case NuanceSourceTransaction::TYPE_DEFAULT_QUEUE:
foreach ($xactions as $xaction) {
if (!$xaction->getNewValue()) {
$error = new PhabricatorApplicationTransactionValidationError(
$type,
pht('Required'),
pht('Sources must have a default queue.'),
$xaction);
$error->setIsMissingFieldError(true);
$errors[] = $error;
}
}
break;
}
return $errors;

View file

@ -169,25 +169,39 @@ abstract class NuanceSourceDefinition extends Phobject {
$form = $this->augmentEditForm($form, $ex);
$default_phid = $source->getDefaultQueuePHID();
if ($default_phid) {
$default_queues = array($default_phid);
} else {
$default_queues = array();
}
$form
->appendControl(
id(new AphrontFormTokenizerControl())
->setLabel(pht('Default Queue'))
->setName('defaultQueuePHIDs')
->setLimit(1)
->setDatasource(new NuanceQueueDatasource())
->setValue($default_queues))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($source)
->setPolicies($policies)
->setName('viewPolicy'))
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_VIEW)
->setPolicyObject($source)
->setPolicies($policies)
->setName('viewPolicy'))
->appendChild(
id(new AphrontFormPolicyControl())
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($source)
->setPolicies($policies)
->setName('editPolicy'))
->setUser($user)
->setCapability(PhabricatorPolicyCapability::CAN_EDIT)
->setPolicyObject($source)
->setPolicies($policies)
->setName('editPolicy'))
->appendChild(
id(new AphrontFormSubmitControl())
->addCancelButton($source->getURI())
->setValue(pht('Save')));
->addCancelButton($source->getURI())
->setValue(pht('Save')));
return $form;
}
@ -213,13 +227,19 @@ abstract class NuanceSourceDefinition extends Phobject {
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_EDIT_POLICY)
->setNewValue($request->getStr('editPolicy'));
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(PhabricatorTransactions::TYPE_VIEW_POLICY)
->setNewValue($request->getStr('viewPolicy'));
$transactions[] = id(new NuanceSourceTransaction())
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(NuanceSourceTransaction::TYPE_NAME)
->setNewvalue($request->getStr('name'));
$transactions[] = id(new NuanceSourceTransaction())
->setTransactionType(NuanceSourceTransaction::TYPE_DEFAULT_QUEUE)
->setNewvalue(head($request->getArr('defaultQueuePHIDs')));
return $transactions;
}
@ -251,6 +271,12 @@ abstract class NuanceSourceDefinition extends Phobject {
->setTransactionType(NuanceItemTransaction::TYPE_REQUESTOR)
->setNewValue($requestor->getPHID());
// TODO: Eventually, apply real routing rules. For now, just put everything
// in the default queue for the source.
$xactions[] = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemTransaction::TYPE_QUEUE)
->setNewValue($source->getDefaultQueuePHID());
foreach ($properties as $key => $property) {
$xactions[] = id(new NuanceItemTransaction())
->setTransactionType(NuanceItemTransaction::TYPE_PROPERTY)

View file

@ -17,13 +17,12 @@ final class NuanceItem
protected $sourceLabel;
protected $data = array();
protected $mailKey;
protected $dateNuanced;
protected $queuePHID;
private $source = self::ATTACHABLE;
public static function initializeNewItem() {
return id(new NuanceItem())
->setDateNuanced(time())
->setStatus(self::STATUS_OPEN);
}
@ -38,17 +37,19 @@ final class NuanceItem
'sourceLabel' => 'text255?',
'status' => 'uint32',
'mailKey' => 'bytes20',
'dateNuanced' => 'epoch',
),
self::CONFIG_KEY_SCHEMA => array(
'key_source' => array(
'columns' => array('sourcePHID', 'status', 'dateNuanced', 'id'),
'columns' => array('sourcePHID', 'status'),
),
'key_owner' => array(
'columns' => array('ownerPHID', 'status', 'dateNuanced', 'id'),
'columns' => array('ownerPHID', 'status'),
),
'key_contacter' => array(
'columns' => array('requestorPHID', 'status', 'dateNuanced', 'id'),
'key_requestor' => array(
'columns' => array('requestorPHID', 'status'),
),
'key_queue' => array(
'columns' => array('queuePHID', 'status'),
),
),
) + parent::getConfiguration();
@ -144,7 +145,6 @@ final class NuanceItem
'sourceLabel' => $this->getSourceLabel(),
'dateCreated' => $this->getDateCreated(),
'dateModified' => $this->getDateModified(),
'dateNuanced' => $this->getDateNuanced(),
);
}

View file

@ -9,6 +9,7 @@ final class NuanceItemTransaction
const TYPE_REQUESTOR = 'nuance.item.requestor';
const TYPE_SOURCE = 'nuance.item.source';
const TYPE_PROPERTY = 'nuance.item.property';
const TYPE_QUEUE = 'nuance.item.queue';
public function getApplicationTransactionType() {
return NuanceItemPHIDType::TYPECONST;
@ -18,4 +19,55 @@ final class NuanceItemTransaction
return new NuanceItemTransactionComment();
}
public function shouldHide() {
$old = $this->getOldValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_REQUESTOR:
case self::TYPE_SOURCE:
return ($old === null);
}
return parent::shouldHide();
}
public function getRequiredHandlePHIDs() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$phids = parent::getRequiredHandlePHIDs();
switch ($type) {
case self::TYPE_QUEUE:
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
break;
}
return $phids;
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$author_phid = $this->getAuthorPHID();
switch ($type) {
case self::TYPE_QUEUE:
return pht(
'%s routed this item to the %s queue.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($new));
}
return parent::getTitle();
}
}

View file

@ -1,34 +0,0 @@
<?php
final class NuanceQueueItem
extends NuanceDAO {
protected $queuePHID;
protected $itemPHID;
protected $itemStatus;
protected $itemDateNuanced;
protected function getConfiguration() {
return array(
self::CONFIG_COLUMN_SCHEMA => array(
'itemStatus' => 'uint32',
'itemDateNuanced' => 'epoch',
),
self::CONFIG_KEY_SCHEMA => array(
'key_one_per_queue' => array(
'columns' => array('itemPHID', 'queuePHID'),
'unique' => true,
),
'key_queue' => array(
'columns' => array(
'queuePHID',
'itemStatus',
'itemDateNuanced',
'id',
),
),
),
) + parent::getConfiguration();
}
}

View file

@ -12,4 +12,34 @@ final class NuanceQueueTransaction extends NuanceTransaction {
return new NuanceQueueTransactionComment();
}
public function shouldHide() {
$old = $this->getOldValue();
$type = $this->getTransactionType();
switch ($type) {
case self::TYPE_NAME:
return ($old === null);
}
return parent::shouldHide();
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$author_phid = $this->getAuthorPHID();
switch ($type) {
case self::TYPE_NAME:
return pht(
'%s renamed this queue from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
return parent::getTitle();
}
}

View file

@ -11,6 +11,7 @@ final class NuanceSource extends NuanceDAO
protected $mailKey;
protected $viewPolicy;
protected $editPolicy;
protected $defaultQueuePHID;
private $definition;

View file

@ -3,7 +3,8 @@
final class NuanceSourceTransaction
extends NuanceTransaction {
const TYPE_NAME = 'name-source';
const TYPE_NAME = 'source.name';
const TYPE_DEFAULT_QUEUE = 'source.queue.default';
public function getApplicationTransactionType() {
return NuanceSourcePHIDType::TYPECONST;
@ -13,27 +14,63 @@ final class NuanceSourceTransaction
return new NuanceSourceTransactionComment();
}
public function getTitle() {
public function shouldHide() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$author_phid = $this->getAuthorPHID();
$type = $this->getTransactionType();
switch ($this->getTransactionType()) {
switch ($type) {
case self::TYPE_DEFAULT_QUEUE:
return !$old;
case self::TYPE_NAME:
if ($old === null) {
return pht(
'%s created this source.',
$this->renderHandleLink($author_phid));
} else {
return pht(
'%s renamed this source from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
return ($old === null);
}
return parent::shouldHide();
}
public function getRequiredHandlePHIDs() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$phids = parent::getRequiredHandlePHIDs();
switch ($type) {
case self::TYPE_DEFAULT_QUEUE:
if ($old) {
$phids[] = $old;
}
if ($new) {
$phids[] = $new;
}
break;
}
return $phids;
}
public function getTitle() {
$old = $this->getOldValue();
$new = $this->getNewValue();
$type = $this->getTransactionType();
$author_phid = $this->getAuthorPHID();
switch ($type) {
case self::TYPE_DEFAULT_QUEUE:
return pht(
'%s changed the default queue from %s to %s.',
$this->renderHandleLink($author_phid),
$this->renderHandleLink($old),
$this->renderHandleLink($new));
case self::TYPE_NAME:
return pht(
'%s renamed this source from "%s" to "%s".',
$this->renderHandleLink($author_phid),
$old,
$new);
}
return parent::getTitle();
}
}

View file

@ -0,0 +1,38 @@
<?php
final class NuanceQueueDatasource
extends PhabricatorTypeaheadDatasource {
public function getBrowseTitle() {
return pht('Browse Queues');
}
public function getPlaceholderText() {
return pht('Type a queue name...');
}
public function getDatasourceApplicationClass() {
return 'PhabricatorNuanceApplication';
}
public function loadResults() {
$viewer = $this->getViewer();
$raw_query = $this->getRawQuery();
$results = array();
// TODO: Make this use real typeahead logic.
$query = new NuanceQueueQuery();
$queues = $this->executeQuery($query);
foreach ($queues as $queue) {
$results[] = id(new PhabricatorTypeaheadResult())
->setName($queue->getName())
->setURI('/nuance/queue/'.$queue->getID().'/')
->setPHID($queue->getPHID());
}
return $this->filterResultsAgainstTokens($results);
}
}