Page MenuHomePhabricator

D17462.id41992.diff
No OneTemporary

D17462.id41992.diff

diff --git a/src/__phutil_library_map__.php b/src/__phutil_library_map__.php
--- a/src/__phutil_library_map__.php
+++ b/src/__phutil_library_map__.php
@@ -1077,6 +1077,7 @@
'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php',
'DrydockWorker' => 'applications/drydock/worker/DrydockWorker.php',
'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php',
+ 'EdgeSearchConduitAPIMethod' => 'infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php',
'FeedConduitAPIMethod' => 'applications/feed/conduit/FeedConduitAPIMethod.php',
'FeedPublishConduitAPIMethod' => 'applications/feed/conduit/FeedPublishConduitAPIMethod.php',
'FeedPublisherHTTPWorker' => 'applications/feed/worker/FeedPublisherHTTPWorker.php',
@@ -2589,6 +2590,8 @@
'PhabricatorEdgeEditType' => 'applications/transactions/edittype/PhabricatorEdgeEditType.php',
'PhabricatorEdgeEditor' => 'infrastructure/edges/editor/PhabricatorEdgeEditor.php',
'PhabricatorEdgeGraph' => 'infrastructure/edges/util/PhabricatorEdgeGraph.php',
+ 'PhabricatorEdgeObject' => 'infrastructure/edges/conduit/PhabricatorEdgeObject.php',
+ 'PhabricatorEdgeObjectQuery' => 'infrastructure/edges/query/PhabricatorEdgeObjectQuery.php',
'PhabricatorEdgeQuery' => 'infrastructure/edges/query/PhabricatorEdgeQuery.php',
'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php',
'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php',
@@ -5886,6 +5889,7 @@
'DrydockWebrootInterface' => 'DrydockInterface',
'DrydockWorker' => 'PhabricatorWorker',
'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation',
+ 'EdgeSearchConduitAPIMethod' => 'ConduitAPIMethod',
'FeedConduitAPIMethod' => 'ConduitAPIMethod',
'FeedPublishConduitAPIMethod' => 'FeedConduitAPIMethod',
'FeedPublisherHTTPWorker' => 'FeedPushWorker',
@@ -7652,6 +7656,11 @@
'PhabricatorEdgeEditType' => 'PhabricatorPHIDListEditType',
'PhabricatorEdgeEditor' => 'Phobject',
'PhabricatorEdgeGraph' => 'AbstractDirectedGraph',
+ 'PhabricatorEdgeObject' => array(
+ 'Phobject',
+ 'PhabricatorPolicyInterface',
+ ),
+ 'PhabricatorEdgeObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
'PhabricatorEdgeQuery' => 'PhabricatorQuery',
'PhabricatorEdgeTestCase' => 'PhabricatorTestCase',
'PhabricatorEdgeType' => 'Phobject',
diff --git a/src/applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php b/src/applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php
--- a/src/applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php
+++ b/src/applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php
@@ -24,4 +24,17 @@
$add_edges);
}
+ public function getConduitKey() {
+ return 'mentioned-in';
+ }
+
+ public function getConduitName() {
+ return pht('Mention In');
+ }
+
+ public function getConduitDescription() {
+ return pht(
+ 'The source object is mentioned in a comment on the destination object.');
+ }
+
}
diff --git a/src/applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php b/src/applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php
--- a/src/applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php
+++ b/src/applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php
@@ -13,4 +13,17 @@
return true;
}
+ public function getConduitKey() {
+ return 'mention';
+ }
+
+ public function getConduitName() {
+ return pht('Mention');
+ }
+
+ public function getConduitDescription() {
+ return pht(
+ 'The source object has a comment which mentions the destination object.');
+ }
+
}
diff --git a/src/infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php b/src/infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php
@@ -0,0 +1,173 @@
+<?php
+
+final class EdgeSearchConduitAPIMethod
+ extends ConduitAPIMethod {
+
+ public function getAPIMethodName() {
+ return 'edge.search';
+ }
+
+ public function getMethodDescription() {
+ return pht('Read edge relationships between objects.');
+ }
+
+ public function getMethodDocumentation() {
+ $viewer = $this->getViewer();
+
+ $rows = array();
+ foreach ($this->getConduitEdgeTypeMap() as $key => $type) {
+ $inverse_constant = $type->getInverseEdgeConstant();
+ if ($inverse_constant) {
+ $inverse_type = PhabricatorEdgeType::getByConstant($inverse_constant);
+ $inverse = $inverse_type->getConduitKey();
+ } else {
+ $inverse = null;
+ }
+
+ $rows[] = array(
+ $key,
+ $type->getConduitName(),
+ $inverse,
+ new PHUIRemarkupView($viewer, $type->getConduitDescription()),
+ );
+ }
+
+ $types_table = id(new AphrontTableView($rows))
+ ->setHeaders(
+ array(
+ pht('Constant'),
+ pht('Name'),
+ pht('Inverse'),
+ pht('Description'),
+ ))
+ ->setColumnClasses(
+ array(
+ 'mono',
+ 'pri',
+ 'mono',
+ 'wide',
+ ));
+
+ return id(new PHUIObjectBoxView())
+ ->setHeaderText(pht('Edge Types'))
+ ->setTable($types_table);
+ }
+
+ public function getMethodStatus() {
+ return self::METHOD_STATUS_UNSTABLE;
+ }
+
+ public function getMethodStatusDescription() {
+ return pht('This method is new and experimental.');
+ }
+
+ protected function defineParamTypes() {
+ return array(
+ 'sourcePHIDs' => 'list<phid>',
+ 'types' => 'list<const>',
+ 'destinationPHIDs' => 'optional list<phid>',
+ ) + $this->getPagerParamTypes();
+ }
+
+ protected function defineReturnType() {
+ return 'list<dict>';
+ }
+
+ protected function defineErrorTypes() {
+ return array();
+ }
+
+ protected function execute(ConduitAPIRequest $request) {
+ $viewer = $request->getUser();
+ $pager = $this->newPager($request);
+
+ $source_phids = $request->getValue('sourcePHIDs', array());
+ $edge_types = $request->getValue('types', array());
+ $destination_phids = $request->getValue('destinationPHIDs', array());
+
+ $object_query = id(new PhabricatorObjectQuery())
+ ->setViewer($viewer)
+ ->withNames($source_phids);
+
+ $object_query->execute();
+ $objects = $object_query->getNamedResults();
+ foreach ($source_phids as $phid) {
+ if (empty($objects[$phid])) {
+ throw new Exception(
+ pht(
+ 'Source PHID "%s" does not identify a valid object, or you do '.
+ 'not have permission to view it.',
+ $phid));
+ }
+ }
+ $source_phids = mpull($objects, 'getPHID');
+
+ if (!$edge_types) {
+ throw new Exception(
+ pht(
+ 'Edge search must specify a nonempty list of edge types.'));
+ }
+
+ $edge_map = $this->getConduitEdgeTypeMap();
+
+ $constant_map = array();
+ $edge_constants = array();
+ foreach ($edge_types as $edge_type) {
+ if (!isset($edge_map[$edge_type])) {
+ throw new Exception(
+ pht(
+ 'Edge type "%s" is not a recognized edge type.',
+ $edge_type));
+ }
+
+ $constant = $edge_map[$edge_type]->getEdgeConstant();
+
+ $edge_constants[] = $constant;
+ $constant_map[$constant] = $edge_type;
+ }
+
+ $edge_query = id(new PhabricatorEdgeObjectQuery())
+ ->setViewer($viewer)
+ ->withSourcePHIDs($source_phids)
+ ->withEdgeTypes($edge_constants);
+
+ if ($destination_phids) {
+ $edge_query->withDestinationPHIDs($destination_phids);
+ }
+
+ $edge_objects = $edge_query->executeWithCursorPager($pager);
+
+ $edges = array();
+ foreach ($edge_objects as $edge_object) {
+ $edges[] = array(
+ 'sourcePHID' => $edge_object->getSourcePHID(),
+ 'edgeType' => $constant_map[$edge_object->getEdgeType()],
+ 'destinationPHID' => $edge_object->getDestinationPHID(),
+ );
+ }
+
+ $results = array(
+ 'data' => $edges,
+ );
+
+ return $this->addPagerResults($results, $pager);
+ }
+
+ private function getConduitEdgeTypeMap() {
+ $types = PhabricatorEdgeType::getAllTypes();
+
+ $map = array();
+ foreach ($types as $type) {
+ $key = $type->getConduitKey();
+ if ($key === null) {
+ continue;
+ }
+
+ $map[$key] = $type;
+ }
+
+ ksort($map);
+
+ return $map;
+ }
+}
diff --git a/src/infrastructure/edges/conduit/PhabricatorEdgeObject.php b/src/infrastructure/edges/conduit/PhabricatorEdgeObject.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/edges/conduit/PhabricatorEdgeObject.php
@@ -0,0 +1,63 @@
+<?php
+
+final class PhabricatorEdgeObject
+ extends Phobject
+ implements PhabricatorPolicyInterface {
+
+ private $id;
+ private $src;
+ private $dst;
+ private $type;
+
+ public static function newFromRow(array $row) {
+ $edge = new self();
+
+ $edge->id = $row['id'];
+ $edge->src = $row['src'];
+ $edge->dst = $row['dst'];
+ $edge->type = $row['type'];
+
+ return $edge;
+ }
+
+ public function getID() {
+ return $this->id;
+ }
+
+ public function getSourcePHID() {
+ return $this->src;
+ }
+
+ public function getEdgeType() {
+ return $this->type;
+ }
+
+ public function getDestinationPHID() {
+ return $this->dst;
+ }
+
+ public function getPHID() {
+ return null;
+ }
+
+/* -( PhabricatorPolicyInterface )----------------------------------------- */
+
+
+ public function getCapabilities() {
+ return array(
+ PhabricatorPolicyCapability::CAN_VIEW,
+ );
+ }
+
+ public function getPolicy($capability) {
+ switch ($capability) {
+ case PhabricatorPolicyCapability::CAN_VIEW:
+ return PhabricatorPolicies::getMostOpenPolicy();
+ }
+ }
+
+ public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
+ return false;
+ }
+
+}
diff --git a/src/infrastructure/edges/query/PhabricatorEdgeObjectQuery.php b/src/infrastructure/edges/query/PhabricatorEdgeObjectQuery.php
new file mode 100644
--- /dev/null
+++ b/src/infrastructure/edges/query/PhabricatorEdgeObjectQuery.php
@@ -0,0 +1,163 @@
+<?php
+
+/**
+ * This is a more formal version of @{class:PhabricatorEdgeQuery} that is used
+ * to expose edges to Conduit.
+ */
+final class PhabricatorEdgeObjectQuery
+ extends PhabricatorCursorPagedPolicyAwareQuery {
+
+ private $sourcePHIDs;
+ private $sourcePHIDType;
+ private $edgeTypes;
+ private $destinationPHIDs;
+
+
+ public function withSourcePHIDs(array $source_phids) {
+ $this->sourcePHIDs = $source_phids;
+ return $this;
+ }
+
+ public function withEdgeTypes(array $types) {
+ $this->edgeTypes = $types;
+ return $this;
+ }
+
+ public function withDestinationPHIDs(array $destination_phids) {
+ $this->destinationPHIDs = $destination_phids;
+ return $this;
+ }
+
+ protected function willExecute() {
+ $source_phids = $this->sourcePHIDs;
+
+ if (!$source_phids) {
+ throw new Exception(
+ pht(
+ 'Edge object query must be executed with a nonempty list of '.
+ 'source PHIDs.'));
+ }
+
+ $phid_item = null;
+ $phid_type = null;
+ foreach ($source_phids as $phid) {
+ $this_type = phid_get_type($phid);
+ if ($this_type == PhabricatorPHIDConstants::PHID_TYPE_UNKNOWN) {
+ throw new Exception(
+ pht(
+ 'Source PHID "%s" in edge object query has unknown PHID type.',
+ $phid));
+ }
+
+ if ($phid_type === null) {
+ $phid_type = $this_type;
+ $phid_item = $phid;
+ continue;
+ }
+
+ if ($phid_type !== $this_type) {
+ throw new Exception(
+ pht(
+ 'Two source PHIDs ("%s" and "%s") have different PHID types '.
+ '("%s" and "%s"). All PHIDs must be of the same type to execute '.
+ 'an edge object query.',
+ $phid_item,
+ $phid,
+ $phid_type,
+ $this_type));
+ }
+ }
+
+ $this->sourcePHIDType = $phid_type;
+ }
+
+ protected function loadPage() {
+ $type = $this->sourcePHIDType;
+ $conn = PhabricatorEdgeConfig::establishConnection($type, 'r');
+ $table = PhabricatorEdgeConfig::TABLE_NAME_EDGE;
+ $rows = $this->loadStandardPageRowsWithConnection($conn, $table);
+
+ $result = array();
+ foreach ($rows as $row) {
+ $result[] = PhabricatorEdgeObject::newFromRow($row);
+ }
+
+ return $result;
+ }
+
+ protected function buildSelectClauseParts(AphrontDatabaseConnection $conn) {
+ $parts = parent::buildSelectClauseParts($conn);
+
+ // TODO: This is hacky, because we don't have real IDs on this table.
+ $parts[] = qsprintf(
+ $conn,
+ 'CONCAT(dateCreated, %s, seq) AS id',
+ '_');
+
+ return $parts;
+ }
+
+ protected function buildWhereClauseParts(AphrontDatabaseConnection $conn) {
+ $parts = parent::buildWhereClauseParts($conn);
+
+ $parts[] = qsprintf(
+ $conn,
+ 'src IN (%Ls)',
+ $this->sourcePHIDs);
+
+ $parts[] = qsprintf(
+ $conn,
+ 'type IN (%Ls)',
+ $this->edgeTypes);
+
+ if ($this->destinationPHIDs !== null) {
+ $parts[] = qsprintf(
+ $conn,
+ 'dst IN (%Ls)',
+ $this->destinationPHIDs);
+ }
+
+ return $parts;
+ }
+
+ public function getQueryApplicationClass() {
+ return null;
+ }
+
+ protected function getPrimaryTableAlias() {
+ return 'edge';
+ }
+
+ public function getOrderableColumns() {
+ return array(
+ 'dateCreated' => array(
+ 'table' => 'edge',
+ 'column' => 'dateCreated',
+ 'type' => 'int',
+ ),
+ 'sequence' => array(
+ 'table' => 'edge',
+ 'column' => 'seq',
+ 'type' => 'int',
+
+ // TODO: This is not actually unique, but we're just doing our best
+ // here.
+ 'unique' => true,
+ ),
+ );
+ }
+
+ protected function getDefaultOrderVector() {
+ return array('dateCreated', 'sequence');
+ }
+
+ protected function getPagingValueMap($cursor, array $keys) {
+ $parts = explode('_', $cursor);
+
+ return array(
+ 'dateCreated' => $parts[0],
+ 'sequence' => $parts[1],
+ );
+ }
+
+}
diff --git a/src/infrastructure/edges/type/PhabricatorEdgeType.php b/src/infrastructure/edges/type/PhabricatorEdgeType.php
--- a/src/infrastructure/edges/type/PhabricatorEdgeType.php
+++ b/src/infrastructure/edges/type/PhabricatorEdgeType.php
@@ -27,6 +27,18 @@
return $const;
}
+ public function getConduitKey() {
+ return null;
+ }
+
+ public function getConduitName() {
+ return null;
+ }
+
+ public function getConduitDescription() {
+ return null;
+ }
+
public function getInverseEdgeConstant() {
return null;
}
diff --git a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
--- a/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
+++ b/src/infrastructure/query/policy/PhabricatorCursorPagedPolicyAwareQuery.php
@@ -85,12 +85,20 @@
protected function loadStandardPageRows(PhabricatorLiskDAO $table) {
$conn = $table->establishConnection('r');
+ return $this->loadStandardPageRowsWithConnection(
+ $conn,
+ $table->getTableName());
+ }
+
+ protected function loadStandardPageRowsWithConnection(
+ AphrontDatabaseConnection $conn,
+ $table_name) {
$rows = queryfx_all(
$conn,
'%Q FROM %T %Q %Q %Q %Q %Q %Q %Q',
$this->buildSelectClause($conn),
- $table->getTableName(),
+ $table_name,
(string)$this->getPrimaryTableAlias(),
$this->buildJoinClause($conn),
$this->buildWhereClause($conn),

File Metadata

Mime Type
text/plain
Expires
Wed, Feb 26, 1:03 AM (6 h, 39 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7204440
Default Alt Text
D17462.id41992.diff (15 KB)

Event Timeline