mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-10 00:42:41 +01:00
Insert Maniphest transactions when edges are edited
Summary: - See D2741. - When EdgeEditor performs edits, emit events. - Listen for Maniphest edge events and save the changes as transactions. - Do all this in a reasonably generic way that won't take too much rewriting as we use edges more generally. Test Plan: Attached and detached commits from tasks, saw reasonable-looking transactions spring into existence. Reviewers: btrahan, davidreuss Reviewed By: btrahan CC: aran Differential Revision: https://secure.phabricator.com/D2906
This commit is contained in:
parent
310cf00fc3
commit
9f4cfd40bc
9 changed files with 198 additions and 3 deletions
|
@ -500,6 +500,7 @@ phutil_register_library_map(array(
|
|||
'ManiphestController' => 'applications/maniphest/controller/ManiphestController.php',
|
||||
'ManiphestDAO' => 'applications/maniphest/storage/ManiphestDAO.php',
|
||||
'ManiphestDefaultTaskExtensions' => 'applications/maniphest/extensions/ManiphestDefaultTaskExtensions.php',
|
||||
'ManiphestEdgeEventListener' => 'applications/maniphest/event/ManiphestEdgeEventListener.php',
|
||||
'ManiphestExportController' => 'applications/maniphest/controller/ManiphestExportController.php',
|
||||
'ManiphestReplyHandler' => 'applications/maniphest/ManiphestReplyHandler.php',
|
||||
'ManiphestReportController' => 'applications/maniphest/controller/ManiphestReportController.php',
|
||||
|
@ -1527,6 +1528,7 @@ phutil_register_library_map(array(
|
|||
'ManiphestController' => 'PhabricatorController',
|
||||
'ManiphestDAO' => 'PhabricatorLiskDAO',
|
||||
'ManiphestDefaultTaskExtensions' => 'ManiphestTaskExtensions',
|
||||
'ManiphestEdgeEventListener' => 'PhutilEventListener',
|
||||
'ManiphestExportController' => 'ManiphestController',
|
||||
'ManiphestReplyHandler' => 'PhabricatorMailReplyHandler',
|
||||
'ManiphestReportController' => 'ManiphestController',
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2011 Facebook, Inc.
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
|
@ -29,6 +29,7 @@ final class ManiphestTransactionType extends ManiphestConstants {
|
|||
const TYPE_PRIORITY = 'priority';
|
||||
|
||||
const TYPE_ATTACH = 'attach';
|
||||
const TYPE_EDGE = 'edge';
|
||||
|
||||
const TYPE_TITLE = 'title';
|
||||
const TYPE_DESCRIPTION = 'description';
|
||||
|
|
|
@ -69,6 +69,9 @@ final class ManiphestTransactionEditor {
|
|||
case ManiphestTransactionType::TYPE_PRIORITY:
|
||||
$old = $task->getPriority();
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_EDGE:
|
||||
$old = $transaction->getOldValue();
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_ATTACH:
|
||||
$old = $task->getAttached();
|
||||
break;
|
||||
|
@ -173,6 +176,10 @@ final class ManiphestTransactionEditor {
|
|||
$aux_key = $transaction->getMetadataValue('aux:key');
|
||||
$task->setAuxiliaryAttribute($aux_key, $new);
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_EDGE:
|
||||
// Edge edits are accomplished through PhabricatorEdgeEditor, which
|
||||
// has authority.
|
||||
break;
|
||||
default:
|
||||
throw new Exception('Unknown action type.');
|
||||
}
|
||||
|
|
145
src/applications/maniphest/event/ManiphestEdgeEventListener.php
Normal file
145
src/applications/maniphest/event/ManiphestEdgeEventListener.php
Normal file
|
@ -0,0 +1,145 @@
|
|||
<?php
|
||||
|
||||
/*
|
||||
* Copyright 2012 Facebook, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Listener for Maniphest Task edge events. When some workflow causes task
|
||||
* edges to be added or removed, we consider the edge edit authoritative but
|
||||
* duplicate the information into a ManiphestTansaction for display.
|
||||
*
|
||||
* @group maniphest
|
||||
*/
|
||||
final class ManiphestEdgeEventListener extends PhutilEventListener {
|
||||
|
||||
private $edges = array();
|
||||
private $tasks = array();
|
||||
|
||||
public function register() {
|
||||
$this->listen(PhabricatorEventType::TYPE_EDGE_WILLEDITEDGES);
|
||||
$this->listen(PhabricatorEventType::TYPE_EDGE_DIDEDITEDGES);
|
||||
}
|
||||
|
||||
public function handleEvent(PhutilEvent $event) {
|
||||
switch ($event->getType()) {
|
||||
case PhabricatorEventType::TYPE_EDGE_WILLEDITEDGES:
|
||||
return $this->handleWillEditEvent($event);
|
||||
case PhabricatorEventType::TYPE_EDGE_DIDEDITEDGES:
|
||||
return $this->handleDidEditEvent($event);
|
||||
}
|
||||
}
|
||||
|
||||
private function handleWillEditEvent(PhutilEvent $event) {
|
||||
// NOTE: Everything is namespaced by `id` so that we aren't left in an
|
||||
// inconsistent state if an edit fails to complete (e.g., something throws)
|
||||
// or an edit happens inside another edit.
|
||||
|
||||
$id = $event->getValue('id');
|
||||
|
||||
$edges = $this->loadAllEdges($event);
|
||||
$tasks = array();
|
||||
if ($edges) {
|
||||
$tasks = id(new ManiphestTask())->loadAllWhere(
|
||||
'phid IN (%Ls)',
|
||||
array_keys($edges));
|
||||
$tasks = mpull($tasks, null, 'getPHID');
|
||||
}
|
||||
|
||||
$this->edges[$id] = $edges;
|
||||
$this->tasks[$id] = $tasks;
|
||||
}
|
||||
|
||||
private function handleDidEditEvent(PhutilEvent $event) {
|
||||
$id = $event->getValue('id');
|
||||
|
||||
$old_edges = $this->edges[$id];
|
||||
$tasks = $this->tasks[$id];
|
||||
|
||||
unset($this->edges[$id]);
|
||||
unset($this->tasks[$id]);
|
||||
|
||||
$new_edges = $this->loadAllEdges($event);
|
||||
$editor = new ManiphestTransactionEditor();
|
||||
|
||||
foreach ($tasks as $phid => $task) {
|
||||
$xactions = array();
|
||||
|
||||
$old = $old_edges[$phid];
|
||||
$new = $new_edges[$phid];
|
||||
|
||||
$types = array_keys($old + $new);
|
||||
foreach ($types as $type) {
|
||||
$old_type = idx($old, $type, array());
|
||||
$new_type = idx($new, $type, array());
|
||||
|
||||
if ($old_type === $new_type) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$xactions[] = id(new ManiphestTransaction())
|
||||
->setTransactionType(ManiphestTransactionType::TYPE_EDGE)
|
||||
->setOldValue($old_type)
|
||||
->setNewValue($new_type)
|
||||
->setMetadataValue('edge:type', $type)
|
||||
->setAuthorPHID($event->getUser()->getPHID());
|
||||
}
|
||||
|
||||
if ($xactions) {
|
||||
$editor->applyTransactions($task, $xactions);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function filterEdgesBySourceType(array $edges, $type) {
|
||||
foreach ($edges as $key => $edge) {
|
||||
if ($edge['src_type'] !== $type) {
|
||||
unset($edges[$key]);
|
||||
}
|
||||
}
|
||||
return $edges;
|
||||
}
|
||||
|
||||
private function loadAllEdges(PhutilEvent $event) {
|
||||
$add_edges = $event->getValue('add');
|
||||
$rem_edges = $event->getValue('rem');
|
||||
|
||||
$type_task = PhabricatorPHIDConstants::PHID_TYPE_TASK;
|
||||
|
||||
$all_edges = array_merge($add_edges, $rem_edges);
|
||||
$all_edges = $this->filterEdgesBySourceType($all_edges, $type_task);
|
||||
|
||||
if (!$all_edges) {
|
||||
return;
|
||||
}
|
||||
|
||||
$all_tasks = array();
|
||||
$all_types = array();
|
||||
foreach ($all_edges as $edge) {
|
||||
$all_tasks[$edge['src']] = true;
|
||||
$all_types[$edge['type']] = true;
|
||||
}
|
||||
|
||||
$all_tasks = array_keys($all_tasks);
|
||||
$all_types = array_keys($all_types);
|
||||
|
||||
return id(new PhabricatorEdgeQuery())
|
||||
->withSourcePHIDs($all_tasks)
|
||||
->withEdgeTypes($all_types)
|
||||
->needEdgeData(true)
|
||||
->execute();
|
||||
}
|
||||
|
||||
}
|
|
@ -58,6 +58,8 @@ final class ManiphestTransaction extends ManiphestDAO {
|
|||
$phids[] = $this->getOldValue();
|
||||
$phids[] = $this->getNewValue();
|
||||
break;
|
||||
case ManiphestTransactionType::TYPE_EDGE:
|
||||
return array_keys($this->getOldValue() + $this->getNewValue());
|
||||
case ManiphestTransactionType::TYPE_ATTACH:
|
||||
$old = $this->getOldValue();
|
||||
$new = $this->getNewValue();
|
||||
|
|
|
@ -74,6 +74,7 @@ final class PhabricatorSearchAttachController
|
|||
$add_phids = $phids;
|
||||
$rem_phids = array_diff($old_phids, $add_phids);
|
||||
$editor = id(new PhabricatorEdgeEditor());
|
||||
$editor->setUser($user);
|
||||
foreach ($add_phids as $phid) {
|
||||
$editor->addEdge($this->phid, $edge_type, $phid);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,7 @@
|
|||
*
|
||||
* id(new PhabricatorEdgeEditor())
|
||||
* ->addEdge($src, $type, $dst)
|
||||
* ->setUser($user)
|
||||
* ->save();
|
||||
*
|
||||
* @task edit Editing Edges
|
||||
|
@ -38,6 +39,12 @@ final class PhabricatorEdgeEditor {
|
|||
private $addEdges = array();
|
||||
private $remEdges = array();
|
||||
private $openTransactions = array();
|
||||
private $user;
|
||||
|
||||
public function setUser(PhabricatorUser $user) {
|
||||
$this->user = $user;
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/* -( Editing Edges )------------------------------------------------------ */
|
||||
|
@ -111,12 +118,20 @@ final class PhabricatorEdgeEditor {
|
|||
|
||||
$this->writeEdgeData();
|
||||
|
||||
static $id = 0;
|
||||
$id++;
|
||||
|
||||
$this->sendEvent($id, PhabricatorEventType::TYPE_EDGE_WILLEDITEDGES);
|
||||
|
||||
// NOTE: Removes first, then adds, so that "remove + add" is a useful
|
||||
// operation meaning "overwrite".
|
||||
|
||||
$this->executeRemoves();
|
||||
$this->executeAdds();
|
||||
|
||||
$this->sendEvent($id, PhabricatorEventType::TYPE_EDGE_DIDEDITEDGES);
|
||||
|
||||
|
||||
$this->saveTransactions();
|
||||
}
|
||||
|
||||
|
@ -136,11 +151,15 @@ final class PhabricatorEdgeEditor {
|
|||
$data['data'] = $options['data'];
|
||||
}
|
||||
|
||||
$src_type = phid_get_type($src);
|
||||
$dst_type = phid_get_type($dst);
|
||||
|
||||
$specs = array();
|
||||
$specs[] = array(
|
||||
'src' => $src,
|
||||
'src_type' => phid_get_type($src),
|
||||
'src_type' => $src_type,
|
||||
'dst' => $dst,
|
||||
'dst_type' => $dst_type,
|
||||
'type' => $type,
|
||||
'data' => $data,
|
||||
);
|
||||
|
@ -156,8 +175,9 @@ final class PhabricatorEdgeEditor {
|
|||
|
||||
$specs[] = array(
|
||||
'src' => $dst,
|
||||
'src_type' => phid_get_type($dst),
|
||||
'src_type' => $dst_type,
|
||||
'dst' => $src,
|
||||
'dst_type' => $src_type,
|
||||
'type' => $inverse,
|
||||
'data' => $data,
|
||||
);
|
||||
|
@ -307,4 +327,16 @@ final class PhabricatorEdgeEditor {
|
|||
}
|
||||
}
|
||||
|
||||
private function sendEvent($edit_id, $event_type) {
|
||||
$event = new PhabricatorEvent(
|
||||
$event_type,
|
||||
array(
|
||||
'id' => $edit_id,
|
||||
'add' => $this->addEdges,
|
||||
'rem' => $this->remEdges,
|
||||
));
|
||||
$event->setUser($this->user);
|
||||
PhutilEventEngine::dispatchEvent($event);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -26,6 +26,8 @@ final class PhabricatorEventEngine {
|
|||
|
||||
// Register the DarkConosole event logger.
|
||||
id(new DarkConsoleEventPluginAPI())->register();
|
||||
id(new ManiphestEdgeEventListener())->register();
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -23,4 +23,7 @@ final class PhabricatorEventType extends PhutilEventType {
|
|||
const TYPE_DIFFERENTIAL_WILLSENDMAIL = 'differential.willSendMail';
|
||||
const TYPE_DIFFERENTIAL_WILLMARKGENERATED = 'differential.willMarkGenerated';
|
||||
|
||||
const TYPE_EDGE_WILLEDITEDGES = 'edge.willEditEdges';
|
||||
const TYPE_EDGE_DIDEDITEDGES = 'edge.didEditEdges';
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue