1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-11-23 15:22:41 +01:00

Add "owners.search" Conduit API endpoint, with CustomField support

Summary:
Ref T9964. Adds a new-style "owners.search" endpoint, and an extension for customfields.

Puts enough indirection in place to give us nice, consistent "custom.key" user-facing keys instead of "std:custom:owners:na0shf9a8dfdsafl" junk.

Test Plan:
  - Searched Owners via API.
  - Searched by ID.
  - Ordered by custom fields.
  - Reviewed API docs.
  - Used normal search with ordering.
  - Viewed custom field values in search results.

Reviewers: chad

Reviewed By: chad

Maniphest Tasks: T9964

Differential Revision: https://secure.phabricator.com/D14758
This commit is contained in:
epriestley 2015-12-12 16:25:51 -08:00
parent 32a7674c22
commit 9499987cfe
12 changed files with 198 additions and 14 deletions

View file

@ -1417,6 +1417,7 @@ phutil_register_library_map(array(
'OwnersEditConduitAPIMethod' => 'applications/owners/conduit/OwnersEditConduitAPIMethod.php', 'OwnersEditConduitAPIMethod' => 'applications/owners/conduit/OwnersEditConduitAPIMethod.php',
'OwnersPackageReplyHandler' => 'applications/owners/mail/OwnersPackageReplyHandler.php', 'OwnersPackageReplyHandler' => 'applications/owners/mail/OwnersPackageReplyHandler.php',
'OwnersQueryConduitAPIMethod' => 'applications/owners/conduit/OwnersQueryConduitAPIMethod.php', 'OwnersQueryConduitAPIMethod' => 'applications/owners/conduit/OwnersQueryConduitAPIMethod.php',
'OwnersSearchConduitAPIMethod' => 'applications/owners/conduit/OwnersSearchConduitAPIMethod.php',
'PHIDConduitAPIMethod' => 'applications/phid/conduit/PHIDConduitAPIMethod.php', 'PHIDConduitAPIMethod' => 'applications/phid/conduit/PHIDConduitAPIMethod.php',
'PHIDInfoConduitAPIMethod' => 'applications/phid/conduit/PHIDInfoConduitAPIMethod.php', 'PHIDInfoConduitAPIMethod' => 'applications/phid/conduit/PHIDInfoConduitAPIMethod.php',
'PHIDLookupConduitAPIMethod' => 'applications/phid/conduit/PHIDLookupConduitAPIMethod.php', 'PHIDLookupConduitAPIMethod' => 'applications/phid/conduit/PHIDLookupConduitAPIMethod.php',
@ -1993,7 +1994,7 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldAttachment' => 'infrastructure/customfield/field/PhabricatorCustomFieldAttachment.php', 'PhabricatorCustomFieldAttachment' => 'infrastructure/customfield/field/PhabricatorCustomFieldAttachment.php',
'PhabricatorCustomFieldConfigOptionType' => 'infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php', 'PhabricatorCustomFieldConfigOptionType' => 'infrastructure/customfield/config/PhabricatorCustomFieldConfigOptionType.php',
'PhabricatorCustomFieldDataNotAvailableException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldDataNotAvailableException.php', 'PhabricatorCustomFieldDataNotAvailableException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldDataNotAvailableException.php',
'PhabricatorCustomFieldEditEngineExtension' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditEngineExtension.php', 'PhabricatorCustomFieldEditEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldEditEngineExtension.php',
'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php', 'PhabricatorCustomFieldEditField' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditField.php',
'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php', 'PhabricatorCustomFieldEditType' => 'infrastructure/customfield/editor/PhabricatorCustomFieldEditType.php',
'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php', 'PhabricatorCustomFieldHeraldField' => 'infrastructure/customfield/herald/PhabricatorCustomFieldHeraldField.php',
@ -2006,6 +2007,7 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldNotAttachedException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldNotAttachedException.php', 'PhabricatorCustomFieldNotAttachedException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldNotAttachedException.php',
'PhabricatorCustomFieldNotProxyException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldNotProxyException.php', 'PhabricatorCustomFieldNotProxyException' => 'infrastructure/customfield/exception/PhabricatorCustomFieldNotProxyException.php',
'PhabricatorCustomFieldNumericIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldNumericIndexStorage.php', 'PhabricatorCustomFieldNumericIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldNumericIndexStorage.php',
'PhabricatorCustomFieldSearchEngineExtension' => 'infrastructure/customfield/engineextension/PhabricatorCustomFieldSearchEngineExtension.php',
'PhabricatorCustomFieldStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php', 'PhabricatorCustomFieldStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStorage.php',
'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php', 'PhabricatorCustomFieldStringIndexStorage' => 'infrastructure/customfield/storage/PhabricatorCustomFieldStringIndexStorage.php',
'PhabricatorCustomHeaderConfigType' => 'applications/config/custom/PhabricatorCustomHeaderConfigType.php', 'PhabricatorCustomHeaderConfigType' => 'applications/config/custom/PhabricatorCustomHeaderConfigType.php',
@ -5439,6 +5441,7 @@ phutil_register_library_map(array(
'OwnersEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod', 'OwnersEditConduitAPIMethod' => 'PhabricatorEditEngineAPIMethod',
'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler', 'OwnersPackageReplyHandler' => 'PhabricatorMailReplyHandler',
'OwnersQueryConduitAPIMethod' => 'OwnersConduitAPIMethod', 'OwnersQueryConduitAPIMethod' => 'OwnersConduitAPIMethod',
'OwnersSearchConduitAPIMethod' => 'PhabricatorSearchEngineAPIMethod',
'PHIDConduitAPIMethod' => 'ConduitAPIMethod', 'PHIDConduitAPIMethod' => 'ConduitAPIMethod',
'PHIDInfoConduitAPIMethod' => 'PHIDConduitAPIMethod', 'PHIDInfoConduitAPIMethod' => 'PHIDConduitAPIMethod',
'PHIDLookupConduitAPIMethod' => 'PHIDConduitAPIMethod', 'PHIDLookupConduitAPIMethod' => 'PHIDConduitAPIMethod',
@ -6116,6 +6119,7 @@ phutil_register_library_map(array(
'PhabricatorCustomFieldNotAttachedException' => 'Exception', 'PhabricatorCustomFieldNotAttachedException' => 'Exception',
'PhabricatorCustomFieldNotProxyException' => 'Exception', 'PhabricatorCustomFieldNotProxyException' => 'Exception',
'PhabricatorCustomFieldNumericIndexStorage' => 'PhabricatorCustomFieldIndexStorage', 'PhabricatorCustomFieldNumericIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
'PhabricatorCustomFieldSearchEngineExtension' => 'PhabricatorSearchEngineExtension',
'PhabricatorCustomFieldStorage' => 'PhabricatorLiskDAO', 'PhabricatorCustomFieldStorage' => 'PhabricatorLiskDAO',
'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage', 'PhabricatorCustomFieldStringIndexStorage' => 'PhabricatorCustomFieldIndexStorage',
'PhabricatorCustomHeaderConfigType' => 'PhabricatorConfigOptionType', 'PhabricatorCustomHeaderConfigType' => 'PhabricatorConfigOptionType',
@ -6791,6 +6795,7 @@ phutil_register_library_map(array(
'PhabricatorApplicationTransactionInterface', 'PhabricatorApplicationTransactionInterface',
'PhabricatorCustomFieldInterface', 'PhabricatorCustomFieldInterface',
'PhabricatorDestructibleInterface', 'PhabricatorDestructibleInterface',
'PhabricatorConduitResultInterface',
), ),
'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource', 'PhabricatorOwnersPackageDatasource' => 'PhabricatorTypeaheadDatasource',
'PhabricatorOwnersPackageEditEngine' => 'PhabricatorEditEngine', 'PhabricatorOwnersPackageEditEngine' => 'PhabricatorEditEngine',

View file

@ -0,0 +1,18 @@
<?php
final class OwnersSearchConduitAPIMethod
extends PhabricatorSearchEngineAPIMethod {
public function getAPIMethodName() {
return 'owners.search';
}
public function newSearchEngine() {
return new PhabricatorOwnersPackageSearchEngine();
}
public function getMethodSummary() {
return pht('Read information about Owners packages.');
}
}

View file

@ -6,7 +6,8 @@ final class PhabricatorOwnersPackage
PhabricatorPolicyInterface, PhabricatorPolicyInterface,
PhabricatorApplicationTransactionInterface, PhabricatorApplicationTransactionInterface,
PhabricatorCustomFieldInterface, PhabricatorCustomFieldInterface,
PhabricatorDestructibleInterface { PhabricatorDestructibleInterface,
PhabricatorConduitResultInterface {
protected $name; protected $name;
protected $originalName; protected $originalName;
@ -365,4 +366,33 @@ final class PhabricatorOwnersPackage
$this->saveTransaction(); $this->saveTransaction();
} }
/* -( PhabricatorConduitResultInterface )---------------------------------- */
public function getFieldSpecificationsForConduit() {
return array(
'name' => array(
'type' => 'string',
'description' => pht('The name of the package.'),
),
'description' => array(
'type' => 'string',
'description' => pht('The package description.'),
),
'status' => array(
'type' => 'string',
'description' => pht('Active or archived status of the package.'),
),
);
}
public function getFieldValuesForConduit() {
return array(
'name' => $this->getName(),
'description' => $this->getDescription(),
'status' => $this->getStatus(),
);
}
} }

View file

@ -1174,6 +1174,14 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
public function getSearchFieldsForConduit() { public function getSearchFieldsForConduit() {
$fields = $this->buildSearchFields(); $fields = $this->buildSearchFields();
// These are handled separately for Conduit, so don't show them as
// supported.
unset($fields['ids']);
unset($fields['phids']);
unset($fields['order']);
unset($fields['limit']);
return $fields; return $fields;
} }
@ -1220,6 +1228,7 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$query = $this->buildQueryFromSavedQuery($saved_query); $query = $this->buildQueryFromSavedQuery($saved_query);
$pager = $this->newPagerForSavedQuery($saved_query); $pager = $this->newPagerForSavedQuery($saved_query);
$this->setAutomaticConstraintsForConduit($query, $request, $constraints);
$this->setQueryOrderForConduit($query, $request); $this->setQueryOrderForConduit($query, $request);
$this->setPagerLimitForConduit($pager, $request); $this->setPagerLimitForConduit($pager, $request);
$this->setPagerOffsetsForConduit($pager, $request); $this->setPagerOffsetsForConduit($pager, $request);
@ -1269,6 +1278,12 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
$field_extensions = array(); $field_extensions = array();
foreach ($extensions as $key => $extension) { foreach ($extensions as $key => $extension) {
$extension->setViewer($this->requireViewer());
if (!$extension->supportsObject($object)) {
continue;
}
if ($extension->getFieldSpecificationsForConduit($object)) { if ($extension->getFieldSpecificationsForConduit($object)) {
$field_extensions[$key] = $extension; $field_extensions[$key] = $extension;
} }
@ -1277,6 +1292,22 @@ abstract class PhabricatorApplicationSearchEngine extends Phobject {
return $field_extensions; return $field_extensions;
} }
private function setAutomaticConstraintsForConduit(
$query,
ConduitAPIRequest $request,
array $constraints) {
$with_ids = idx($constraints, 'ids');
if ($with_ids) {
$query->withIDs($with_ids);
}
$with_phids = idx($constraints, 'phids');
if ($with_phids) {
$query->withPHIDs($with_phids);
}
}
private function setQueryOrderForConduit($query, ConduitAPIRequest $request) { private function setQueryOrderForConduit($query, ConduitAPIRequest $request) {
$order = $request->getValue('order'); $order = $request->getValue('order');
if ($order === null) { if ($order === null) {

View file

@ -143,13 +143,18 @@ EOTEXT
$head_type = pht('Type'); $head_type = pht('Type');
$head_desc = pht('Description'); $head_desc = pht('Description');
$desc_ids = pht('Search for specific objects by ID.');
$desc_phids = pht('Search for specific objects by PHID.');
$fields = $engine->getSearchFieldsForConduit(); $fields = $engine->getSearchFieldsForConduit();
$table = array(); $table = array();
$table[] = "| {$head_key} | {$head_label} | {$head_type} | {$head_desc} |"; $table[] = "| {$head_key} | {$head_label} | {$head_type} | {$head_desc} |";
$table[] = '|-------------|---------------|--------------|--------------|'; $table[] = '|-------------|---------------|--------------|--------------|';
$table[] = "| `ids` | **IDs** | `list<int>` | {$desc_ids} |";
$table[] = "| `phids` | **PHIDs** | `list<phid>` | {$desc_phids} |";
foreach ($fields as $field) { foreach ($fields as $field) {
$key = $field->getKey(); $key = $field->getKeyForConduit();
$label = $field->getLabel(); $label = $field->getLabel();
// TODO: Support generating and surfacing this information. // TODO: Support generating and surfacing this information.

View file

@ -26,6 +26,10 @@ final class PhabricatorSearchCustomFieldProxyField
return $this; return $this;
} }
public function getLabel() {
return $this->getCustomField()->getFieldName();
}
public function getCustomField() { public function getCustomField() {
return $this->customField; return $this->customField;
} }
@ -34,6 +38,10 @@ final class PhabricatorSearchCustomFieldProxyField
return null; return null;
} }
public function getKeyForConduit() {
return $this->getCustomField()->getModernFieldKey();
}
protected function getValueExistsInRequest(AphrontRequest $request, $key) { protected function getValueExistsInRequest(AphrontRequest $request, $key) {
// TODO: For historical reasons, the keys we look for don't line up with // TODO: For historical reasons, the keys we look for don't line up with
// the keys that CustomFields use. Just skip the check for existence and // the keys that CustomFields use. Just skip the check for existence and

View file

@ -271,4 +271,14 @@ abstract class PhabricatorSearchField extends Phobject {
return $list; return $list;
} }
public function getKeyForConduit() {
// TODO: This shouldn't really be different, but internal handling of
// custom field keys is a bit of a mess for now.
return $this->getKey();
}
} }

View file

@ -0,0 +1,58 @@
<?php
final class PhabricatorCustomFieldSearchEngineExtension
extends PhabricatorSearchEngineExtension {
const EXTENSIONKEY = 'customfield';
public function isExtensionEnabled() {
return true;
}
public function getExtensionName() {
return pht('Support for Custom Fields');
}
public function supportsObject($object) {
return ($object instanceof PhabricatorCustomFieldInterface);
}
public function getFieldSpecificationsForConduit($object) {
$fields = PhabricatorCustomField::getObjectFields(
$object,
PhabricatorCustomField::ROLE_CONDUIT);
$map = array();
foreach ($fields->getFields() as $field) {
$key = $field->getModernFieldKey();
$map[$key] = array(
'type' => 'wild',
'description' => $field->getFieldDescription(),
);
}
return $map;
}
public function getFieldValuesForConduit($object) {
// TODO: This is currently very inefficient. We should bulk-load these
// field values instead.
$fields = PhabricatorCustomField::getObjectFields(
$object,
PhabricatorCustomField::ROLE_CONDUIT);
$fields
->setViewer($this->getViewer())
->readFieldsFromStorage($object);
$map = array();
foreach ($fields->getFields() as $field) {
$key = $field->getModernFieldKey();
$map[$key] = $field->getConduitDictionaryValue();
}
return $map;
}
}

View file

@ -188,6 +188,13 @@ abstract class PhabricatorCustomField extends Phobject {
$field_key_is_incomplete = true); $field_key_is_incomplete = true);
} }
public function getModernFieldKey() {
if ($this->proxy) {
return $this->proxy->getModernFieldKey();
}
return $this->getFieldKey();
}
/** /**
* Return a human-readable field name. * Return a human-readable field name.
@ -199,7 +206,7 @@ abstract class PhabricatorCustomField extends Phobject {
if ($this->proxy) { if ($this->proxy) {
return $this->proxy->getFieldName(); return $this->proxy->getFieldName();
} }
return $this->getFieldKey(); return $this->getModernFieldKey();
} }
@ -1109,7 +1116,7 @@ abstract class PhabricatorCustomField extends Phobject {
return $this->newEditField() return $this->newEditField()
->setKey($this->getFieldKey()) ->setKey($this->getFieldKey())
->setEditTypeKey('custom.'.$this->getFieldKey()) ->setEditTypeKey($this->getModernFieldKey())
->setLabel($this->getFieldName()) ->setLabel($this->getFieldName())
->setDescription($this->getFieldDescription()) ->setDescription($this->getFieldDescription())
->setTransactionType($this->getApplicationTransactionType()) ->setTransactionType($this->getApplicationTransactionType())

View file

@ -440,7 +440,7 @@ abstract class PhabricatorStandardCustomField
} }
protected function newStandardEditField() { protected function newStandardEditField() {
$short = 'custom.'.$this->getRawStandardFieldKey(); $short = $this->getModernFieldKey();
return parent::newStandardEditField() return parent::newStandardEditField()
->setEditTypeKey($short) ->setEditTypeKey($short)
@ -451,4 +451,16 @@ abstract class PhabricatorStandardCustomField
return true; return true;
} }
public function shouldAppearInConduitDictionary() {
return true;
}
public function getModernFieldKey() {
return 'custom.'.$this->getRawStandardFieldKey();
}
public function getConduitDictionaryValue() {
return $this->getFieldValue();
}
} }

View file

@ -716,13 +716,13 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
continue; continue;
} }
$key = $field->getFieldKey(); $legacy_key = 'custom:'.$field->getFieldKey();
$digest = $field->getFieldIndex(); $modern_key = $field->getModernFieldKey();
$full_key = 'custom:'.$key; $orders[$modern_key] = array(
$orders[$full_key] = array( 'vector' => array($modern_key, 'id'),
'vector' => array($full_key, 'id'),
'name' => $field->getFieldName(), 'name' => $field->getFieldName(),
'aliases' => array($legacy_key),
); );
} }
} }
@ -903,11 +903,11 @@ abstract class PhabricatorCursorPagedPolicyAwareQuery
continue; continue;
} }
$key = $field->getFieldKey();
$digest = $field->getFieldIndex(); $digest = $field->getFieldIndex();
$full_key = 'custom:'.$key; $key = $field->getModernFieldKey();
$columns[$full_key] = array(
$columns[$key] = array(
'table' => 'appsearch_order_'.$digest, 'table' => 'appsearch_order_'.$digest,
'column' => 'indexValue', 'column' => 'indexValue',
'type' => $index->getIndexValueType(), 'type' => $index->getIndexValueType(),