1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-26 16:52:41 +01:00

Migrate project membership to edges

Summary:
  - Store project members in edges.
  - Migrate existing members to edge storage.
  - Delete PhabricatorProjectAffiliation.
  - I left the actual underlying data around just in case something goes wrong; we can delete it evenutally.

Test Plan:
  - Ran migration.
  - Created a new project.
  - Joined and left a project.
  - Added and removed project members.
  - Manually called PhabricatorOwnersOwner::loadAffiliatedUserPHIDs() to verify its behavior.

Reviewers: vrana, btrahan

Reviewed By: vrana

CC: aran

Maniphest Tasks: T603

Differential Revision: https://secure.phabricator.com/D3186
This commit is contained in:
epriestley 2012-08-07 18:02:05 -07:00
parent d5a0352fd7
commit f9fcaa1f84
11 changed files with 164 additions and 165 deletions

View file

@ -0,0 +1,49 @@
<?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.
*/
echo "Migrating project members to edges...\n";
foreach (new LiskMigrationIterator(new PhabricatorProject()) as $proj) {
$id = $proj->getID();
echo "Project {$id}: ";
$members = queryfx_all(
$proj->establishConnection('r'),
'SELECT userPHID FROM %T WHERE projectPHID = %s',
'project_affiliation',
$proj->getPHID());
if (!$members) {
echo "-\n";
continue;
}
$members = ipull($members, 'userPHID');
$editor = new PhabricatorEdgeEditor();
$editor->setSuppressEvents(true);
foreach ($members as $user_phid) {
$editor->addEdge(
$proj->getPHID(),
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER,
$user_phid);
}
$editor->save();
echo "OKAY\n";
}
echo "Done.\n";

View file

