mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-26 08:42:41 +01:00
Fix an issue where "Request Review" of a fully-accepted revision would transition to "Accepted"
Summary: Ref T10967. This is explained in more detail in T10967#217125 When an author does "Request Review" on an accepted revision, void (in the sense of "cancel out", like a bank check) any "accepted" reviewers on the current diff. Test Plan: - Create a revision with author A and reviewer B. - Accept as B. - "Request Review" as A. - (With sticky accepts enabled.) - Before patch: revision swithced back to "accepted". - After patch: the earlier review is "voided" by te "Request Review", and the revision switches to "Review Requested". Reviewers: chad Reviewed By: chad Maniphest Tasks: T10967 Differential Revision: https://secure.phabricator.com/D17566
This commit is contained in:
parent
7d3956bec1
commit
aea46e55da
6 changed files with 109 additions and 21 deletions
2
resources/sql/autopatches/20170328.reviewers.01.void.sql
Normal file
2
resources/sql/autopatches/20170328.reviewers.01.void.sql
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
ALTER TABLE {$NAMESPACE}_differential.differential_reviewer
|
||||||
|
ADD voidedPHID VARBINARY(64);
|
|
@ -571,6 +571,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialRevisionTransactionType' => 'applications/differential/xaction/DifferentialRevisionTransactionType.php',
|
'DifferentialRevisionTransactionType' => 'applications/differential/xaction/DifferentialRevisionTransactionType.php',
|
||||||
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php',
|
'DifferentialRevisionUpdateHistoryView' => 'applications/differential/view/DifferentialRevisionUpdateHistoryView.php',
|
||||||
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
|
'DifferentialRevisionViewController' => 'applications/differential/controller/DifferentialRevisionViewController.php',
|
||||||
|
'DifferentialRevisionVoidTransaction' => 'applications/differential/xaction/DifferentialRevisionVoidTransaction.php',
|
||||||
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
|
'DifferentialSchemaSpec' => 'applications/differential/storage/DifferentialSchemaSpec.php',
|
||||||
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
|
'DifferentialSetDiffPropertyConduitAPIMethod' => 'applications/differential/conduit/DifferentialSetDiffPropertyConduitAPIMethod.php',
|
||||||
'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php',
|
'DifferentialStoredCustomField' => 'applications/differential/customfield/DifferentialStoredCustomField.php',
|
||||||
|
@ -5357,6 +5358,7 @@ phutil_register_library_map(array(
|
||||||
'DifferentialRevisionTransactionType' => 'PhabricatorModularTransactionType',
|
'DifferentialRevisionTransactionType' => 'PhabricatorModularTransactionType',
|
||||||
'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
|
'DifferentialRevisionUpdateHistoryView' => 'AphrontView',
|
||||||
'DifferentialRevisionViewController' => 'DifferentialController',
|
'DifferentialRevisionViewController' => 'DifferentialController',
|
||||||
|
'DifferentialRevisionVoidTransaction' => 'DifferentialRevisionTransactionType',
|
||||||
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
'DifferentialSchemaSpec' => 'PhabricatorConfigSchemaSpec',
|
||||||
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
'DifferentialSetDiffPropertyConduitAPIMethod' => 'DifferentialConduitAPIMethod',
|
||||||
'DifferentialStoredCustomField' => 'DifferentialCustomField',
|
'DifferentialStoredCustomField' => 'DifferentialCustomField',
|
||||||
|
|
|
@ -357,6 +357,14 @@ final class DifferentialTransactionEditor
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ($downgrade_accepts) {
|
||||||
|
$void_type = DifferentialRevisionVoidTransaction::TRANSACTIONTYPE;
|
||||||
|
$results[] = id(new DifferentialTransaction())
|
||||||
|
->setTransactionType($void_type)
|
||||||
|
->setIgnoreOnNoEffect(true)
|
||||||
|
->setNewValue(true);
|
||||||
|
}
|
||||||
|
|
||||||
$is_commandeer = false;
|
$is_commandeer = false;
|
||||||
switch ($xaction->getTransactionType()) {
|
switch ($xaction->getTransactionType()) {
|
||||||
case DifferentialTransaction::TYPE_UPDATE:
|
case DifferentialTransaction::TYPE_UPDATE:
|
||||||
|
@ -685,11 +693,8 @@ final class DifferentialTransactionEditor
|
||||||
break;
|
break;
|
||||||
case DifferentialReviewerStatus::STATUS_ACCEPTED:
|
case DifferentialReviewerStatus::STATUS_ACCEPTED:
|
||||||
if ($reviewer->isUser()) {
|
if ($reviewer->isUser()) {
|
||||||
$action_phid = $reviewer->getLastActionDiffPHID();
|
|
||||||
$active_phid = $active_diff->getPHID();
|
$active_phid = $active_diff->getPHID();
|
||||||
$is_current = ($action_phid == $active_phid);
|
if ($reviewer->isAccepted($active_phid)) {
|
||||||
|
|
||||||
if ($is_sticky_accept || $is_current) {
|
|
||||||
$has_accepting_user = true;
|
$has_accepting_user = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,6 +9,7 @@ final class DifferentialReviewer
|
||||||
protected $lastActionDiffPHID;
|
protected $lastActionDiffPHID;
|
||||||
protected $lastCommentDiffPHID;
|
protected $lastCommentDiffPHID;
|
||||||
protected $lastActorPHID;
|
protected $lastActorPHID;
|
||||||
|
protected $voidedPHID;
|
||||||
|
|
||||||
private $authority = array();
|
private $authority = array();
|
||||||
|
|
||||||
|
@ -19,6 +20,7 @@ final class DifferentialReviewer
|
||||||
'lastActionDiffPHID' => 'phid?',
|
'lastActionDiffPHID' => 'phid?',
|
||||||
'lastCommentDiffPHID' => 'phid?',
|
'lastCommentDiffPHID' => 'phid?',
|
||||||
'lastActorPHID' => 'phid?',
|
'lastActorPHID' => 'phid?',
|
||||||
|
'voidedPHID' => 'phid?',
|
||||||
),
|
),
|
||||||
self::CONFIG_KEY_SCHEMA => array(
|
self::CONFIG_KEY_SCHEMA => array(
|
||||||
'key_revision' => array(
|
'key_revision' => array(
|
||||||
|
@ -59,6 +61,13 @@ final class DifferentialReviewer
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// If this accept has been voided (for example, but a reviewer using
|
||||||
|
// "Request Review"), don't count it as a real "Accept" even if it is
|
||||||
|
// against the current diff PHID.
|
||||||
|
if ($this->getVoidedPHID()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!$diff_phid) {
|
if (!$diff_phid) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
|
@ -50,25 +50,19 @@ abstract class DifferentialRevisionReviewTransaction
|
||||||
protected function isViewerFullyAccepted(
|
protected function isViewerFullyAccepted(
|
||||||
DifferentialRevision $revision,
|
DifferentialRevision $revision,
|
||||||
PhabricatorUser $viewer) {
|
PhabricatorUser $viewer) {
|
||||||
return $this->isViewerReviewerStatusFullyAmong(
|
return $this->isViewerReviewerStatusFully(
|
||||||
$revision,
|
$revision,
|
||||||
$viewer,
|
$viewer,
|
||||||
array(
|
DifferentialReviewerStatus::STATUS_ACCEPTED);
|
||||||
DifferentialReviewerStatus::STATUS_ACCEPTED,
|
|
||||||
),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isViewerFullyRejected(
|
protected function isViewerFullyRejected(
|
||||||
DifferentialRevision $revision,
|
DifferentialRevision $revision,
|
||||||
PhabricatorUser $viewer) {
|
PhabricatorUser $viewer) {
|
||||||
return $this->isViewerReviewerStatusFullyAmong(
|
return $this->isViewerReviewerStatusFully(
|
||||||
$revision,
|
$revision,
|
||||||
$viewer,
|
$viewer,
|
||||||
array(
|
DifferentialReviewerStatus::STATUS_REJECTED);
|
||||||
DifferentialReviewerStatus::STATUS_REJECTED,
|
|
||||||
),
|
|
||||||
true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function getViewerReviewerStatus(
|
protected function getViewerReviewerStatus(
|
||||||
|
@ -90,11 +84,10 @@ abstract class DifferentialRevisionReviewTransaction
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function isViewerReviewerStatusFullyAmong(
|
private function isViewerReviewerStatusFully(
|
||||||
DifferentialRevision $revision,
|
DifferentialRevision $revision,
|
||||||
PhabricatorUser $viewer,
|
PhabricatorUser $viewer,
|
||||||
array $status_list,
|
$require_status) {
|
||||||
$require_current) {
|
|
||||||
|
|
||||||
// If the user themselves is not a reviewer, the reviews they have
|
// If the user themselves is not a reviewer, the reviews they have
|
||||||
// authority over can not all be in any set of states since their own
|
// authority over can not all be in any set of states since their own
|
||||||
|
@ -106,24 +99,33 @@ abstract class DifferentialRevisionReviewTransaction
|
||||||
|
|
||||||
$active_phid = $this->getActiveDiffPHID($revision);
|
$active_phid = $this->getActiveDiffPHID($revision);
|
||||||
|
|
||||||
|
$status_accepted = DifferentialReviewerStatus::STATUS_ACCEPTED;
|
||||||
|
$is_accepted = ($require_status == $status_accepted);
|
||||||
|
|
||||||
// Otherwise, check that all reviews they have authority over are in
|
// Otherwise, check that all reviews they have authority over are in
|
||||||
// the desired set of states.
|
// the desired set of states.
|
||||||
$status_map = array_fuse($status_list);
|
|
||||||
foreach ($revision->getReviewers() as $reviewer) {
|
foreach ($revision->getReviewers() as $reviewer) {
|
||||||
if (!$reviewer->hasAuthority($viewer)) {
|
if (!$reviewer->hasAuthority($viewer)) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$status = $reviewer->getReviewerStatus();
|
$status = $reviewer->getReviewerStatus();
|
||||||
if (!isset($status_map[$status])) {
|
if ($status != $require_status) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($require_current) {
|
// Here, we're primarily testing if we can remove a void on the review.
|
||||||
if ($reviewer->getLastActionDiffPHID() != $active_phid) {
|
if ($is_accepted) {
|
||||||
|
if (!$reviewer->isAccepted($active_phid)) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// This is a broader check to see if we can update the diff where the
|
||||||
|
// last action occurred.
|
||||||
|
if ($reviewer->getLastActionDiffPHID() != $active_phid) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return true;
|
return true;
|
||||||
|
@ -223,6 +225,11 @@ abstract class DifferentialRevisionReviewTransaction
|
||||||
$reviewer->setLastActorPHID($this->getActingAsPHID());
|
$reviewer->setLastActorPHID($this->getActingAsPHID());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Clear any outstanding void on this reviewer. A void may be placed
|
||||||
|
// by the author using "Request Review" when a reviewer has already
|
||||||
|
// accepted.
|
||||||
|
$reviewer->setVoidedPHID(null);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
$reviewer->save();
|
$reviewer->save();
|
||||||
} catch (AphrontDuplicateKeyQueryException $ex) {
|
} catch (AphrontDuplicateKeyQueryException $ex) {
|
||||||
|
|
|
@ -0,0 +1,63 @@
|
||||||
|
<?php
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This is an internal transaction type used to void reviews.
|
||||||
|
*
|
||||||
|
* For example, "Request Review" voids any open accepts, so they no longer
|
||||||
|
* act as current accepts.
|
||||||
|
*/
|
||||||
|
final class DifferentialRevisionVoidTransaction
|
||||||
|
extends DifferentialRevisionTransactionType {
|
||||||
|
|
||||||
|
const TRANSACTIONTYPE = 'differential.revision.void';
|
||||||
|
|
||||||
|
public function generateOldValue($object) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function generateNewValue($object, $value) {
|
||||||
|
$table = new DifferentialReviewer();
|
||||||
|
$table_name = $table->getTableName();
|
||||||
|
$conn = $table->establishConnection('w');
|
||||||
|
|
||||||
|
$rows = queryfx_all(
|
||||||
|
$conn,
|
||||||
|
'SELECT reviewerPHID FROM %T
|
||||||
|
WHERE revisionPHID = %s
|
||||||
|
AND voidedPHID IS NULL
|
||||||
|
AND reviewerStatus = %s',
|
||||||
|
$table_name,
|
||||||
|
$object->getPHID(),
|
||||||
|
DifferentialReviewerStatus::STATUS_ACCEPTED);
|
||||||
|
|
||||||
|
return ipull($rows, 'reviewerPHID');
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTransactionHasEffect($object, $old, $new) {
|
||||||
|
return (bool)$new;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function applyExternalEffects($object, $value) {
|
||||||
|
$table = new DifferentialReviewer();
|
||||||
|
$table_name = $table->getTableName();
|
||||||
|
$conn = $table->establishConnection('w');
|
||||||
|
|
||||||
|
queryfx(
|
||||||
|
$conn,
|
||||||
|
'UPDATE %T SET voidedPHID = %s
|
||||||
|
WHERE revisionPHID = %s
|
||||||
|
AND voidedPHID IS NULL
|
||||||
|
AND reviewerStatus = %s',
|
||||||
|
$table_name,
|
||||||
|
$this->getActingAsPHID(),
|
||||||
|
$object->getPHID(),
|
||||||
|
DifferentialReviewerStatus::STATUS_ACCEPTED);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function shouldHide() {
|
||||||
|
// This is an internal transaction, so don't show it in feeds or
|
||||||
|
// transaction logs.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Loading…
Reference in a new issue