1
0
Fork 0
mirror of https://we.phorge.it/source/phorge.git synced 2024-12-23 05:50:55 +01:00

Allow "transaction.search" to be called on an object type

Summary:
Ref T13631. This supports a more robust version of "poll for updates by using dateModified window queries" that uses transactions as a logical clock.

This is particularly relevant for commits, since they don't have a "dateModified" at time of writing.

Test Plan:
  - Queried for transactions by type and object.
  - Issued various invalid transaction queries, got appropriate errors.

Maniphest Tasks: T13631

Differential Revision: https://secure.phabricator.com/D21601
This commit is contained in:
epriestley 2021-03-10 10:05:57 -08:00
parent 404b55ce57
commit afdef332fb

View file

@ -8,7 +8,9 @@ final class TransactionSearchConduitAPIMethod
} }
public function getMethodDescription() { public function getMethodDescription() {
return pht('Read transactions and comments for an object.'); return pht(
'Read transactions and comments for a particular object '.
'or an entire object type.');
} }
public function getMethodDocumentation() { public function getMethodDocumentation() {
@ -23,6 +25,26 @@ One common reason to call this method is that you're implmenting a webhook and
just received a notification that an object has changed. See the Webhooks just received a notification that an object has changed. See the Webhooks
documentation for more detailed discussion of this use case. documentation for more detailed discussion of this use case.
One Object Type at a Time
=========================
This API method can query transactions for any type of object which supports
transactions, but only one type of object can be queried per call. For example:
you can retrieve transactions affecting Tasks, or you can retrieve transactions
affecting Revisions, but a single call can not retrieve both.
This is a technical limitation arising because (among other reasons) there is
no global ordering on transactions.
To find transactions for a specific object (like a particular task), pass the
object PHID or an appropriate object identifier (like `T123`) as an
`objectIdentifier`.
To find all transactions for an object type, pass the object type constant as
an `objectType`. For example, the correct identifier for tasks is `TASK`. (You
can quickly find an unknown type constant by looking at the PHID of an object
of that type.)
Constraints Constraints
=========== ===========
@ -64,8 +86,9 @@ EOREMARKUP
protected function defineParamTypes() { protected function defineParamTypes() {
return array( return array(
'objectIdentifier' => 'phid|string', 'objectIdentifier' => 'optional phid|string',
'constraints' => 'map<string, wild>', 'objectType' => 'optional string',
'constraints' => 'optional map<string, wild>',
) + $this->getPagerParamTypes(); ) + $this->getPagerParamTypes();
} }
@ -81,43 +104,19 @@ EOREMARKUP
$viewer = $request->getUser(); $viewer = $request->getUser();
$pager = $this->newPager($request); $pager = $this->newPager($request);
$object_name = $request->getValue('objectIdentifier', null); $object = $this->loadTemplateObject($request);
if (!strlen($object_name)) {
throw new Exception(
pht(
'When calling "transaction.search", you must provide an object to '.
'retrieve transactions for.'));
}
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames(array($object_name))
->executeOne();
if (!$object) {
throw new Exception(
pht(
'No object "%s" exists.',
$object_name));
}
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
throw new Exception(
pht(
'Object "%s" (of type "%s") does not implement "%s", so '.
'transactions can not be loaded for it.',
$object_name,
get_class($object),
'PhabricatorApplicationTransactionInterface'));
}
$xaction_query = PhabricatorApplicationTransactionQuery::newQueryForObject( $xaction_query = PhabricatorApplicationTransactionQuery::newQueryForObject(
$object); $object);
$xaction_query $xaction_query
->needHandles(false) ->needHandles(false)
->withObjectPHIDs(array($object->getPHID()))
->setViewer($viewer); ->setViewer($viewer);
if ($object->getPHID()) {
$xaction_query->withObjectPHIDs(array($object->getPHID()));
}
$constraints = $request->getValue('constraints', array()); $constraints = $request->getValue('constraints', array());
$xaction_query = $this->applyConstraints($constraints, $xaction_query); $xaction_query = $this->applyConstraints($constraints, $xaction_query);
@ -355,4 +354,65 @@ EOREMARKUP
); );
} }
private function loadTemplateObject(ConduitAPIRequest $request) {
$viewer = $request->getUser();
$object_identifier = $request->getValue('objectIdentifier');
$object_type = $request->getValue('objectType');
$has_identifier = ($object_identifier !== null);
$has_type = ($object_type !== null);
if (!$has_type && !$has_identifier) {
throw new Exception(
pht(
'Calls to "transaction.search" must specify either an "objectType" '.
'or an "objectIdentifier"'));
} else if ($has_type && $has_identifier) {
throw new Exception(
pht(
'Calls to "transaction.search" must not specify both an '.
'"objectType" and an "objectIdentifier".'));
}
if ($has_type) {
$all_types = PhabricatorPHIDType::getAllTypes();
if (!isset($all_types[$object_type])) {
ksort($all_types);
throw new Exception(
pht(
'In call to "transaction.search", specified "objectType" ("%s") '.
'is unknown. Valid object types are: %s.',
$object_type,
implode(', ', array_keys($all_types))));
}
$object = $all_types[$object_type]->newObject();
} else {
$object = id(new PhabricatorObjectQuery())
->setViewer($viewer)
->withNames(array($object_identifier))
->executeOne();
if (!$object) {
throw new Exception(
pht(
'In call to "transaction.search", specified "objectIdentifier" '.
'("%s") does not exist.',
$object_identifier));
}
}
if (!($object instanceof PhabricatorApplicationTransactionInterface)) {
throw new Exception(
pht(
'In call to "transaction.search", selected object (of type "%s") '.
'does not implement "%s", so transactions can not be loaded for it.',
get_class($object),
'PhabricatorApplicationTransactionInterface'));
}
return $object;
}
} }