@ -894,7 +894,6 @@ phutil_register_library_map(array(
'PhabricatorPolicyTestQuery' => 'applications/policy/__tests__/PhabricatorPolicyTestQuery.php', 'PhabricatorPolicyTestQuery' => 'applications/policy/__tests__/PhabricatorPolicyTestQuery.php',
'PhabricatorProfileHeaderView' => 'view/layout/PhabricatorProfileHeaderView.php', 'PhabricatorProfileHeaderView' => 'view/layout/PhabricatorProfileHeaderView.php',
'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php', 'PhabricatorProject' => 'applications/project/storage/PhabricatorProject.php',
'PhabricatorProjectAffiliation' => 'applications/project/storage/PhabricatorProjectAffiliation.php',
'PhabricatorProjectConstants' => 'applications/project/constants/PhabricatorProjectConstants.php', 'PhabricatorProjectConstants' => 'applications/project/constants/PhabricatorProjectConstants.php',
'PhabricatorProjectController' => 'applications/project/controller/PhabricatorProjectController.php', 'PhabricatorProjectController' => 'applications/project/controller/PhabricatorProjectController.php',
'PhabricatorProjectCreateController' => 'applications/project/controller/PhabricatorProjectCreateController.php', 'PhabricatorProjectCreateController' => 'applications/project/controller/PhabricatorProjectCreateController.php',
@ -1933,7 +1932,6 @@ phutil_register_library_map(array(
'PhabricatorPolicyTestQuery' => 'PhabricatorPolicyQuery', 'PhabricatorPolicyTestQuery' => 'PhabricatorPolicyQuery',
'PhabricatorProfileHeaderView' => 'AphrontView', 'PhabricatorProfileHeaderView' => 'AphrontView',
'PhabricatorProject' => 'PhabricatorProjectDAO', 'PhabricatorProject' => 'PhabricatorProjectDAO',
'PhabricatorProjectAffiliation' => 'PhabricatorProjectDAO',
'PhabricatorProjectController' => 'PhabricatorController', 'PhabricatorProjectController' => 'PhabricatorController',
'PhabricatorProjectCreateController' => 'PhabricatorProjectController', 'PhabricatorProjectCreateController' => 'PhabricatorProjectController',
'PhabricatorProjectDAO' => 'PhabricatorLiskDAO', 'PhabricatorProjectDAO' => 'PhabricatorLiskDAO',

View file

@ -58,12 +58,13 @@ final class PhabricatorOwnersOwner extends PhabricatorOwnersDAO {
array()); array());
$users_in_project_phids = array(); $users_in_project_phids = array();
if (idx($all_phids, PhabricatorPHIDConstants::PHID_TYPE_PROJ)) { $project_phids = idx($all_phids, PhabricatorPHIDConstants::PHID_TYPE_PROJ);
$users_in_project_phids = mpull( if ($project_phids) {
id(new PhabricatorProjectAffiliation())->loadAllWhere( $query = id(new PhabricatorEdgeQuery())
'projectPHID IN (%Ls)', ->withSourcePHIDs($project_phids)
idx($all_phids, PhabricatorPHIDConstants::PHID_TYPE_PROJ, array())), ->withEdgeTypes(array(PhabricatorEdgeConfig::TYPE_PROJ_MEMBER));
'getUserPHID'); $query->execute();
$users_in_project_phids = $query->getDestinationPHIDs();
} }
return array_unique(array_merge($users_in_project_phids, $user_phids)); return array_unique(array_merge($users_in_project_phids, $user_phids));

View file

@ -35,12 +35,19 @@ final class PhabricatorProjectCreateController
try { try {
$xactions = array(); $xactions = array();
$xaction = new PhabricatorProjectTransaction(); $xaction = new PhabricatorProjectTransaction();
$xaction->setTransactionType( $xaction->setTransactionType(
PhabricatorProjectTransactionType::TYPE_NAME); PhabricatorProjectTransactionType::TYPE_NAME);
$xaction->setNewValue($request->getStr('name')); $xaction->setNewValue($request->getStr('name'));
$xactions[] = $xaction; $xactions[] = $xaction;
$xaction = new PhabricatorProjectTransaction();
$xaction->setTransactionType(
PhabricatorProjectTransactionType::TYPE_MEMBERS);
$xaction->setNewValue(array($user->getPHID()));
$xactions[] = $xaction;
$editor = new PhabricatorProjectEditor($project); $editor = new PhabricatorProjectEditor($project);
$editor->setUser($user); $editor->setUser($user);
$editor->applyTransactions($xactions); $editor->applyTransactions($xactions);
@ -56,13 +63,6 @@ final class PhabricatorProjectCreateController
$profile->setProjectPHID($project->getPHID()); $profile->setProjectPHID($project->getPHID());
$profile->save(); $profile->save();
id(new PhabricatorProjectAffiliation())
->setUserPHID($user->getPHID())
->setProjectPHID($project->getPHID())
->setRole('Owner')
->setIsOwner(true)
->save();
if ($request->isAjax()) { if ($request->isAjax()) {
return id(new AphrontAjaxResponse()) return id(new AphrontAjaxResponse())
->setContent(array( ->setContent(array(

View file

@ -81,10 +81,12 @@ final class PhabricatorProjectListController
$profiles = mpull($profiles, null, 'getProjectPHID'); $profiles = mpull($profiles, null, 'getProjectPHID');
} }
$affil_groups = array(); $edge_query = new PhabricatorEdgeQuery();
if ($projects) { if ($projects) {
$affil_groups = PhabricatorProjectAffiliation::loadAllForProjectPHIDs( $edge_query
$project_phids); ->withSourcePHIDs($project_phids)
->withEdgeTypes(array(PhabricatorEdgeConfig::TYPE_PROJ_MEMBER))
->execute();
} }
$tasks = array(); $tasks = array();
@ -104,18 +106,17 @@ final class PhabricatorProjectListController
} }
} }
$rows = array(); $rows = array();
foreach ($projects as $project) { foreach ($projects as $project) {
$phid = $project->getPHID(); $phid = $project->getPHID();
$profile = idx($profiles, $phid); $profile = idx($profiles, $phid);
$affiliations = $affil_groups[$phid]; $members = $edge_query->getDestinationPHIDs(array($phid));
$group = idx($groups, $phid, array()); $group = idx($groups, $phid, array());
$task_count = count($group); $task_count = count($group);
$population = count($affiliations); $population = count($members);
if ($profile) { if ($profile) {
$blurb = $profile->getBlurb(); $blurb = $profile->getBlurb();

View file

@ -22,8 +22,8 @@ final class PhabricatorProjectEditor {
private $user; private $user;
private $projectName; private $projectName;
private $addAffiliations; private $addEdges = array();
private $remAffiliations; private $remEdges = array();
public function __construct(PhabricatorProject $project) { public function __construct(PhabricatorProject $project) {
$this->project = $project; $this->project = $project;
@ -67,22 +67,26 @@ final class PhabricatorProjectEditor {
} }
try { try {
$project->openTransaction();
$project->save(); $project->save();
$edge_type = PhabricatorEdgeConfig::TYPE_PROJ_MEMBER;
$editor = new PhabricatorEdgeEditor();
$editor->setUser($this->user);
foreach ($this->remEdges as $phid) {
$editor->removeEdge($project->getPHID(), $edge_type, $phid);
}
foreach ($this->addEdges as $phid) {
$editor->addEdge($project->getPHID(), $edge_type, $phid);
}
$editor->save();
foreach ($transactions as $xaction) { foreach ($transactions as $xaction) {
$xaction->setAuthorPHID($user->getPHID()); $xaction->setAuthorPHID($user->getPHID());
$xaction->setProjectID($project->getID()); $xaction->setProjectID($project->getID());
$xaction->save(); $xaction->save();
} }
$project->saveTransaction();
foreach ($this->remAffiliations as $affil) {
$affil->delete();
}
foreach ($this->addAffiliations as $affil) {
$affil->setProjectPHID($project->getPHID());
$affil->save();
}
foreach ($transactions as $xaction) { foreach ($transactions as $xaction) {
$this->publishTransactionStory($project, $xaction); $this->publishTransactionStory($project, $xaction);
@ -142,11 +146,10 @@ final class PhabricatorProjectEditor {
$xaction->setOldValue($project->getStatus()); $xaction->setOldValue($project->getStatus());
break; break;
case PhabricatorProjectTransactionType::TYPE_MEMBERS: case PhabricatorProjectTransactionType::TYPE_MEMBERS:
$affils = $project->loadAffiliations(); $member_phids = $project->loadMemberPHIDs();
$project->attachAffiliations($affils); $project->attachMemberPHIDs($member_phids);
$old_value = mpull($affils, 'getUserPHID'); $old_value = array_values($member_phids);
$old_value = array_values($old_value);
$xaction->setOldValue($old_value); $xaction->setOldValue($old_value);
$new_value = $xaction->getNewValue(); $new_value = $xaction->getNewValue();
@ -178,27 +181,8 @@ final class PhabricatorProjectEditor {
case PhabricatorProjectTransactionType::TYPE_MEMBERS: case PhabricatorProjectTransactionType::TYPE_MEMBERS:
$old = array_fill_keys($xaction->getOldValue(), true); $old = array_fill_keys($xaction->getOldValue(), true);
$new = array_fill_keys($xaction->getNewValue(), true); $new = array_fill_keys($xaction->getNewValue(), true);
$this->addEdges = array_keys(array_diff_key($new, $old));
$add = array(); $this->remEdges = array_keys(array_diff_key($old, $new));
$rem = array();
foreach ($project->getAffiliations() as $affil) {
if (empty($new[$affil->getUserPHID()])) {
$rem[] = $affil;
}
}
foreach ($new as $phid => $ignored) {
if (empty($old[$phid])) {
$affil = new PhabricatorProjectAffiliation();
$affil->setRole('');
$affil->setUserPHID($phid);
$add[] = $affil;
}
}
$this->addAffiliations = $add;
$this->remAffiliations = $rem;
break; break;
default: default:
throw new Exception("Unknown transaction type '{$type}'!"); throw new Exception("Unknown transaction type '{$type}'!");

View file

@ -60,27 +60,26 @@ final class PhabricatorProjectQuery extends PhabricatorOffsetPagedQuery {
$table = new PhabricatorProject(); $table = new PhabricatorProject();
$conn_r = $table->establishConnection('r'); $conn_r = $table->establishConnection('r');
$where = $this->buildWhereClause($conn_r);
$joins = $this->buildJoinsClause($conn_r);
$order = 'ORDER BY name';
$data = queryfx_all( $data = queryfx_all(
$conn_r, $conn_r,
'SELECT p.* FROM %T p %Q %Q %Q %Q', 'SELECT p.* FROM %T p %Q %Q %Q %Q %Q',
$table->getTableName(), $table->getTableName(),
$joins, $this->buildJoinClause($conn_r),
$where, $this->buildWhereClause($conn_r),
$order, $this->buildGroupClause($conn_r),
'ORDER BY name',
$this->buildLimitClause($conn_r)); $this->buildLimitClause($conn_r));
$projects = $table->loadAllFromArray($data); $projects = $table->loadAllFromArray($data);
if ($projects && $this->needMembers) { if ($projects && $this->needMembers) {
$members = PhabricatorProjectAffiliation::loadAllForProjectPHIDs( $members = id(new PhabricatorEdgeQuery())
mpull($projects, 'getPHID')); ->withSourcePHIDs(mpull($projects, 'getPHID'))
->withTypes(array(PhabricatorEdgeConfig::TYPE_PROJ_MEMBER))
->execute();
foreach ($projects as $project) { foreach ($projects as $project) {
$project->attachAffiliations( $phid = $project->getPHID();
array_values(idx($members, $project->getPHID(), array()))); $project->attachMemberPHIDs(array_keys($members[$phid]));
} }
} }
@ -93,37 +92,25 @@ final class PhabricatorProjectQuery extends PhabricatorOffsetPagedQuery {
if ($this->status != self::STATUS_ANY) { if ($this->status != self::STATUS_ANY) {
switch ($this->status) { switch ($this->status) {
case self::STATUS_OPEN: case self::STATUS_OPEN:
$where[] = qsprintf( case self::STATUS_ACTIVE:
$conn_r, $filter = array(
'status IN (%Ld)',
array(
PhabricatorProjectStatus::STATUS_ACTIVE, PhabricatorProjectStatus::STATUS_ACTIVE,
)); );
break; break;
case self::STATUS_CLOSED: case self::STATUS_CLOSED:
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
array(
PhabricatorProjectStatus::STATUS_ARCHIVED,
));
break;
case self::STATUS_ACTIVE:
$where[] = qsprintf(
$conn_r,
'status = %d',
PhabricatorProjectStatus::STATUS_ACTIVE);
break;
case self::STATUS_ARCHIVED: case self::STATUS_ARCHIVED:
$where[] = qsprintf( $filter = array(
$conn_r, PhabricatorProjectStatus::STATUS_ARCHIVED,
'status = %d', );
PhabricatorProjectStatus::STATUS_ARCHIVED);
break; break;
default: default:
throw new Exception( throw new Exception(
"Unknown project status '{$this->status}'!"); "Unknown project status '{$this->status}'!");
} }
$where[] = qsprintf(
$conn_r,
'status IN (%Ld)',
$filter);
} }
if ($this->ids) { if ($this->ids) {
@ -140,20 +127,33 @@ final class PhabricatorProjectQuery extends PhabricatorOffsetPagedQuery {
$this->phids); $this->phids);
} }
if ($this->memberPHIDs) {
$where[] = qsprintf(
$conn_r,
'e.type = %s AND e.dst IN (%Ls)',
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER,
$this->memberPHIDs);
}
return $this->formatWhereClause($where); return $this->formatWhereClause($where);
} }
private function buildJoinsClause($conn_r) { private function buildGroupClause($conn_r) {
$affil_table = new PhabricatorProjectAffiliation(); if ($this->memberPHIDs) {
return 'GROUP BY p.id';
} else {
return '';
}
}
private function buildJoinClause($conn_r) {
$joins = array(); $joins = array();
if ($this->memberPHIDs) { if ($this->memberPHIDs) {
$joins[] = qsprintf( $joins[] = qsprintf(
$conn_r, $conn_r,
'JOIN %T member ON member.projectPHID = p.phid 'JOIN %T e ON e.src = p.phid',
AND member.userPHID in (%Ls)', PhabricatorEdgeConfig::TABLE_NAME_EDGE);
$affil_table->getTableName(),
$this->memberPHIDs);
} }
return implode(' ', $joins); return implode(' ', $joins);

View file

@ -25,7 +25,8 @@ final class PhabricatorProject extends PhabricatorProjectDAO {
protected $subprojectPHIDs = array(); protected $subprojectPHIDs = array();
protected $phrictionSlug; protected $phrictionSlug;
private $affiliations; private $subprojectsNeedUpdate;
private $memberPHIDs;
public function getConfiguration() { public function getConfiguration() {
return array( return array(
@ -48,31 +49,25 @@ final class PhabricatorProject extends PhabricatorProjectDAO {
return $profile; return $profile;
} }
public function getMemberPHIDs() { public function attachMemberPHIDs(array $phids) {
return mpull($this->getAffiliations(), 'getUserPHID'); $this->memberPHIDs = $phids;
}
public function loadMemberPHIDs() {
return mpull($this->loadAffiliations(), 'getUserPHID');
}
public function getAffiliations() {
if ($this->affiliations === null) {
throw new Exception('Attach affiliations first!');
}
return $this->affiliations;
}
public function attachAffiliations(array $affiliations) {
assert_instances_of($affiliations, 'PhabricatorProjectAffiliation');
$this->affiliations = $affiliations;
return $this; return $this;
} }
public function loadAffiliations() { public function getMemberPHIDs() {
$affils = PhabricatorProjectAffiliation::loadAllForProjectPHIDs( if ($this->memberPHIDs === null) {
array($this->getPHID())); throw new Exception("Call attachMemberPHIDs() first!");
return $affils[$this->getPHID()]; }
return $this->memberPHIDs;
}
public function loadMemberPHIDs() {
if (!$this->getPHID()) {
return array();
}
return PhabricatorEdgeQuery::loadDestinationPHIDs(
$this->getPHID(),
PhabricatorEdgeConfig::TYPE_PROJ_MEMBER);
} }
public function setPhrictionSlug($slug) { public function setPhrictionSlug($slug) {

View file

@ -1,39 +0,0 @@
<?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.
*/
final class PhabricatorProjectAffiliation extends PhabricatorProjectDAO {
protected $projectPHID;
protected $userPHID;
protected $role;
protected $isOwner = 0;
public static function loadAllForProjectPHIDs($phids) {
if (!$phids) {
return array();
}
$default = array_fill_keys($phids, array());
$affiliations = id(new PhabricatorProjectAffiliation())->loadAllWhere(
'projectPHID IN (%Ls) ORDER BY dateCreated',
$phids);
return mgroup($affiliations, 'getProjectPHID') + $default;
}
}

View file

@ -38,6 +38,9 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
const TYPE_TASK_HAS_RELATED_DREV = 11; const TYPE_TASK_HAS_RELATED_DREV = 11;
const TYPE_DREV_HAS_RELATED_TASK = 12; const TYPE_DREV_HAS_RELATED_TASK = 12;
const TYPE_PROJ_MEMBER = 13;
const TYPE_MEMBER_OF_PROJ = 14;
const TYPE_TEST_NO_CYCLE = 9000; const TYPE_TEST_NO_CYCLE = 9000;
public static function getInverse($edge_type) { public static function getInverse($edge_type) {
@ -58,6 +61,9 @@ final class PhabricatorEdgeConfig extends PhabricatorEdgeConstants {
self::TYPE_TASK_HAS_RELATED_DREV => self::TYPE_DREV_HAS_RELATED_TASK, self::TYPE_TASK_HAS_RELATED_DREV => self::TYPE_DREV_HAS_RELATED_TASK,
self::TYPE_DREV_HAS_RELATED_TASK => self::TYPE_TASK_HAS_RELATED_DREV, self::TYPE_DREV_HAS_RELATED_TASK => self::TYPE_TASK_HAS_RELATED_DREV,
self::TYPE_PROJ_MEMBER => self::TYPE_MEMBER_OF_PROJ,
self::TYPE_MEMBER_OF_PROJ => self::TYPE_PROJ_MEMBER,
); );
return idx($map, $edge_type); return idx($map, $edge_type);

View file

@ -940,6 +940,10 @@ final class PhabricatorBuiltinPatchList extends PhabricatorSQLPatchList {
'type' => 'sql', 'type' => 'sql',
'name' => $this->getPatchPath('symbolcontexts.sql'), 'name' => $this->getPatchPath('symbolcontexts.sql'),
), ),
'migrate-project-edges.php' => array(
'type' => 'php',
'name' => $this->getPatchPath('migrate-project-edges.php'),
),
); );
} }