1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-29 10:12:41 +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:
epriestley 2013-03-28 08:34:34 -07:00
parent 1ee7bbe305
commit 696498934c
6 changed files with 234 additions and 4 deletions

View 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 = '';

View file

@ -192,7 +192,7 @@ final class ConpherenceEditor extends PhabricatorApplicationTransactionEditor {
return $v; return $v;
case ConpherenceTransactionType::TYPE_FILES: case ConpherenceTransactionType::TYPE_FILES:
case ConpherenceTransactionType::TYPE_PARTICIPANTS: case ConpherenceTransactionType::TYPE_PARTICIPANTS:
return $this->mergePHIDTransactions($u, $v); return $this->mergePHIDOrEdgeTransactions($u, $v);
} }
return parent::mergeTransactions($u, $v); return parent::mergeTransactions($u, $v);

View file

@ -6,6 +6,7 @@ final class PhabricatorTransactions {
const TYPE_SUBSCRIBERS = 'core:subscribers'; const TYPE_SUBSCRIBERS = 'core:subscribers';
const TYPE_VIEW_POLICY = 'core:view-policy'; const TYPE_VIEW_POLICY = 'core:view-policy';
const TYPE_EDIT_POLICY = 'core:edit-policy'; const TYPE_EDIT_POLICY = 'core:edit-policy';
const TYPE_EDGE = 'core:edge';
const COLOR_RED = 'red'; const COLOR_RED = 'red';
const COLOR_ORANGE = 'orange'; const COLOR_ORANGE = 'orange';

View file

@ -101,6 +101,26 @@ abstract class PhabricatorApplicationTransactionEditor
return $object->getViewPolicy(); return $object->getViewPolicy();
case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY:
return $object->getEditPolicy(); 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: default:
return $this->getCustomTransactionOldValue($object, $xaction); return $this->getCustomTransactionOldValue($object, $xaction);
} }
@ -115,6 +135,8 @@ abstract class PhabricatorApplicationTransactionEditor
case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY:
return $xaction->getNewValue(); return $xaction->getNewValue();
case PhabricatorTransactions::TYPE_EDGE:
return $this->getEdgeTransactionNewValue($xaction);
default: default:
return $this->getCustomTransactionNewValue($object, $xaction); return $this->getCustomTransactionNewValue($object, $xaction);
} }
@ -180,7 +202,46 @@ abstract class PhabricatorApplicationTransactionEditor
$subeditor->save(); $subeditor->save();
break; 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); return $this->applyCustomExternalTransaction($object, $xaction);
} }
@ -460,7 +521,14 @@ abstract class PhabricatorApplicationTransactionEditor
switch ($type) { switch ($type) {
case PhabricatorTransactions::TYPE_SUBSCRIBERS: 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. // By default, do not merge the transactions.
@ -516,7 +584,7 @@ abstract class PhabricatorApplicationTransactionEditor
return array_values($result); return array_values($result);
} }
protected function mergePHIDTransactions( protected function mergePHIDOrEdgeTransactions(
PhabricatorApplicationTransaction $u, PhabricatorApplicationTransaction $u,
PhabricatorApplicationTransaction $v) { PhabricatorApplicationTransaction $v) {
@ -529,7 +597,6 @@ abstract class PhabricatorApplicationTransactionEditor
return $u; return $u;
} }
protected function getPHIDTransactionNewValue( protected function getPHIDTransactionNewValue(
PhabricatorApplicationTransaction $xaction) { PhabricatorApplicationTransaction $xaction) {
@ -578,6 +645,110 @@ abstract class PhabricatorApplicationTransactionEditor
return array_values($result); 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) { protected function sortTransactions(array $xactions) {
$head = array(); $head = array();
$tail = array(); $tail = array();

View file

@ -18,6 +18,7 @@ abstract class PhabricatorApplicationTransaction
protected $transactionType; protected $transactionType;
protected $oldValue; protected $oldValue;
protected $newValue; protected $newValue;
protected $metadata = array();
protected $contentSource; protected $contentSource;
@ -36,6 +37,15 @@ abstract class PhabricatorApplicationTransaction
return new PhabricatorApplicationTransactionView(); 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() { public function generatePHID() {
$type = PhabricatorPHIDConstants::PHID_TYPE_XACT; $type = PhabricatorPHIDConstants::PHID_TYPE_XACT;
$subtype = $this->getApplicationTransactionType(); $subtype = $this->getApplicationTransactionType();
@ -49,6 +59,7 @@ abstract class PhabricatorApplicationTransaction
self::CONFIG_SERIALIZATION => array( self::CONFIG_SERIALIZATION => array(
'oldValue' => self::SERIALIZATION_JSON, 'oldValue' => self::SERIALIZATION_JSON,
'newValue' => self::SERIALIZATION_JSON, 'newValue' => self::SERIALIZATION_JSON,
'metadata' => self::SERIALIZATION_JSON,
), ),
) + parent::getConfiguration(); ) + parent::getConfiguration();
} }
@ -108,6 +119,10 @@ abstract class PhabricatorApplicationTransaction
$phids[] = $old; $phids[] = $old;
$phids[] = $new; $phids[] = $new;
break; break;
case PhabricatorTransactions::TYPE_EDGE:
$phids[] = ipull($old, 'dst');
$phids[] = ipull($new, 'dst');
break;
} }
return array_mergev($phids); return array_mergev($phids);
@ -160,6 +175,8 @@ abstract class PhabricatorApplicationTransaction
case PhabricatorTransactions::TYPE_VIEW_POLICY: case PhabricatorTransactions::TYPE_VIEW_POLICY:
case PhabricatorTransactions::TYPE_EDIT_POLICY: case PhabricatorTransactions::TYPE_EDIT_POLICY:
return 'lock'; return 'lock';
case PhabricatorTransactions::TYPE_EDGE:
return 'link';
} }
return null; return null;
@ -201,6 +218,8 @@ abstract class PhabricatorApplicationTransaction
return pht( return pht(
'All users are already subscribed to this %s.', 'All users are already subscribed to this %s.',
$this->getApplicationObjectTypeName()); $this->getApplicationObjectTypeName());
case PhabricatorTransactions::TYPE_EDGE:
return pht('Edges already exist; transaction has no effect.');
} }
return pht('Transaction has no effect.'); return pht('Transaction has no effect.');
@ -259,6 +278,12 @@ abstract class PhabricatorApplicationTransaction
$this->renderHandleList($rem)); $this->renderHandleList($rem));
} }
break; break;
case PhabricatorTransactions::TYPE_EDGE:
$type = $this->getMetadata('edge:type');
return pht(
'%s edited edges of type %s.',
$this->renderHandleLink($author_phid),
$type);
default: default:
return pht( return pht(
'%s edited this %s.', '%s edited this %s.',
@ -295,6 +320,11 @@ abstract class PhabricatorApplicationTransaction
'%s updated subscribers of %s.', '%s updated subscribers of %s.',
$this->renderHandleLink($author_phid), $this->renderHandleLink($author_phid),
$this->renderHandleLink($object_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(); return $this->getTitle();

View file

@ -1194,6 +1194,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'sql', 'type' => 'sql',
'name' => $this->getPatchPath('20130317.phrictionedge.sql'), 'name' => $this->getPatchPath('20130317.phrictionedge.sql'),
), ),
'20130310.xactionmeta.sql' => array(
'type' => 'sql',
'name' => $this->getPatchPath('20130310.xactionmeta.sql'),
),
); );
} }