Skip to content

Commit be16f9b

Browse files
author
epriestley
committedMar 4, 2017
Add a generic "edge.search" method
Summary: Ref T12337. Ref T5873. This provides a generic "edge.search" method which feels like other "verison 3" `*.search` methods. The major issues here are: 1. Edges use constants internally, which aren't great for an API. 2. A lot of edges are internal and probably not useful to query. 3. Edges don't have a real "id", so paginating them properly is challenging. I've solved these things like this: - Edges must opt-in to being available via Conduit by providing a human-readable key (like "mention" instead of "52"). This solvs (1) and (2). - I faked a mostly-reasonable behavior for paginating. Test Plan: Ran various valid and invalid searches. Paginated a large search. Reviewed UI. {F3651818} Reviewers: chad Reviewed By: chad Maniphest Tasks: T12337, T5873 Differential Revision: https://secure.phabricator.com/D17462
1 parent 9ccef52 commit be16f9b

8 files changed

+455
-1
lines changed
 

‎src/__phutil_library_map__.php

+9
Original file line numberDiff line numberDiff line change
@@ -1077,6 +1077,7 @@
10771077
'DrydockWebrootInterface' => 'applications/drydock/interface/webroot/DrydockWebrootInterface.php',
10781078
'DrydockWorker' => 'applications/drydock/worker/DrydockWorker.php',
10791079
'DrydockWorkingCopyBlueprintImplementation' => 'applications/drydock/blueprint/DrydockWorkingCopyBlueprintImplementation.php',
1080+
'EdgeSearchConduitAPIMethod' => 'infrastructure/edges/conduit/EdgeSearchConduitAPIMethod.php',
10801081
'FeedConduitAPIMethod' => 'applications/feed/conduit/FeedConduitAPIMethod.php',
10811082
'FeedPublishConduitAPIMethod' => 'applications/feed/conduit/FeedPublishConduitAPIMethod.php',
10821083
'FeedPublisherHTTPWorker' => 'applications/feed/worker/FeedPublisherHTTPWorker.php',
@@ -2589,6 +2590,8 @@
25892590
'PhabricatorEdgeEditType' => 'applications/transactions/edittype/PhabricatorEdgeEditType.php',
25902591
'PhabricatorEdgeEditor' => 'infrastructure/edges/editor/PhabricatorEdgeEditor.php',
25912592
'PhabricatorEdgeGraph' => 'infrastructure/edges/util/PhabricatorEdgeGraph.php',
2593+
'PhabricatorEdgeObject' => 'infrastructure/edges/conduit/PhabricatorEdgeObject.php',
2594+
'PhabricatorEdgeObjectQuery' => 'infrastructure/edges/query/PhabricatorEdgeObjectQuery.php',
25922595
'PhabricatorEdgeQuery' => 'infrastructure/edges/query/PhabricatorEdgeQuery.php',
25932596
'PhabricatorEdgeTestCase' => 'infrastructure/edges/__tests__/PhabricatorEdgeTestCase.php',
25942597
'PhabricatorEdgeType' => 'infrastructure/edges/type/PhabricatorEdgeType.php',
@@ -5886,6 +5889,7 @@
58865889
'DrydockWebrootInterface' => 'DrydockInterface',
58875890
'DrydockWorker' => 'PhabricatorWorker',
58885891
'DrydockWorkingCopyBlueprintImplementation' => 'DrydockBlueprintImplementation',
5892+
'EdgeSearchConduitAPIMethod' => 'ConduitAPIMethod',
58895893
'FeedConduitAPIMethod' => 'ConduitAPIMethod',
58905894
'FeedPublishConduitAPIMethod' => 'FeedConduitAPIMethod',
58915895
'FeedPublisherHTTPWorker' => 'FeedPushWorker',
@@ -7652,6 +7656,11 @@
76527656
'PhabricatorEdgeEditType' => 'PhabricatorPHIDListEditType',
76537657
'PhabricatorEdgeEditor' => 'Phobject',
76547658
'PhabricatorEdgeGraph' => 'AbstractDirectedGraph',
7659+
'PhabricatorEdgeObject' => array(
7660+
'Phobject',
7661+
'PhabricatorPolicyInterface',
7662+
),
7663+
'PhabricatorEdgeObjectQuery' => 'PhabricatorCursorPagedPolicyAwareQuery',
76557664
'PhabricatorEdgeQuery' => 'PhabricatorQuery',
76567665
'PhabricatorEdgeTestCase' => 'PhabricatorTestCase',
76577666
'PhabricatorEdgeType' => 'Phobject',

