diff --git a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php index 0edc0b3f5a..0614f1d4ed 100644 --- a/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php +++ b/src/applications/transactions/conduit/TransactionSearchConduitAPIMethod.php @@ -1,243 +1,268 @@ 'phid|string', 'constraints' => 'map', ) + $this->getPagerParamTypes(); } protected function defineReturnType() { return 'list'; } protected function defineErrorTypes() { return array(); } protected function execute(ConduitAPIRequest $request) { $viewer = $request->getUser(); $pager = $this->newPager($request); $object_name = $request->getValue('objectIdentifier', null); 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" does not implement "%s", so transactions can not '. 'be loaded for it.')); } $xaction_query = PhabricatorApplicationTransactionQuery::newQueryForObject( $object); $xaction_query ->needHandles(false) ->withObjectPHIDs(array($object->getPHID())) ->setViewer($viewer); $constraints = $request->getValue('constraints', array()); - PhutilTypeSpec::checkMap( - $constraints, - array( - 'phids' => 'optional list', - )); - $with_phids = idx($constraints, 'phids'); - - if ($with_phids === array()) { - throw new Exception( - pht( - 'Constraint "phids" to "transaction.search" requires nonempty list, '. - 'empty list provided.')); - } - - if ($with_phids) { - $xaction_query->withPHIDs($with_phids); - } + $xaction_query = $this->applyConstraints($constraints, $xaction_query); $xactions = $xaction_query->executeWithCursorPager($pager); $comment_map = array(); if ($xactions) { $template = head($xactions)->getApplicationTransactionCommentObject(); if ($template) { $query = new PhabricatorApplicationTransactionTemplatedCommentQuery(); $comment_map = $query ->setViewer($viewer) ->setTemplate($template) ->withTransactionPHIDs(mpull($xactions, 'getPHID')) ->execute(); $comment_map = msort($comment_map, 'getCommentVersion'); $comment_map = array_reverse($comment_map); $comment_map = mgroup($comment_map, 'getTransactionPHID'); } } $modular_classes = array(); $modular_objects = array(); $modular_xactions = array(); foreach ($xactions as $xaction) { if (!$xaction instanceof PhabricatorModularTransaction) { continue; } // TODO: Hack things so certain transactions which don't have a modular // type yet can use a pseudotype until they modularize. Some day, we'll // modularize everything and remove this. switch ($xaction->getTransactionType()) { case DifferentialTransaction::TYPE_INLINE: $modular_template = new DifferentialRevisionInlineTransaction(); break; default: $modular_template = $xaction->getModularType(); break; } $modular_class = get_class($modular_template); if (!isset($modular_objects[$modular_class])) { try { $modular_object = newv($modular_class, array()); $modular_objects[$modular_class] = $modular_object; } catch (Exception $ex) { continue; } } $modular_classes[$xaction->getPHID()] = $modular_class; $modular_xactions[$modular_class][] = $xaction; } $modular_data_map = array(); foreach ($modular_objects as $class => $modular_type) { $modular_data_map[$class] = $modular_type ->setViewer($viewer) ->loadTransactionTypeConduitData($modular_xactions[$class]); } $data = array(); foreach ($xactions as $xaction) { $comments = idx($comment_map, $xaction->getPHID()); $comment_data = array(); if ($comments) { $removed = head($comments)->getIsDeleted(); foreach ($comments as $comment) { if ($removed) { // If the most recent version of the comment has been removed, // don't show the history. This is for consistency with the web // UI, which also prevents users from retrieving the content of // removed comments. $content = array( 'raw' => '', ); } else { $content = array( 'raw' => (string)$comment->getContent(), ); } $comment_data[] = array( 'id' => (int)$comment->getID(), 'phid' => (string)$comment->getPHID(), 'version' => (int)$comment->getCommentVersion(), 'authorPHID' => (string)$comment->getAuthorPHID(), 'dateCreated' => (int)$comment->getDateCreated(), 'dateModified' => (int)$comment->getDateModified(), 'removed' => (bool)$comment->getIsDeleted(), 'content' => $content, ); } } $fields = array(); $type = null; if (isset($modular_classes[$xaction->getPHID()])) { $modular_class = $modular_classes[$xaction->getPHID()]; $modular_object = $modular_objects[$modular_class]; $modular_data = $modular_data_map[$modular_class]; $type = $modular_object->getTransactionTypeForConduit($xaction); $fields = $modular_object->getFieldValuesForConduit( $xaction, $modular_data); } if (!$fields) { $fields = (object)$fields; } // If we haven't found a modular type, fallback for some simple core // types. Ideally, we'll modularize everything some day. if ($type === null) { switch ($xaction->getTransactionType()) { case PhabricatorTransactions::TYPE_COMMENT: $type = 'comment'; break; case PhabricatorTransactions::TYPE_CREATE: $type = 'create'; break; } } $data[] = array( 'id' => (int)$xaction->getID(), 'phid' => (string)$xaction->getPHID(), 'type' => $type, 'authorPHID' => (string)$xaction->getAuthorPHID(), 'objectPHID' => (string)$xaction->getObjectPHID(), 'dateCreated' => (int)$xaction->getDateCreated(), 'dateModified' => (int)$xaction->getDateModified(), 'comments' => $comment_data, 'fields' => $fields, ); } $results = array( 'data' => $data, ); return $this->addPagerResults($results, $pager); } + + private function applyConstraints( + array $constraints, + PhabricatorApplicationTransactionQuery $query) { + + PhutilTypeSpec::checkMap( + $constraints, + array( + 'phids' => 'optional list', + 'authorPHIDs' => 'optional list', + )); + + $with_phids = idx($constraints, 'phids'); + + if ($with_phids === array()) { + throw new Exception( + pht( + 'Constraint "phids" to "transaction.search" requires nonempty list, '. + 'empty list provided.')); + } + + if ($with_phids) { + $query->withPHIDs($with_phids); + } + + $with_authors = idx($constraints, 'authorPHIDs'); + if ($with_authors === array()) { + throw new Exception( + pht( + 'Constraint "authorPHIDs" to "transaction.search" requires '. + 'nonempty list, empty list provided.')); + } + + if ($with_authors) { + $query->withAuthorPHIDs($with_authors); + } + + return $query; + } + + }