Page MenuHomestyx hydra

No OneTemporary

diff --git a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php
index a1c10cd5e9..ce411aad35 100644
--- a/src/applications/owners/query/PhabricatorOwnersPackageQuery.php
+++ b/src/applications/owners/query/PhabricatorOwnersPackageQuery.php
@@ -1,419 +1,442 @@
<?php
final class PhabricatorOwnersPackageQuery
extends PhabricatorCursorPagedPolicyAwareQuery {
private $ids;
private $phids;
private $ownerPHIDs;
private $authorityPHIDs;
private $repositoryPHIDs;
private $paths;
private $statuses;
private $controlMap = array();
private $controlResults;
private $needPaths;
/**
* Query owner PHIDs exactly. This does not expand authorities, so a user
* PHID will not match projects the user is a member of.
*/
public function withOwnerPHIDs(array $phids) {
$this->ownerPHIDs = $phids;
return $this;
}
/**
* Query owner authority. This will expand authorities, so a user PHID will
* match both packages they own directly and packages owned by a project they
* are a member of.
*/
public function withAuthorityPHIDs(array $phids) {
$this->authorityPHIDs = $phids;
return $this;
}
public function withPHIDs(array $phids) {
$this->phids = $phids;
return $this;
}
public function withIDs(array $ids) {
$this->ids = $ids;
return $this;
}
public function withRepositoryPHIDs(array $phids) {
$this->repositoryPHIDs = $phids;
return $this;
}
public function withPaths(array $paths) {
$this->paths = $paths;
return $this;
}
public function withStatuses(array $statuses) {
$this->statuses = $statuses;
return $this;
}
public function withControl($repository_phid, array $paths) {
if (empty($this->controlMap[$repository_phid])) {
$this->controlMap[$repository_phid] = array();
}
foreach ($paths as $path) {
$path = (string)$path;
$this->controlMap[$repository_phid][$path] = $path;
}
// We need to load paths to execute control queries.
$this->needPaths = true;
return $this;
}
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
new PhabricatorOwnersPackageNameNgrams(),
$ngrams);
}
public function needPaths($need_paths) {
$this->needPaths = $need_paths;
return $this;
}
public function newResultObject() {
return new PhabricatorOwnersPackage();
}
protected function willExecute() {
$this->controlResults = array();
}
protected function loadPage() {
return $this->loadStandardPage($this->newResultObject());
}
protected function willFilterPage(array $packages) {
$package_ids = mpull($packages, 'getID');
$owners = id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID IN (%Ld)',
$package_ids);
$owners = mgroup($owners, 'getPackageID');
foreach ($packages as $package) {
$package->attachOwners(idx($owners, $package->getID(), array()));
}
return $packages;
}
protected function didFilterPage(array $packages) {
$package_ids = mpull($packages, 'getID');
if ($this->needPaths) {
$paths = id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID IN (%Ld)',
$package_ids);
$paths = mgroup($paths, 'getPackageID');
foreach ($packages as $package) {
$package->attachPaths(idx($paths, $package->getID(), array()));
}
}
if ($this->controlMap) {
foreach ($packages as $package) {
// If this package is archived, it's no longer a controlling package
// for any path. In particular, it can not force active packages with
// weak dominion to give up control.
if ($package->isArchived()) {
continue;
}
$this->controlResults[$package->getID()] = $package;
}
}
return $packages;
}
protected function buildJoinClauseParts(AphrontDatabaseConnection $conn) {
$joins = parent::buildJoinClauseParts($conn);
if ($this->shouldJoinOwnersTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %T o ON o.packageID = p.id',
id(new PhabricatorOwnersOwner())->getTableName());
}
if ($this->shouldJoinPathTable()) {
$joins[] = qsprintf(
$conn,
'JOIN %T rpath ON rpath.packageID = p.id',
id(new PhabricatorOwnersPath())->getTableName());
}
return $joins;
}
protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
$where = parent::buildWhereClauseParts($conn);
if ($this->phids !== null) {
$where[] = qsprintf(
$conn,
'p.phid IN (%Ls)',
$this->phids);
}
if ($this->ids !== null) {
$where[] = qsprintf(
$conn,
'p.id IN (%Ld)',
$this->ids);
}
if ($this->repositoryPHIDs !== null) {
$where[] = qsprintf(
$conn,
'rpath.repositoryPHID IN (%Ls)',
$this->repositoryPHIDs);
}
if ($this->authorityPHIDs !== null) {
$authority_phids = $this->expandAuthority($this->authorityPHIDs);
$where[] = qsprintf(
$conn,
'o.userPHID IN (%Ls)',
$authority_phids);
}
if ($this->ownerPHIDs !== null) {
$where[] = qsprintf(
$conn,
'o.userPHID IN (%Ls)',
$this->ownerPHIDs);
}
if ($this->paths !== null) {
$where[] = qsprintf(
$conn,
'rpath.path IN (%Ls)',
$this->getFragmentsForPaths($this->paths));
}
if ($this->statuses !== null) {
$where[] = qsprintf(
$conn,
'p.status IN (%Ls)',
$this->statuses);
}
if ($this->controlMap) {
$clauses = array();
foreach ($this->controlMap as $repository_phid => $paths) {
$fragments = $this->getFragmentsForPaths($paths);
$clauses[] = qsprintf(
$conn,
'(rpath.repositoryPHID = %s AND rpath.path IN (%Ls))',
$repository_phid,
$fragments);
}
$where[] = implode(' OR ', $clauses);
}
return $where;
}
protected function shouldGroupQueryResultRows() {
if ($this->shouldJoinOwnersTable()) {
return true;
}
if ($this->shouldJoinPathTable()) {
return true;
}
return parent::shouldGroupQueryResultRows();
}
public function getBuiltinOrders() {
return array(
'name' => array(
'vector' => array('name'),
'name' => pht('Name'),
),
) + parent::getBuiltinOrders();
}
public function getOrderableColumns() {
return parent::getOrderableColumns() + array(
'name' => array(
'table' => $this->getPrimaryTableAlias(),
'column' => 'name',
'type' => 'string',
'unique' => true,
'reverse' => true,
),
);
}
protected function getPagingValueMap($cursor, array $keys) {
$package = $this->loadCursorObject($cursor);
return array(
'id' => $package->getID(),
'name' => $package->getName(),
);
}
public function getQueryApplicationClass() {
return 'PhabricatorOwnersApplication';
}
protected function getPrimaryTableAlias() {
return 'p';
}
private function shouldJoinOwnersTable() {
if ($this->ownerPHIDs !== null) {
return true;
}
if ($this->authorityPHIDs !== null) {
return true;
}
return false;
}
private function shouldJoinPathTable() {
if ($this->repositoryPHIDs !== null) {
return true;
}
if ($this->paths !== null) {
return true;
}
if ($this->controlMap) {
return true;
}
return false;
}
private function expandAuthority(array $phids) {
$projects = id(new PhabricatorProjectQuery())
->setViewer($this->getViewer())
->withMemberPHIDs($phids)
->execute();
$project_phids = mpull($projects, 'getPHID');
return array_fuse($phids) + array_fuse($project_phids);
}
private function getFragmentsForPaths(array $paths) {
$fragments = array();
foreach ($paths as $path) {
foreach (PhabricatorOwnersPackage::splitPath($path) as $fragment) {
$fragments[$fragment] = $fragment;
}
}
return $fragments;
}
/* -( Path Control )------------------------------------------------------- */
/**
* Get a list of all packages which control a path or its parent directories,
* ordered from weakest to strongest.
*
* The first package has the most specific claim on the path; the last
* package has the most general claim. Multiple packages may have claims of
* equal strength, so this ordering is primarily one of usability and
* convenience.
*
* @return list<PhabricatorOwnersPackage> List of controlling packages.
*/
public function getControllingPackagesForPath(
$repository_phid,
$path,
$ignore_dominion = false) {
$path = (string)$path;
if (!isset($this->controlMap[$repository_phid][$path])) {
throw new PhutilInvalidStateException('withControl');
}
if ($this->controlResults === null) {
throw new PhutilInvalidStateException('execute');
}
$packages = $this->controlResults;
$weak_dominion = PhabricatorOwnersPackage::DOMINION_WEAK;
$path_fragments = PhabricatorOwnersPackage::splitPath($path);
$fragment_count = count($path_fragments);
$matches = array();
foreach ($packages as $package_id => $package) {
$best_match = null;
$include = false;
$repository_paths = $package->getPathsForRepository($repository_phid);
foreach ($repository_paths as $package_path) {
$strength = $package_path->getPathMatchStrength(
$path_fragments,
$fragment_count);
if ($strength > $best_match) {
$best_match = $strength;
$include = !$package_path->getExcluded();
}
}
if ($best_match && $include) {
if ($ignore_dominion) {
$is_weak = false;
} else {
$is_weak = ($package->getDominion() == $weak_dominion);
}
$matches[$package_id] = array(
'strength' => $best_match,
'weak' => $is_weak,
'package' => $package,
);
}
}
+ // At each strength level, drop weak packages if there are also strong
+ // packages of the same strength.
+ $strength_map = igroup($matches, 'strength');
+ foreach ($strength_map as $strength => $package_list) {
+ $any_strong = false;
+ foreach ($package_list as $package_id => $package) {
+ if (!$package['weak']) {
+ $any_strong = true;
+ break;
+ }
+ }
+ if ($any_strong) {
+ foreach ($package_list as $package_id => $package) {
+ if ($package['weak']) {
+ unset($matches[$package_id]);
+ }
+ }
+ }
+ }
+
$matches = isort($matches, 'strength');
$matches = array_reverse($matches);
- $first_id = null;
+ $strongest = null;
foreach ($matches as $package_id => $match) {
- if ($first_id === null) {
- $first_id = $package_id;
+ if ($strongest === null) {
+ $strongest = $match['strength'];
+ }
+
+ if ($match['strength'] === $strongest) {
continue;
}
if ($match['weak']) {
unset($matches[$package_id]);
}
}
return array_values(ipull($matches, 'package'));
}
}
diff --git a/src/applications/owners/storage/PhabricatorOwnersPackage.php b/src/applications/owners/storage/PhabricatorOwnersPackage.php
index 2b22a7a145..5f7b4f28c1 100644
--- a/src/applications/owners/storage/PhabricatorOwnersPackage.php
+++ b/src/applications/owners/storage/PhabricatorOwnersPackage.php
@@ -1,601 +1,628 @@
<?php
final class PhabricatorOwnersPackage
extends PhabricatorOwnersDAO
implements
PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface,
PhabricatorCustomFieldInterface,
PhabricatorDestructibleInterface,
PhabricatorConduitResultInterface,
PhabricatorFulltextInterface,
PhabricatorNgramsInterface {
protected $name;
protected $auditingEnabled;
protected $autoReview;
protected $description;
protected $primaryOwnerPHID;
protected $mailKey;
protected $status;
protected $viewPolicy;
protected $editPolicy;
protected $dominion;
private $paths = self::ATTACHABLE;
private $owners = self::ATTACHABLE;
private $customFields = self::ATTACHABLE;
private $pathRepositoryMap = array();
const STATUS_ACTIVE = 'active';
const STATUS_ARCHIVED = 'archived';
const AUTOREVIEW_NONE = 'none';
const AUTOREVIEW_SUBSCRIBE = 'subscribe';
const AUTOREVIEW_REVIEW = 'review';
const AUTOREVIEW_BLOCK = 'block';
const DOMINION_STRONG = 'strong';
const DOMINION_WEAK = 'weak';
public static function initializeNewPackage(PhabricatorUser $actor) {
$app = id(new PhabricatorApplicationQuery())
->setViewer($actor)
->withClasses(array('PhabricatorOwnersApplication'))
->executeOne();
$view_policy = $app->getPolicy(
PhabricatorOwnersDefaultViewCapability::CAPABILITY);
$edit_policy = $app->getPolicy(
PhabricatorOwnersDefaultEditCapability::CAPABILITY);
return id(new PhabricatorOwnersPackage())
->setAuditingEnabled(0)
->setAutoReview(self::AUTOREVIEW_NONE)
->setDominion(self::DOMINION_STRONG)
->setViewPolicy($view_policy)
->setEditPolicy($edit_policy)
->attachPaths(array())
->setStatus(self::STATUS_ACTIVE)
->attachOwners(array())
->setDescription('');
}
public static function getStatusNameMap() {
return array(
self::STATUS_ACTIVE => pht('Active'),
self::STATUS_ARCHIVED => pht('Archived'),
);
}
public static function getAutoreviewOptionsMap() {
return array(
self::AUTOREVIEW_NONE => array(
'name' => pht('No Autoreview'),
),
self::AUTOREVIEW_SUBSCRIBE => array(
'name' => pht('Subscribe to Changes'),
),
self::AUTOREVIEW_REVIEW => array(
'name' => pht('Review Changes'),
),
self::AUTOREVIEW_BLOCK => array(
'name' => pht('Review Changes (Blocking)'),
),
);
}
public static function getDominionOptionsMap() {
return array(
self::DOMINION_STRONG => array(
'name' => pht('Strong (Control All Paths)'),
'short' => pht('Strong'),
),
self::DOMINION_WEAK => array(
'name' => pht('Weak (Control Unowned Paths)'),
'short' => pht('Weak'),
),
);
}
protected function getConfiguration() {
return array(
// This information is better available from the history table.
self::CONFIG_TIMESTAMPS => false,
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'sort',
'description' => 'text',
'primaryOwnerPHID' => 'phid?',
'auditingEnabled' => 'bool',
'mailKey' => 'bytes20',
'status' => 'text32',
'autoReview' => 'text32',
'dominion' => 'text32',
),
) + parent::getConfiguration();
}
public function generatePHID() {
return PhabricatorPHID::generateNewPHID(
PhabricatorOwnersPackagePHIDType::TYPECONST);
}
public function save() {
if (!$this->getMailKey()) {
$this->setMailKey(Filesystem::readRandomCharacters(20));
}
return parent::save();
}
public function isArchived() {
return ($this->getStatus() == self::STATUS_ARCHIVED);
}
public function setName($name) {
$this->name = $name;
return $this;
}
public function loadOwners() {
if (!$this->getID()) {
return array();
}
return id(new PhabricatorOwnersOwner())->loadAllWhere(
'packageID = %d',
$this->getID());
}
public function loadPaths() {
if (!$this->getID()) {
return array();
}
return id(new PhabricatorOwnersPath())->loadAllWhere(
'packageID = %d',
$this->getID());
}
public static function loadAffectedPackages(
PhabricatorRepository $repository,
array $paths) {
if (!$paths) {
return array();
}
return self::loadPackagesForPaths($repository, $paths);
}
public static function loadOwningPackages($repository, $path) {
if (empty($path)) {
return array();
}
return self::loadPackagesForPaths($repository, array($path), 1);
}
private static function loadPackagesForPaths(
PhabricatorRepository $repository,
array $paths,
$limit = 0) {
$fragments = array();
foreach ($paths as $path) {
foreach (self::splitPath($path) as $fragment) {
$fragments[$fragment][$path] = true;
}
}
$package = new PhabricatorOwnersPackage();
$path = new PhabricatorOwnersPath();
$conn = $package->establishConnection('r');
$repository_clause = qsprintf(
$conn,
'AND p.repositoryPHID = %s',
$repository->getPHID());
// NOTE: The list of $paths may be very large if we're coming from
// the OwnersWorker and processing, e.g., an SVN commit which created a new
// branch. Break it apart so that it will fit within 'max_allowed_packet',
// and then merge results in PHP.
$rows = array();
foreach (array_chunk(array_keys($fragments), 128) as $chunk) {
$rows[] = queryfx_all(
$conn,
'SELECT pkg.id, pkg.dominion, p.excluded, p.path
FROM %T pkg JOIN %T p ON p.packageID = pkg.id
WHERE p.path IN (%Ls) AND pkg.status IN (%Ls) %Q',
$package->getTableName(),
$path->getTableName(),
$chunk,
array(
self::STATUS_ACTIVE,
),
$repository_clause);
}
$rows = array_mergev($rows);
$ids = self::findLongestPathsPerPackage($rows, $fragments);
if (!$ids) {
return array();
}
arsort($ids);
if ($limit) {
$ids = array_slice($ids, 0, $limit, $preserve_keys = true);
}
$ids = array_keys($ids);
$packages = $package->loadAllWhere('id in (%Ld)', $ids);
$packages = array_select_keys($packages, $ids);
return $packages;
}
public static function loadPackagesForRepository($repository) {
$package = new PhabricatorOwnersPackage();
$ids = ipull(
queryfx_all(
$package->establishConnection('r'),
'SELECT DISTINCT packageID FROM %T WHERE repositoryPHID = %s',
id(new PhabricatorOwnersPath())->getTableName(),
$repository->getPHID()),
'packageID');
return $package->loadAllWhere('id in (%Ld)', $ids);
}
public static function findLongestPathsPerPackage(array $rows, array $paths) {
// Build a map from each path to all the package paths which match it.
$path_hits = array();
$weak = array();
foreach ($rows as $row) {
$id = $row['id'];
$path = $row['path'];
$length = strlen($path);
$excluded = $row['excluded'];
if ($row['dominion'] === self::DOMINION_WEAK) {
$weak[$id] = true;
}
$matches = $paths[$path];
foreach ($matches as $match => $ignored) {
$path_hits[$match][] = array(
'id' => $id,
'excluded' => $excluded,
'length' => $length,
);
}
}
// For each path, process the matching package paths to figure out which
// packages actually own it.
$path_packages = array();
foreach ($path_hits as $match => $hits) {
$hits = isort($hits, 'length');
$packages = array();
foreach ($hits as $hit) {
$package_id = $hit['id'];
if ($hit['excluded']) {
unset($packages[$package_id]);
} else {
$packages[$package_id] = $hit;
}
}
$path_packages[$match] = $packages;
}
// Remove packages with weak dominion rules that should cede control to
// a more specific package.
if ($weak) {
foreach ($path_packages as $match => $packages) {
+
+ // Group packages by length.
+ $length_map = array();
+ foreach ($packages as $package_id => $package) {
+ $length_map[$package['length']][$package_id] = $package;
+ }
+
+ // For each path length, remove all weak packages if there are any
+ // strong packages of the same length. This makes sure that if there
+ // are one or more strong claims on a particular path, only those
+ // claims stand.
+ foreach ($length_map as $package_list) {
+ $any_strong = false;
+ foreach ($package_list as $package_id => $package) {
+ if (!isset($weak[$package_id])) {
+ $any_strong = true;
+ break;
+ }
+ }
+
+ if ($any_strong) {
+ foreach ($package_list as $package_id => $package) {
+ if (isset($weak[$package_id])) {
+ unset($packages[$package_id]);
+ }
+ }
+ }
+ }
+
$packages = isort($packages, 'length');
$packages = array_reverse($packages, true);
- $first = null;
+ $best_length = null;
foreach ($packages as $package_id => $package) {
- // If this is the first package we've encountered, note it and
- // continue. We're iterating over the packages from longest to
- // shortest match, so this package always has the strongest claim
- // on the path.
- if ($first === null) {
- $first = $package_id;
- continue;
+ // If this is the first package we've encountered, note its length.
+ // We're iterating over the packages from longest to shortest match,
+ // so packages of this length always have the best claim on the path.
+ if ($best_length === null) {
+ $best_length = $package['length'];
}
- // If this is the first package we saw, its claim stands even if it
- // is a weak package.
- if ($first === $package_id) {
+ // If this package has the same length as the best length, its claim
+ // stands.
+ if ($package['length'] === $best_length) {
continue;
}
- // If this is a weak package and not the first package we saw,
+ // If this is a weak package and does not have the best length,
// cede its claim to the stronger package.
if (isset($weak[$package_id])) {
unset($packages[$package_id]);
}
}
$path_packages[$match] = $packages;
}
}
// For each package that owns at least one path, identify the longest
// path it owns.
$package_lengths = array();
foreach ($path_packages as $match => $hits) {
foreach ($hits as $hit) {
$length = $hit['length'];
$id = $hit['id'];
if (empty($package_lengths[$id])) {
$package_lengths[$id] = $length;
} else {
$package_lengths[$id] = max($package_lengths[$id], $length);
}
}
}
return $package_lengths;
}
public static function splitPath($path) {
$trailing_slash = preg_match('@/$@', $path) ? '/' : '';
$path = trim($path, '/');
$parts = explode('/', $path);
$result = array();
while (count($parts)) {
$result[] = '/'.implode('/', $parts).$trailing_slash;
$trailing_slash = '/';
array_pop($parts);
}
$result[] = '/';
return array_reverse($result);
}
public function attachPaths(array $paths) {
assert_instances_of($paths, 'PhabricatorOwnersPath');
$this->paths = $paths;
// Drop this cache if we're attaching new paths.
$this->pathRepositoryMap = array();
return $this;
}
public function getPaths() {
return $this->assertAttached($this->paths);
}
public function getPathsForRepository($repository_phid) {
if (isset($this->pathRepositoryMap[$repository_phid])) {
return $this->pathRepositoryMap[$repository_phid];
}
$map = array();
foreach ($this->getPaths() as $path) {
if ($path->getRepositoryPHID() == $repository_phid) {
$map[] = $path;
}
}
$this->pathRepositoryMap[$repository_phid] = $map;
return $this->pathRepositoryMap[$repository_phid];
}
public function attachOwners(array $owners) {
assert_instances_of($owners, 'PhabricatorOwnersOwner');
$this->owners = $owners;
return $this;
}
public function getOwners() {
return $this->assertAttached($this->owners);
}
public function getOwnerPHIDs() {
return mpull($this->getOwners(), 'getUserPHID');
}
public function isOwnerPHID($phid) {
if (!$phid) {
return false;
}
$owner_phids = $this->getOwnerPHIDs();
$owner_phids = array_fuse($owner_phids);
return isset($owner_phids[$phid]);
}
public function getMonogram() {
return 'O'.$this->getID();
}
public function getURI() {
// TODO: Move these to "/O123" for consistency.
return '/owners/package/'.$this->getID().'/';
}
/* -( PhabricatorPolicyInterface )----------------------------------------- */
public function getCapabilities() {
return array(
PhabricatorPolicyCapability::CAN_VIEW,
PhabricatorPolicyCapability::CAN_EDIT,
);
}
public function getPolicy($capability) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
return $this->getViewPolicy();
case PhabricatorPolicyCapability::CAN_EDIT:
return $this->getEditPolicy();
}
}
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
switch ($capability) {
case PhabricatorPolicyCapability::CAN_VIEW:
if ($this->isOwnerPHID($viewer->getPHID())) {
return true;
}
break;
}
return false;
}
public function describeAutomaticCapability($capability) {
return pht('Owners of a package may always view it.');
}
/* -( PhabricatorApplicationTransactionInterface )------------------------- */
public function getApplicationTransactionEditor() {
return new PhabricatorOwnersPackageTransactionEditor();
}
public function getApplicationTransactionObject() {
return $this;
}
public function getApplicationTransactionTemplate() {
return new PhabricatorOwnersPackageTransaction();
}
public function willRenderTimeline(
PhabricatorApplicationTransactionView $timeline,
AphrontRequest $request) {
return $timeline;
}
/* -( PhabricatorCustomFieldInterface )------------------------------------ */
public function getCustomFieldSpecificationForRole($role) {
return PhabricatorEnv::getEnvConfig('owners.fields');
}
public function getCustomFieldBaseClass() {
return 'PhabricatorOwnersCustomField';
}
public function getCustomFields() {
return $this->assertAttached($this->customFields);
}
public function attachCustomFields(PhabricatorCustomFieldAttachment $fields) {
$this->customFields = $fields;
return $this;
}
/* -( PhabricatorDestructibleInterface )----------------------------------- */
public function destroyObjectPermanently(
PhabricatorDestructionEngine $engine) {
$this->openTransaction();
$conn_w = $this->establishConnection('w');
queryfx(
$conn_w,
'DELETE FROM %T WHERE packageID = %d',
id(new PhabricatorOwnersPath())->getTableName(),
$this->getID());
queryfx(
$conn_w,
'DELETE FROM %T WHERE packageID = %d',
id(new PhabricatorOwnersOwner())->getTableName(),
$this->getID());
$this->delete();
$this->saveTransaction();
}
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('name')
->setType('string')
->setDescription(pht('The name of the package.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('description')
->setType('string')
->setDescription(pht('The package description.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('status')
->setType('string')
->setDescription(pht('Active or archived status of the package.')),
id(new PhabricatorConduitSearchFieldSpecification())
->setKey('owners')
->setType('list<map<string, wild>>')
->setDescription(pht('List of package owners.')),
);
}
public function getFieldValuesForConduit() {
$owner_list = array();
foreach ($this->getOwners() as $owner) {
$owner_list[] = array(
'ownerPHID' => $owner->getUserPHID(),
);
}
return array(
'name' => $this->getName(),
'description' => $this->getDescription(),
'status' => $this->getStatus(),
'owners' => $owner_list,
);
}
public function getConduitSearchAttachments() {
return array(
id(new PhabricatorOwnersPathsSearchEngineAttachment())
->setAttachmentKey('paths'),
);
}
/* -( PhabricatorFulltextInterface )--------------------------------------- */
public function newFulltextEngine() {
return new PhabricatorOwnersPackageFulltextEngine();
}
/* -( PhabricatorNgramsInterface )----------------------------------------- */
public function newNgrams() {
return array(
id(new PhabricatorOwnersPackageNameNgrams())
->setValue($this->getName()),
);
}
}
diff --git a/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php b/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php
index 7d24fc6e8d..f21f09b44e 100644
--- a/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php
+++ b/src/applications/owners/storage/__tests__/PhabricatorOwnersPackageTestCase.php
@@ -1,105 +1,194 @@
<?php
final class PhabricatorOwnersPackageTestCase extends PhabricatorTestCase {
public function testFindLongestPathsPerPackage() {
$rows = array(
array(
'id' => 1,
'excluded' => 0,
'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG,
'path' => 'src/',
),
array(
'id' => 1,
'excluded' => 1,
'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG,
'path' => 'src/releeph/',
),
array(
'id' => 2,
'excluded' => 0,
'dominion' => PhabricatorOwnersPackage::DOMINION_STRONG,
'path' => 'src/releeph/',
),
);
$paths = array(
'src/' => array('src/a.php' => true, 'src/releeph/b.php' => true),
'src/releeph/' => array('src/releeph/b.php' => true),
);
$this->assertEqual(
array(
1 => strlen('src/'),
2 => strlen('src/releeph/'),
),
PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths));
$paths = array(
'src/' => array('src/releeph/b.php' => true),
'src/releeph/' => array('src/releeph/b.php' => true),
);
$this->assertEqual(
array(
2 => strlen('src/releeph/'),
),
PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths));
// Test packages with weak dominion. Here, only package #2 should own the
// path. Package #1's claim is ceded to Package #2 because it uses weak
// rules. Package #2 gets the claim even though it also has weak rules
// because there is no more-specific package.
$rows = array(
array(
'id' => 1,
'excluded' => 0,
'dominion' => PhabricatorOwnersPackage::DOMINION_WEAK,
'path' => 'src/',
),
array(
'id' => 2,
'excluded' => 0,
'dominion' => PhabricatorOwnersPackage::DOMINION_WEAK,
'path' => 'src/applications/',
),
);
$pvalue = array('src/applications/main/main.c' => true);
$paths = array(
'src/' => $pvalue,
'src/applications/' => $pvalue,
);
$this->assertEqual(
array(
2 => strlen('src/applications/'),
),
PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths));
// Now, add a more specific path to Package #1. This tests nested ownership
// in packages with weak dominion rules. This time, Package #1 should end
// up back on top, with Package #2 cedeing control to its more specific
// path.
$rows[] = array(
'id' => 1,
'excluded' => 0,
'dominion' => PhabricatorOwnersPackage::DOMINION_WEAK,
'path' => 'src/applications/main/',
);
$paths['src/applications/main/'] = $pvalue;
$this->assertEqual(
array(
1 => strlen('src/applications/main/'),
),
PhabricatorOwnersPackage::findLongestPathsPerPackage($rows, $paths));
+ // Test cases where multiple packages own the same path, with various
+ // dominion rules.
+
+ $main_c = 'src/applications/main/main.c';
+
+ $rules = array(
+ // All claims strong.
+ array(
+ PhabricatorOwnersPackage::DOMINION_STRONG,
+ PhabricatorOwnersPackage::DOMINION_STRONG,
+ PhabricatorOwnersPackage::DOMINION_STRONG,
+ ),
+ // All claims weak.
+ array(
+ PhabricatorOwnersPackage::DOMINION_WEAK,
+ PhabricatorOwnersPackage::DOMINION_WEAK,
+ PhabricatorOwnersPackage::DOMINION_WEAK,
+ ),
+ // Mixture of strong and weak claims, strong first.
+ array(
+ PhabricatorOwnersPackage::DOMINION_STRONG,
+ PhabricatorOwnersPackage::DOMINION_STRONG,
+ PhabricatorOwnersPackage::DOMINION_WEAK,
+ ),
+ // Mixture of strong and weak claims, weak first.
+ array(
+ PhabricatorOwnersPackage::DOMINION_WEAK,
+ PhabricatorOwnersPackage::DOMINION_STRONG,
+ PhabricatorOwnersPackage::DOMINION_STRONG,
+ ),
+ );
+
+ foreach ($rules as $rule_idx => $rule) {
+ $rows = array(
+ array(
+ 'id' => 1,
+ 'excluded' => 0,
+ 'dominion' => $rule[0],
+ 'path' => $main_c,
+ ),
+ array(
+ 'id' => 2,
+ 'excluded' => 0,
+ 'dominion' => $rule[1],
+ 'path' => $main_c,
+ ),
+ array(
+ 'id' => 3,
+ 'excluded' => 0,
+ 'dominion' => $rule[2],
+ 'path' => $main_c,
+ ),
+ );
+
+ $paths = array(
+ $main_c => $pvalue,
+ );
+
+ // If one or more packages have strong dominion, they should own the
+ // path. If not, all the packages with weak dominion should own the
+ // path.
+ $strong = array();
+ $weak = array();
+ foreach ($rule as $idx => $dominion) {
+ if ($dominion == PhabricatorOwnersPackage::DOMINION_STRONG) {
+ $strong[] = $idx + 1;
+ } else {
+ $weak[] = $idx + 1;
+ }
+ }
+
+ if ($strong) {
+ $expect = $strong;
+ } else {
+ $expect = $weak;
+ }
+
+ $expect = array_fill_keys($expect, strlen($main_c));
+ $actual = PhabricatorOwnersPackage::findLongestPathsPerPackage(
+ $rows,
+ $paths);
+
+ ksort($actual);
+
+ $this->assertEqual(
+ $expect,
+ $actual,
+ pht('Ruleset "%s" for Identical Ownership', $rule_idx));
+ }
}
}

File Metadata

Mime Type
text/x-diff
Expires
Tue, Mar 17, 1:22 AM (13 h, 57 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
964064
Default Alt Text
(35 KB)

Event Timeline