1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2025-01-08 22:01:03 +01:00

Add unique constraint for Almanac network names

Summary:
The name of networks should be unique.

Also adds support for exact-name queries for AlamanacNetworks.

Test Plan: Applied migration with existing duplicates, saw networks renamed, attempted to add duplicates, got a nice error message.

Reviewers: epriestley

Reviewed By: epriestley

Subscribers: Korvin, PHID-OPKG-gm6ozazyms6q6i22gyam

Differential Revision: https://secure.phabricator.com/D19379
This commit is contained in:
Austin McKinley 2018-04-18 11:45:50 -07:00
parent a817aa6c71
commit 0a83f253ed
5 changed files with 115 additions and 17 deletions

View file

@ -0,0 +1,46 @@
<?php
$table = new AlmanacNetwork();
$conn = $table->establishConnection('w');
queryfx(
$conn,
'LOCK TABLES %T WRITE',
$table->getTableName());
$seen = array();
foreach (new LiskMigrationIterator($table) as $network) {
$name = $network->getName();
// If this is the first copy of this row we've seen, mark it as seen and
// move on.
if (empty($seen[$name])) {
$seen[$name] = 1;
continue;
}
// Otherwise, rename this row.
while (true) {
$new_name = $name.'-'.$seen[$name];
if (empty($seen[$new_name])) {
$network->setName($new_name);
try {
$network->save();
break;
} catch (AphrontDuplicateKeyQueryException $ex) {
// New name is a dupe of a network we haven't seen yet.
}
}
$seen[$name]++;
}
$seen[$new_name] = 1;
}
queryfx(
$conn,
'ALTER TABLE %T ADD UNIQUE KEY `key_name` (name)',
$table->getTableName());
queryfx(
$conn,
'UNLOCK TABLES');

View file

@ -5,6 +5,7 @@ final class AlmanacNetworkQuery
private $ids;
private $phids;
private $names;
public function withIDs(array $ids) {
$this->ids = $ids;
@ -20,6 +21,11 @@ final class AlmanacNetworkQuery
return new AlmanacNetwork();
}
public function withNames(array $names) {
$this->names = $names;
return $this;
}
public function withNameNgrams($ngrams) {
return $this->withNgramsConstraint(
new AlmanacNetworkNameNgrams(),
@ -47,6 +53,13 @@ final class AlmanacNetworkQuery
$this->phids);
}
if ($this->names !== null) {
$where[] = qsprintf(
$conn,
'network.name IN (%Ls)',
$this->names);
}
return $where;
}

View file

@ -24,8 +24,15 @@ final class AlmanacNetwork
return array(
self::CONFIG_AUX_PHID => true,
self::CONFIG_COLUMN_SCHEMA => array(
'name' => 'text128',
'name' => 'sort128',
'mailKey' => 'bytes20',
),
self::CONFIG_KEY_SCHEMA => array(
'key_name' => array(
'columns' => array('name'),
'unique' => true,
),
),
) + parent::getConfiguration();
}

View file

@ -6,57 +6,58 @@ final class AlmanacNames extends Phobject {
if (strlen($name) < 3) {
throw new Exception(
pht(
'Almanac service, device, property and namespace names must be '.
'at least 3 characters long.'));
'Almanac service, device, property, network and namespace names '.
'must be at least 3 characters long.'));
}
if (strlen($name) > 100) {
throw new Exception(
pht(
'Almanac service, device, property and namespace names may not '.
'be more than 100 characters long.'));
'Almanac service, device, property, network and namespace names '.
'may not be more than 100 characters long.'));
}
if (!preg_match('/^[a-z0-9.-]+\z/', $name)) {
throw new Exception(
pht(
'Almanac service, device, property and namespace names may only '.
'contain lowercase letters, numbers, hyphens, and periods.'));
'Almanac service, device, property, network and namespace names '.
'may only contain lowercase letters, numbers, hyphens, and '.
'periods.'));
}
if (preg_match('/(^|\\.)\d+(\z|\\.)/', $name)) {
throw new Exception(
pht(
'Almanac service, device, property and namespace names may not '.
'have any segments containing only digits.'));
'Almanac service, device, network, property and namespace names '.
'may not have any segments containing only digits.'));
}
if (preg_match('/\.\./', $name)) {
throw new Exception(
pht(
'Almanac service, device, property and namespace names may not '.
'contain multiple consecutive periods.'));
'Almanac service, device, property, network and namespace names '.
'may not contain multiple consecutive periods.'));
}
if (preg_match('/\\.-|-\\./', $name)) {
throw new Exception(
pht(
'Almanac service, device, property and namespace names may not '.
'contain hyphens adjacent to periods.'));
'Almanac service, device, property, network and namespace names '.
'may not contain hyphens adjacent to periods.'));
}
if (preg_match('/--/', $name)) {
throw new Exception(
pht(
'Almanac service, device, property and namespace names may not '.
'contain multiple consecutive hyphens.'));
'Almanac service, device, property, network and namespace names '.
'may not contain multiple consecutive hyphens.'));
}
if (!preg_match('/^[a-z0-9].*[a-z0-9]\z/', $name)) {
throw new Exception(
pht(
'Almanac service, device, property and namespace names must begin '.
'and end with a letter or number.'));
'Almanac service, device, property, network and namespace names '.
'must begin and end with a letter or number.'));
}
}

View file

@ -38,6 +38,37 @@ final class AlmanacNetworkNameTransaction
pht('Network name is required.'));
}
foreach ($xactions as $xaction) {
$name = $xaction->getNewValue();
$message = null;
try {
AlmanacNames::validateName($name);
} catch (Exception $ex) {
$message = $ex->getMessage();
}
if ($message !== null) {
$errors[] = $this->newInvalidError($message, $xaction);
continue;
}
if ($name === $object->getName()) {
continue;
}
$other = id(new AlmanacNetworkQuery())
->setViewer(PhabricatorUser::getOmnipotentUser())
->withNames(array($name))
->executeOne();
if ($other && ($other->getID() != $object->getID())) {
$errors[] = $this->newInvalidError(
pht('Almanac networks must have unique names.'),
$xaction);
continue;
}
}
return $errors;
}