Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F15246848
D17462.id41999.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Award Token
Flag For Later
Size
15 KB
Referenced Files
None
Subscribers
None
D17462.id41999.diff
View Options
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
Details
Attached
Mime Type
text/plain
Expires
Wed, Feb 26, 2:11 AM (7 h, 37 m)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
7204440
Default Alt Text
D17462.id41999.diff (15 KB)
Attached To
Mode
D17462: Add a generic "edge.search" method
Attached
Detach File
Event Timeline
Log In to Comment