‎src/applications/transactions/edges/PhabricatorObjectMentionedByObjectEdgeType.php

+13
Original file line numberDiff line numberDiff line change
@@ -24,4 +24,17 @@ public function getTransactionAddString(
2424
$add_edges);
2525
}
2626

27+
public function getConduitKey() {
28+
return 'mentioned-in';
29+
}
30+
31+
public function getConduitName() {
32+
return pht('Mention In');
33+
}
34+
35+
public function getConduitDescription() {
36+
return pht(
37+
'The source object is mentioned in a comment on the destination object.');
38+
}
39+
2740
}

‎src/applications/transactions/edges/PhabricatorObjectMentionsObjectEdgeType.php

+13
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,17 @@ public function shouldWriteInverseTransactions() {
1313
return true;
1414
}
1515

16+
public function getConduitKey() {
17+
return 'mention';
18+
}
19+
20+
public function getConduitName() {
21+
return pht('Mention');
22+
}
23+
24+
public function getConduitDescription() {
25+
return pht(
26+
'The source object has a comment which mentions the destination object.');
27+
}
28+
1629
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,173 @@
1+
<?php
2+
3+
final class EdgeSearchConduitAPIMethod
4+
extends ConduitAPIMethod {
5+
6+
public function getAPIMethodName() {
7+
return 'edge.search';
8+
}
9+
10+
public function getMethodDescription() {
11+
return pht('Read edge relationships between objects.');
12+
}
13+
14+
public function getMethodDocumentation() {
15+
$viewer = $this->getViewer();
16+
17+
$rows = array();
18+
foreach ($this->getConduitEdgeTypeMap() as $key => $type) {
19+
$inverse_constant = $type->getInverseEdgeConstant();
20+
if ($inverse_constant) {
21+
$inverse_type = PhabricatorEdgeType::getByConstant($inverse_constant);
22+
$inverse = $inverse_type->getConduitKey();
23+
} else {
24+
$inverse = null;
25+
}
26+
27+
$rows[] = array(
28+
$key,
29+
$type->getConduitName(),
30+
$inverse,
31+
new PHUIRemarkupView($viewer, $type->getConduitDescription()),
32+
);
33+
}
34+
35+
$types_table = id(new AphrontTableView($rows))
36+
->setHeaders(
37+
array(
38+
pht('Constant'),
39+
pht('Name'),
40+
pht('Inverse'),
41+
pht('Description'),
42+
))
43+
->setColumnClasses(
44+
array(
45+
'mono',
46+
'pri',
47+
'mono',
48+
'wide',
49+
));
50+
51+
return id(new PHUIObjectBoxView())
52+
->setHeaderText(pht('Edge Types'))
53+
->setTable($types_table);
54+
}
55+
56+
public function getMethodStatus() {
57+
return self::METHOD_STATUS_UNSTABLE;
58+
}
59+
60+
public function getMethodStatusDescription() {
61+
return pht('This method is new and experimental.');
62+
}
63+
64+
protected function defineParamTypes() {
65+
return array(
66+
'sourcePHIDs' => 'list<phid>',
67+
'types' => 'list<const>',
68+
'destinationPHIDs' => 'optional list<phid>',
69+
) + $this->getPagerParamTypes();
70+
}
71+
72+
protected function defineReturnType() {
73+
return 'list<dict>';
74+
}
75+
76+
protected function defineErrorTypes() {
77+
return array();
78+
}
79+
80+
protected function execute(ConduitAPIRequest $request) {
81+
$viewer = $request->getUser();
82+
$pager = $this->newPager($request);
83+
84+
$source_phids = $request->getValue('sourcePHIDs', array());
85+
$edge_types = $request->getValue('types', array());
86+
$destination_phids = $request->getValue('destinationPHIDs', array());
87+
88+
$object_query = id(new PhabricatorObjectQuery())
89+
->setViewer($viewer)
90+
->withNames($source_phids);
91+
92+
$object_query->execute();
93+
$objects = $object_query->getNamedResults();
94+
foreach ($source_phids as $phid) {
95+
if (empty($objects[$phid])) {
96+
throw new Exception(
97+
pht(
98+
'Source PHID "%s" does not identify a valid object, or you do '.
99+
'not have permission to view it.',
100+
$phid));
101+
}
102+
}
103+
$source_phids = mpull($objects, 'getPHID');
104+
105+
if (!$edge_types) {
106+
throw new Exception(
107+
pht(
108+
'Edge search must specify a nonempty list of edge types.'));
109+
}
110+
111+
$edge_map = $this->getConduitEdgeTypeMap();
112+
113+
$constant_map = array();
114+
$edge_constants = array();
115+
foreach ($edge_types as $edge_type) {
116+
if (!isset($edge_map[$edge_type])) {
117+
throw new Exception(
118+
pht(
119+
'Edge type "%s" is not a recognized edge type.',
120+
$edge_type));
121+
}
122+
123+
$constant = $edge_map[$edge_type]->getEdgeConstant();
124+
125+
$edge_constants[] = $constant;
126+
$constant_map[$constant] = $edge_type;
127+
}
128+
129+
$edge_query = id(new PhabricatorEdgeObjectQuery())
130+
->setViewer($viewer)
131+
->withSourcePHIDs($source_phids)
132+
->withEdgeTypes($edge_constants);
133+
134+
if ($destination_phids) {
135+
$edge_query->withDestinationPHIDs($destination_phids);
136+
}
137+
138+
$edge_objects = $edge_query->executeWithCursorPager($pager);
139+
140+
$edges = array();
141+
foreach ($edge_objects as $edge_object) {
142+
$edges[] = array(
143+
'sourcePHID' => $edge_object->getSourcePHID(),
144+
'edgeType' => $constant_map[$edge_object->getEdgeType()],
145+
'destinationPHID' => $edge_object->getDestinationPHID(),
146+
);
147+
}
148+
149+
$results = array(
150+
'data' => $edges,
151+
);
152+
153+
return $this->addPagerResults($results, $pager);
154+
}
155+
156+
private function getConduitEdgeTypeMap() {
157+
$types = PhabricatorEdgeType::getAllTypes();
158+
159+
$map = array();
160+
foreach ($types as $type) {
161+
$key = $type->getConduitKey();
162+
if ($key === null) {
163+
continue;
164+
}
165+
166+
$map[$key] = $type;
167+
}
168+
169+
ksort($map);
170+
171+
return $map;
172+
}
173+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
<?php
2+
3+
final class PhabricatorEdgeObject
4+
extends Phobject
5+
implements PhabricatorPolicyInterface {
6+
7+
private $id;
8+
private $src;
9+
private $dst;
10+
private $type;
11+
12+
public static function newFromRow(array $row) {
13+
$edge = new self();
14+
15+
$edge->id = $row['id'];
16+
$edge->src = $row['src'];
17+
$edge->dst = $row['dst'];
18+
$edge->type = $row['type'];
19+
20+
return $edge;
21+
}
22+
23+
public function getID() {
24+
return $this->id;
25+
}
26+
27+
public function getSourcePHID() {
28+
return $this->src;
29+
}
30+
31+
public function getEdgeType() {
32+
return $this->type;
33+
}
34+
35+
public function getDestinationPHID() {
36+
return $this->dst;
37+
}
38+
39+
public function getPHID() {
40+
return null;
41+
}
42+
43+
/* -( PhabricatorPolicyInterface )----------------------------------------- */
44+
45+
46+
public function getCapabilities() {
47+
return array(
48+
PhabricatorPolicyCapability::CAN_VIEW,
49+
);
50+
}
51+
52+
public function getPolicy($capability) {
53+
switch ($capability) {
54+
case PhabricatorPolicyCapability::CAN_VIEW:
55+
return PhabricatorPolicies::getMostOpenPolicy();
56+
}
57+
}
58+
59+
public function hasAutomaticCapability($capability, PhabricatorUser $viewer) {
60+
return false;
61+
}
62+
63+
}

0 commit comments

Comments
 (0)
Failed to load comments.