mirror of
https://we.phorge.it/source/phorge.git
synced 2024-12-29 00:40:57 +01:00
Support edge transactions in ApplicationTransactions
Summary: Fixes T2655. Adds generic support for edge edits (e.g., membership or attached objects). Test Plan: See next diff. Reviewers: chad, btrahan Reviewed By: btrahan CC: aran Maniphest Tasks: T2655 Differential Revision: https://secure.phabricator.com/D5434
This commit is contained in:
parent
1ee7bbe305
commit
696498934c
6 changed files with 234 additions and 4 deletions
24
resources/sql/patches/20130310.xactionmeta.sql
Normal file
24
resources/sql/patches/20130310.xactionmeta.sql
Normal file
|
@ -0,0 +1,24 @@
|
|||
ALTER TABLE `{$NAMESPACE}_file`.`macro_transaction`
|
||||
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||
UPDATE `{$NAMESPACE}_file`.macro_transaction SET metadata = '{}'
|
||||
WHERE metadata = '';
|
||||
|
||||
ALTER TABLE `{$NAMESPACE}_pholio`.`pholio_transaction`
|
||||
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||
UPDATE `{$NAMESPACE}_pholio`.pholio_transaction SET metadata = '{}'
|
||||
WHERE metadata = '';
|
||||
|
||||
ALTER TABLE `{$NAMESPACE}_config`.`config_transaction`
|
||||
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||
UPDATE `{$NAMESPACE}_config`.config_transaction SET metadata = '{}'
|
||||
WHERE metadata = '';
|
||||
|
||||
ALTER TABLE `{$NAMESPACE}_conpherence`.`conpherence_transaction`
|
||||
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||
UPDATE `{$NAMESPACE}_conpherence`.conpherence_transaction SET metadata = '{}'
|
||||
WHERE metadata = '';
|
||||
|
||||
ALTER TABLE `{$NAMESPACE}_phlux`.`phlux_transaction`
|
||||
ADD metadata LONGTEXT NOT NULL COLLATE utf8_bin;
|
||||
UPDATE `{$NAMESPACE}_phlux`.phlux_transaction SET metadata = '{}'
|
||||
WHERE metadata = '';
|
|
@ -192,7 +192,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
|
|||
return $v;
|
||||
case ConpherenceTransactionType::TYPE_FILES:
|
||||
case ConpherenceTransactionType::TYPE_PARTICIPANTS:
|
||||
return $this->mergePHIDTransactions($u, $v);
|
||||
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
||||
}
|
||||
|
||||
return parent::mergeTransactions($u, $v);
|
||||
|
|
|
@ -6,6 +6,7 @@ final class PhabricatorTransactions {
|
|||
const TYPE_SUBSCRIBERS = 'core:subscribers';
|
||||
const TYPE_VIEW_POLICY = 'core:view-policy';
|
||||
const TYPE_EDIT_POLICY = 'core:edit-policy';
|
||||
const TYPE_EDGE = 'core:edge';
|
||||
|
||||
const COLOR_RED = 'red';
|
||||
const COLOR_ORANGE = 'orange';
|
||||
|
|
|
@ -101,6 +101,26 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
return $object->getViewPolicy();
|
||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
return $object->getEditPolicy();
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$edge_type = $xaction->getMetadataValue('edge:type');
|
||||
if (!$edge_type) {
|
||||
throw new Exception("Edge transaction has no 'edge:type'!");
|
||||
}
|
||||
|
||||
$old_edges = array();
|
||||
if ($object->getPHID()) {
|
||||
$edge_src = $object->getPHID();
|
||||
|
||||
$old_edges = id(new PhabricatorEdgeQuery())
|
||||
->setViewer($this->getActor())
|
||||
->withSourcePHIDs(array($edge_src))
|
||||
->withEdgeTypes(array($edge_type))
|
||||
->needEdgeData(true)
|
||||
->execute();
|
||||
|
||||
$old_edges = $old_edges[$edge_src][$edge_type];
|
||||
}
|
||||
return $old_edges;
|
||||
default:
|
||||
return $this->getCustomTransactionOldValue($object, $xaction);
|
||||
}
|
||||
|
@ -115,6 +135,8 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
return $xaction->getNewValue();
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
return $this->getEdgeTransactionNewValue($xaction);
|
||||
default:
|
||||
return $this->getCustomTransactionNewValue($object, $xaction);
|
||||
}
|
||||
|
@ -180,7 +202,46 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
|
||||
$subeditor->save();
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$old = $xaction->getOldValue();
|
||||
$new = $xaction->getNewValue();
|
||||
$src = $object->getPHID();
|
||||
$type = $xaction->getMetadataValue('edge:type');
|
||||
|
||||
foreach ($new as $dst_phid => $edge) {
|
||||
$new[$dst_phid]['src'] = $src;
|
||||
}
|
||||
|
||||
$editor = id(new PhabricatorEdgeEditor())
|
||||
->setActor($this->getActor());
|
||||
|
||||
foreach ($old as $dst_phid => $edge) {
|
||||
if (!empty($new[$dst_phid])) {
|
||||
if ($old[$dst_phid]['data'] === $new[$dst_phid]['data']) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
$editor->removeEdge($src, $type, $dst_phid);
|
||||
}
|
||||
|
||||
foreach ($new as $dst_phid => $edge) {
|
||||
if (!empty($old[$dst_phid])) {
|
||||
if ($old[$dst_phid]['data'] === $new[$dst_phid]['data']) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
$data = array(
|
||||
'data' => $edge['data'],
|
||||
);
|
||||
|
||||
$editor->addEdge($src, $type, $dst_phid, $data);
|
||||
}
|
||||
|
||||
$editor->save();
|
||||
break;
|
||||
}
|
||||
|
||||
return $this->applyCustomExternalTransaction($object, $xaction);
|
||||
}
|
||||
|
||||
|
@ -460,7 +521,14 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
|
||||
switch ($type) {
|
||||
case PhabricatorTransactions::TYPE_SUBSCRIBERS:
|
||||
return $this->mergePHIDTransactions($u, $v);
|
||||
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$u_type = $u->getMetadataValue('edge:type');
|
||||
$v_type = $v->getMetadataValue('edge:type');
|
||||
if ($u_type == $v_type) {
|
||||
return $this->mergePHIDOrEdgeTransactions($u, $v);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
// By default, do not merge the transactions.
|
||||
|
@ -516,7 +584,7 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
return array_values($result);
|
||||
}
|
||||
|
||||
protected function mergePHIDTransactions(
|
||||
protected function mergePHIDOrEdgeTransactions(
|
||||
PhabricatorApplicationTransaction $u,
|
||||
PhabricatorApplicationTransaction $v) {
|
||||
|
||||
|
@ -529,7 +597,6 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
return $u;
|
||||
}
|
||||
|
||||
|
||||
protected function getPHIDTransactionNewValue(
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
|
@ -578,6 +645,110 @@ abstract class PhabricatorApplicationTransactionEditor
|
|||
return array_values($result);
|
||||
}
|
||||
|
||||
protected function getEdgeTransactionNewValue(
|
||||
PhabricatorApplicationTransaction $xaction) {
|
||||
|
||||
$new = $xaction->getNewValue();
|
||||
$new_add = idx($new, '+', array());
|
||||
unset($new['+']);
|
||||
$new_rem = idx($new, '-', array());
|
||||
unset($new['-']);
|
||||
$new_set = idx($new, '=', null);
|
||||
unset($new['=']);
|
||||
|
||||
if ($new) {
|
||||
throw new Exception(
|
||||
"Invalid 'new' value for Edge transaction. Value should contain only ".
|
||||
"keys '+' (add edges), '-' (remove edges) and '=' (set edges).");
|
||||
}
|
||||
|
||||
$old = $xaction->getOldValue();
|
||||
|
||||
$lists = array($new_set, $new_add, $new_rem);
|
||||
foreach ($lists as $list) {
|
||||
$this->checkEdgeList($list);
|
||||
}
|
||||
|
||||
$result = array();
|
||||
foreach ($old as $dst_phid => $edge) {
|
||||
if ($new_set !== null && empty($new_set[$dst_phid])) {
|
||||
continue;
|
||||
}
|
||||
$result[$dst_phid] = $this->normalizeEdgeTransactionValue(
|
||||
$xaction,
|
||||
$edge);
|
||||
}
|
||||
|
||||
if ($new_set !== null) {
|
||||
foreach ($new_set as $dst_phid => $edge) {
|
||||
$result[$dst_phid] = $this->normalizeEdgeTransactionValue(
|
||||
$xaction,
|
||||
$edge);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($new_add as $dst_phid => $edge) {
|
||||
$result[$dst_phid] = $this->normalizeEdgeTransactionValue(
|
||||
$xaction,
|
||||
$edge);
|
||||
}
|
||||
|
||||
foreach ($new_rem as $dst_phid => $edge) {
|
||||
unset($result[$dst_phid]);
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function checkEdgeList($list) {
|
||||
if (!$list) {
|
||||
return;
|
||||
}
|
||||
foreach ($list as $key => $item) {
|
||||
if (phid_get_type($key) === PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
|
||||
throw new Exception(
|
||||
"Edge transactions must have destination PHIDs as in edge ".
|
||||
"lists (found key '{$key}').");
|
||||
}
|
||||
if (!is_array($item) && $item !== $key) {
|
||||
throw new Exception(
|
||||
"Edge transactions must have PHIDs or edge specs as values ".
|
||||
"(found value '{$item}').");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function normalizeEdgeTransactionValue(
|
||||
PhabricatorApplicationTransaction $xaction,
|
||||
$edge) {
|
||||
|
||||
if (!is_array($edge)) {
|
||||
$edge = array(
|
||||
'dst' => $edge,
|
||||
);
|
||||
}
|
||||
|
||||
$edge_type = $xaction->getMetadataValue('edge:type');
|
||||
|
||||
if (empty($edge['type'])) {
|
||||
$edge['type'] = $edge_type;
|
||||
} else {
|
||||
if ($edge['type'] != $edge_type) {
|
||||
$this_type = $edge['type'];
|
||||
throw new Exception(
|
||||
"Edge transaction includes edge of type '{$this_type}', but ".
|
||||
"transaction is of type '{$edge_type}'. Each edge transaction must ".
|
||||
"alter edges of only one type.");
|
||||
}
|
||||
}
|
||||
|
||||
if (!isset($edge['data'])) {
|
||||
$edge['data'] = null;
|
||||
}
|
||||
|
||||
return $edge;
|
||||
}
|
||||
|
||||
protected function sortTransactions(array $xactions) {
|
||||
$head = array();
|
||||
$tail = array();
|
||||
|
|
|
@ -18,6 +18,7 @@ abstract class PhabricatorApplicationTransaction
|
|||
protected $transactionType;
|
||||
protected $oldValue;
|
||||
protected $newValue;
|
||||
protected $metadata = array();
|
||||
|
||||
protected $contentSource;
|
||||
|
||||
|
@ -36,6 +37,15 @@ abstract class PhabricatorApplicationTransaction
|
|||
return new PhabricatorApplicationTransactionView();
|
||||
}
|
||||
|
||||
public function getMetadataValue($key, $default = null) {
|
||||
return idx($this->metadata, $key, $default);
|
||||
}
|
||||
|
||||
public function setMetadataValue($key, $value) {
|
||||
$this->metadata[$key] = $value;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function generatePHID() {
|
||||
$type = PhabricatorPHIDConstants::PHID_TYPE_XACT;
|
||||
$subtype = $this->getApplicationTransactionType();
|
||||
|
@ -49,6 +59,7 @@ abstract class PhabricatorApplicationTransaction
|
|||
self::CONFIG_SERIALIZATION => array(
|
||||
'oldValue' => self::SERIALIZATION_JSON,
|
||||
'newValue' => self::SERIALIZATION_JSON,
|
||||
'metadata' => self::SERIALIZATION_JSON,
|
||||
),
|
||||
) + parent::getConfiguration();
|
||||
}
|
||||
|
@ -108,6 +119,10 @@ abstract class PhabricatorApplicationTransaction
|
|||
$phids[] = $old;
|
||||
$phids[] = $new;
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$phids[] = ipull($old, 'dst');
|
||||
$phids[] = ipull($new, 'dst');
|
||||
break;
|
||||
}
|
||||
|
||||
return array_mergev($phids);
|
||||
|
@ -160,6 +175,8 @@ abstract class PhabricatorApplicationTransaction
|
|||
case PhabricatorTransactions::TYPE_VIEW_POLICY:
|
||||
case PhabricatorTransactions::TYPE_EDIT_POLICY:
|
||||
return 'lock';
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
return 'link';
|
||||
}
|
||||
|
||||
return null;
|
||||
|
@ -201,6 +218,8 @@ abstract class PhabricatorApplicationTransaction
|
|||
return pht(
|
||||
'All users are already subscribed to this %s.',
|
||||
$this->getApplicationObjectTypeName());
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
return pht('Edges already exist; transaction has no effect.');
|
||||
}
|
||||
|
||||
return pht('Transaction has no effect.');
|
||||
|
@ -259,6 +278,12 @@ abstract class PhabricatorApplicationTransaction
|
|||
$this->renderHandleList($rem));
|
||||
}
|
||||
break;
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
$type = $this->getMetadata('edge:type');
|
||||
return pht(
|
||||
'%s edited edges of type %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$type);
|
||||
default:
|
||||
return pht(
|
||||
'%s edited this %s.',
|
||||
|
@ -295,6 +320,11 @@ abstract class PhabricatorApplicationTransaction
|
|||
'%s updated subscribers of %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
case PhabricatorTransactions::TYPE_EDGE:
|
||||
return pht(
|
||||
'%s updated edges of %s.',
|
||||
$this->renderHandleLink($author_phid),
|
||||
$this->renderHandleLink($object_phid));
|
||||
}
|
||||
|
||||
return $this->getTitle();
|
||||
|
|
|
@ -1194,6 +1194,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
|
|||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20130317.phrictionedge.sql'),
|
||||
),
|
||||
'20130310.xactionmeta.sql' => array(
|
||||
'type' => 'sql',
|
||||
'name' => $this->getPatchPath('20130310.xactionmeta.sql'),
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue