1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-19 11:11:10 +01:00

Improve ApplicationTransaction behavior for poorly constructed transactions

Summary:
Ref T2222. Five very small improvements:

  - I hit this exception and it took a bit to understand which transaction was causing problems. Add an `Exception` subclass which does a better job of making the message debuggable.
  - The `oldValue` of a transaction may be `null`, legitimately (for example, changing the `repositoryPHID` for a revision from `null` to some valid PHID). Do a check to see if `setOldValue()` has been called, instead of a check for a `null` value.
  - Add an additional check for the other case (shouldn't have a value, but does).
  - When we're not generating a value, don't bother calling the code to generate it. The best case scenario is that it has no effect; any effect it might have (changing the value) is always wrong.
  - Maniphest didn't fall back to the parent correctly when computing this flag, so it got the wrong result for `CustomField` transactions.

Test Plan: Resolved the issue I was hitting more easily, made updates to a `null`-valued custom field, and applied other normal sorts of transactions successfully.

Reviewers: btrahan

Reviewed By: btrahan

CC: aran

Maniphest Tasks: T4557, T2222

Differential Revision: https://secure.phabricator.com/D8401
This commit is contained in:
epriestley 2014-03-05 10:44:21 -08:00
parent dd60f25232
commit 857e3aee83
5 changed files with 82 additions and 26 deletions

View file

@ -1196,6 +1196,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionPHIDTypeTransaction' => 'applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php', 'PhabricatorApplicationTransactionPHIDTypeTransaction' => 'applications/transactions/phid/PhabricatorApplicationTransactionPHIDTypeTransaction.php',
'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php', 'PhabricatorApplicationTransactionQuery' => 'applications/transactions/query/PhabricatorApplicationTransactionQuery.php',
'PhabricatorApplicationTransactionResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionResponse.php', 'PhabricatorApplicationTransactionResponse' => 'applications/transactions/response/PhabricatorApplicationTransactionResponse.php',
'PhabricatorApplicationTransactionStructureException' => 'applications/transactions/exception/PhabricatorApplicationTransactionStructureException.php',
'PhabricatorApplicationTransactionTextDiffDetailView' => 'applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php', 'PhabricatorApplicationTransactionTextDiffDetailView' => 'applications/transactions/view/PhabricatorApplicationTransactionTextDiffDetailView.php',
'PhabricatorApplicationTransactionValidationError' => 'applications/transactions/error/PhabricatorApplicationTransactionValidationError.php', 'PhabricatorApplicationTransactionValidationError' => 'applications/transactions/error/PhabricatorApplicationTransactionValidationError.php',
'PhabricatorApplicationTransactionValidationException' => 'applications/transactions/exception/PhabricatorApplicationTransactionValidationException.php', 'PhabricatorApplicationTransactionValidationException' => 'applications/transactions/exception/PhabricatorApplicationTransactionValidationException.php',
@ -3895,6 +3896,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionPHIDTypeTransaction' => 'PhabricatorPHIDType', 'PhabricatorApplicationTransactionPHIDTypeTransaction' => 'PhabricatorPHIDType',
'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery', 'PhabricatorApplicationTransactionQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorApplicationTransactionResponse' => 'AphrontProxyResponse', 'PhabricatorApplicationTransactionResponse' => 'AphrontProxyResponse',
'PhabricatorApplicationTransactionStructureException' => 'Exception',
'PhabricatorApplicationTransactionTextDiffDetailView' => 'AphrontView', 'PhabricatorApplicationTransactionTextDiffDetailView' => 'AphrontView',
'PhabricatorApplicationTransactionValidationError' => 'Phobject', 'PhabricatorApplicationTransactionValidationError' => 'Phobject',
'PhabricatorApplicationTransactionValidationException' => 'Exception', 'PhabricatorApplicationTransactionValidationException' => 'Exception',

View file

@ -28,17 +28,13 @@ final class ManiphestTransaction
} }
public function shouldGenerateOldValue() { public function shouldGenerateOldValue() {
$generate = true;
switch ($this->getTransactionType()) { switch ($this->getTransactionType()) {
case self::TYPE_PROJECT_COLUMN: case self::TYPE_PROJECT_COLUMN:
case self::TYPE_EDGE: case self::TYPE_EDGE:
$generate = false; return false;
break;
default:
$generate = true;
break;
} }
return $generate;
return parent::shouldGenerateOldValue();
} }
public function getRequiredHandlePHIDs() { public function getRequiredHandlePHIDs() {

View file

@ -122,8 +122,11 @@ abstract class PhabricatorApplicationTransactionEditor
private function adjustTransactionValues( private function adjustTransactionValues(
PhabricatorLiskDAO $object, PhabricatorLiskDAO $object,
PhabricatorApplicationTransaction $xaction) { PhabricatorApplicationTransaction $xaction) {
$old = $this->getTransactionOldValue($object, $xaction);
$xaction->setOldValue($old); if ($xaction->shouldGenerateOldValue()) {
$old = $this->getTransactionOldValue($object, $xaction);
$xaction->setOldValue($old);
}
$new = $this->getTransactionNewValue($object, $xaction); $new = $this->getTransactionNewValue($object, $xaction);
$xaction->setNewValue($new); $xaction->setNewValue($new);
@ -723,37 +726,61 @@ abstract class PhabricatorApplicationTransactionEditor
assert_instances_of($xactions, 'PhabricatorApplicationTransaction'); assert_instances_of($xactions, 'PhabricatorApplicationTransaction');
foreach ($xactions as $xaction) { foreach ($xactions as $xaction) {
if ($xaction->getPHID() || $xaction->getID()) { if ($xaction->getPHID() || $xaction->getID()) {
throw new Exception( throw new PhabricatorApplicationTransactionStructureException(
"You can not apply transactions which already have IDs/PHIDs!"); $xaction,
pht(
"You can not apply transactions which already have IDs/PHIDs!"));
} }
if ($xaction->getObjectPHID()) { if ($xaction->getObjectPHID()) {
throw new Exception( throw new PhabricatorApplicationTransactionStructureException(
"You can not apply transactions which already have objectPHIDs!"); $xaction,
pht(
"You can not apply transactions which already have objectPHIDs!"));
} }
if ($xaction->getAuthorPHID()) { if ($xaction->getAuthorPHID()) {
throw new Exception( throw new PhabricatorApplicationTransactionStructureException(
"You can not apply transactions which already have authorPHIDs!"); $xaction,
pht(
'You can not apply transactions which already have authorPHIDs!'));
} }
if ($xaction->getCommentPHID()) { if ($xaction->getCommentPHID()) {
throw new Exception( throw new PhabricatorApplicationTransactionStructureException(
"You can not apply transactions which already have commentPHIDs!"); $xaction,
pht(
'You can not apply transactions which already have '.
'commentPHIDs!'));
} }
if ($xaction->getCommentVersion() !== 0) { if ($xaction->getCommentVersion() !== 0) {
throw new Exception( throw new PhabricatorApplicationTransactionStructureException(
"You can not apply transactions which already have commentVersions!"); $xaction,
pht(
'You can not apply transactions which already have '.
'commentVersions!'));
} }
if (!$xaction->shouldGenerateOldValue()) { $expect_value = !$xaction->shouldGenerateOldValue();
if ($xaction->getOldValue() === null) { $has_value = $xaction->hasOldValue();
throw new Exception(
'You can not apply transactions which should already have '. if ($expect_value && !$has_value) {
'oldValue but do not!'); throw new PhabricatorApplicationTransactionStructureException(
} $xaction,
pht(
'This transaction is supposed to have an oldValue set, but '.
'it does not!'));
}
if ($has_value && !$expect_value) {
throw new PhabricatorApplicationTransactionStructureException(
$xaction,
pht(
'This transaction should generate its oldValue automatically, '.
'but has already had one set!'));
} }
$type = $xaction->getTransactionType(); $type = $xaction->getTransactionType();
if (empty($types[$type])) { if (empty($types[$type])) {
throw new Exception( throw new PhabricatorApplicationTransactionStructureException(
$xaction,
pht( pht(
'Transaction has type "%s", but that transaction type is not '. 'Transaction has type "%s", but that transaction type is not '.
'supported by this editor (%s).', 'supported by this editor (%s).',

View file

@ -0,0 +1,20 @@
<?php
final class PhabricatorApplicationTransactionStructureException
extends Exception {
public function __construct(
PhabricatorApplicationTransaction $xaction,
$message) {
$full_message = pht(
'Attempting to apply a transaction (of class "%s", with type "%s") '.
'which has not been constructed correctly: %s',
get_class($xaction),
$xaction->getTransactionType(),
$message);
parent::__construct($full_message);
}
}

View file

@ -30,6 +30,7 @@ abstract class PhabricatorApplicationTransaction
private $transactionGroup = array(); private $transactionGroup = array();
private $viewer = self::ATTACHABLE; private $viewer = self::ATTACHABLE;
private $object = self::ATTACHABLE; private $object = self::ATTACHABLE;
private $oldValueHasBeenSet = false;
private $ignoreOnNoEffect; private $ignoreOnNoEffect;
@ -169,6 +170,16 @@ abstract class PhabricatorApplicationTransaction
return $blocks; return $blocks;
} }
public function setOldValue($value) {
$this->oldValueHasBeenSet = true;
$this->writeField('oldValue', $value);
return $this;
}
public function hasOldValue() {
return $this->oldValueHasBeenSet;
}
/* -( Rendering )---------------------------------------------------------- */ /* -( Rendering )---------------------------------------------------------- */