Correct a parameter order swap in "diffusion.historyquery" for Mercurial
[phabricator.git] / src / applications / search / engine / PhabricatorSearchEngineAPIMethod.php
blobe9607f95394290032cbe20f37bf481dbf1b16c5b
1 <?php
3 abstract class PhabricatorSearchEngineAPIMethod
4 extends ConduitAPIMethod {
6 abstract public function newSearchEngine();
8 final public function getQueryMaps($query) {
9 $maps = $this->getCustomQueryMaps($query);
11 // Make sure we emit empty maps as objects, not lists.
12 foreach ($maps as $key => $map) {
13 if (!$map) {
14 $maps[$key] = (object)$map;
18 if (!$maps) {
19 $maps = (object)$maps;
22 return $maps;
25 protected function getCustomQueryMaps($query) {
26 return array();
29 public function getApplication() {
30 $engine = $this->newSearchEngine();
31 $class = $engine->getApplicationClassName();
32 return PhabricatorApplication::getByClass($class);
35 final protected function defineParamTypes() {
36 return array(
37 'queryKey' => 'optional string',
38 'constraints' => 'optional map<string, wild>',
39 'attachments' => 'optional map<string, bool>',
40 'order' => 'optional order',
41 ) + $this->getPagerParamTypes();
44 final protected function defineReturnType() {
45 return 'map<string, wild>';
48 final protected function execute(ConduitAPIRequest $request) {
49 $engine = $this->newSearchEngine()
50 ->setViewer($request->getUser());
52 return $engine->buildConduitResponse($request, $this);
55 final public function getMethodDescription() {
56 return pht(
57 'This is a standard **ApplicationSearch** method which will let you '.
58 'list, query, or search for objects. For documentation on these '.
59 'endpoints, see **[[ %s | Conduit API: Using Search Endpoints ]]**.',
60 PhabricatorEnv::getDoclink('Conduit API: Using Search Endpoints'));
63 final protected function newDocumentationPages(PhabricatorUser $viewer) {
64 $viewer = $this->getViewer();
66 $engine = $this->newSearchEngine()
67 ->setViewer($viewer);
69 $query = $engine->newQuery();
71 $out = array();
73 $out[] = $this->buildQueriesDocumentationPage($viewer, $engine);
74 $out[] = $this->buildConstraintsDocumentationPage($viewer, $engine);
75 $out[] = $this->buildOrderDocumentationPage($viewer, $engine, $query);
76 $out[] = $this->buildFieldsDocumentationPage($viewer, $engine);
77 $out[] = $this->buildAttachmentsDocumentationPage($viewer, $engine);
78 $out[] = $this->buildPagingDocumentationPage($viewer, $engine);
80 return $out;
83 private function buildQueriesDocumentationPage(
84 PhabricatorUser $viewer,
85 PhabricatorApplicationSearchEngine $engine) {
86 $viewer = $this->getViewer();
88 $info = pht(<<<EOTEXT
89 You can choose a builtin or saved query as a starting point for filtering
90 results by selecting it with `queryKey`. If you don't specify a `queryKey`,
91 the query will start with no constraints.
93 For example, many applications have builtin queries like `"active"` or
94 `"open"` to find only active or enabled results. To use a `queryKey`, specify
95 it like this:
97 ```lang=json, name="Selecting a Builtin Query"
99 ...
100 "queryKey": "active",
105 The table below shows the keys to use to select builtin queries and your
106 saved queries, but you can also use **any** query you run via the web UI as a
107 starting point. You can find the key for a query by examining the URI after
108 running a normal search.
110 You can use these keys to select builtin queries and your configured saved
111 queries:
112 EOTEXT
115 $named_queries = $engine->loadAllNamedQueries();
117 $rows = array();
118 foreach ($named_queries as $named_query) {
119 $builtin = $named_query->getIsBuiltin()
120 ? pht('Builtin')
121 : pht('Custom');
123 $rows[] = array(
124 $named_query->getQueryKey(),
125 $named_query->getQueryName(),
126 $builtin,
130 $table = id(new AphrontTableView($rows))
131 ->setHeaders(
132 array(
133 pht('Query Key'),
134 pht('Name'),
135 pht('Builtin'),
137 ->setColumnClasses(
138 array(
139 'prewrap',
140 'pri wide',
141 null,
144 $title = pht('Prebuilt Queries');
145 $content = array(
146 $this->newRemarkupDocumentationView($info),
147 $table,
150 return $this->newDocumentationBoxPage($viewer, $title, $content)
151 ->setAnchor('queries');
154 private function buildConstraintsDocumentationPage(
155 PhabricatorUser $viewer,
156 PhabricatorApplicationSearchEngine $engine) {
158 $info = pht(<<<EOTEXT
159 You can apply custom constraints by passing a dictionary in `constraints`.
160 This will let you search for specific sets of results (for example, you may
161 want show only results with a certain state, status, or owner).
164 If you specify both a `queryKey` and `constraints`, the builtin or saved query
165 will be applied first as a starting point, then any additional values in
166 `constraints` will be applied, overwriting the defaults from the original query.
168 Different endpoints support different constraints. The constraints this method
169 supports are detailed below. As an example, you might specify constraints like
170 this:
172 ```lang=json, name="Example Custom Constraints"
175 "constraints": {
176 "authorPHIDs": ["PHID-USER-1111", "PHID-USER-2222"],
177 "flavors": ["cherry", "orange"],
184 This API endpoint supports these constraints:
185 EOTEXT
188 $fields = $engine->getSearchFieldsForConduit();
190 // As a convenience, put these fields at the very top, even if the engine
191 // specifies and alternate display order for the web UI. These fields are
192 // very important in the API and nearly useless in the web UI.
193 $fields = array_select_keys(
194 $fields,
195 array('ids', 'phids')) + $fields;
197 $constant_lists = array();
199 $rows = array();
200 foreach ($fields as $field) {
201 $key = $field->getConduitKey();
202 $label = $field->getLabel();
204 $constants = $field->newConduitConstants();
205 $show_table = false;
207 $type_object = $field->getConduitParameterType();
208 if ($type_object) {
209 $type = $type_object->getTypeName();
210 $description = $field->getDescription();
211 if ($constants) {
212 $description = array(
213 $description,
214 ' ',
215 phutil_tag('em', array(), pht('(See table below.)')),
217 $show_table = true;
219 } else {
220 $type = null;
221 $description = phutil_tag('em', array(), pht('Not supported.'));
224 $rows[] = array(
225 $key,
226 $label,
227 $type,
228 $description,
231 if ($show_table) {
232 $constant_lists[] = $this->newRemarkupDocumentationView(
233 pht(
234 'Constants supported by the `%s` constraint:',
235 $key));
237 $constants_rows = array();
238 foreach ($constants as $constant) {
239 if ($constant->getIsDeprecated()) {
240 $icon = id(new PHUIIconView())
241 ->setIcon('fa-exclamation-triangle', 'red');
242 } else {
243 $icon = null;
246 $constants_rows[] = array(
247 $constant->getKey(),
248 array(
249 $icon,
250 ' ',
251 $constant->getValue(),
256 $constants_table = id(new AphrontTableView($constants_rows))
257 ->setHeaders(
258 array(
259 pht('Key'),
260 pht('Value'),
262 ->setColumnClasses(
263 array(
264 'mono',
265 'wide',
268 $constant_lists[] = $constants_table;
272 $table = id(new AphrontTableView($rows))
273 ->setHeaders(
274 array(
275 pht('Key'),
276 pht('Label'),
277 pht('Type'),
278 pht('Description'),
280 ->setColumnClasses(
281 array(
282 'prewrap',
283 'pri',
284 'prewrap',
285 'wide',
289 $title = pht('Constraints');
290 $content = array(
291 $this->newRemarkupDocumentationView($info),
292 $table,
293 $constant_lists,
296 return $this->newDocumentationBoxPage($viewer, $title, $content)
297 ->setAnchor('constraints')
298 ->setIconIcon('fa-filter');
301 private function buildOrderDocumentationPage(
302 PhabricatorUser $viewer,
303 PhabricatorApplicationSearchEngine $engine,
304 $query) {
306 $orders_info = pht(<<<EOTEXT
307 Use `order` to choose an ordering for the results.
309 Either specify a single key from the builtin orders (these are a set of
310 meaningful, high-level, human-readable orders) or specify a custom list of
311 low-level columns.
313 To use a high-level order, choose a builtin order from the table below
314 and specify it like this:
316 ```lang=json, name="Choosing a Result Order"
319 "order": "newest",
324 These builtin orders are available:
325 EOTEXT
328 $orders = $query->getBuiltinOrders();
330 $rows = array();
331 foreach ($orders as $key => $order) {
332 $rows[] = array(
333 $key,
334 $order['name'],
335 implode(', ', $order['vector']),
339 $orders_table = id(new AphrontTableView($rows))
340 ->setHeaders(
341 array(
342 pht('Key'),
343 pht('Description'),
344 pht('Columns'),
346 ->setColumnClasses(
347 array(
348 'pri',
350 'wide',
353 $columns_info = pht(<<<EOTEXT
354 You can choose a low-level column order instead. To do this, provide a list
355 of columns instead of a single key. This is an advanced feature.
357 In a custom column order:
359 - each column may only be specified once;
360 - each column may be prefixed with `-` to invert the order;
361 - the last column must be a unique column, usually `id`; and
362 - no column other than the last may be unique.
364 To use a low-level order, choose a sequence of columns and specify them like
365 this:
367 ```lang=json, name="Using a Custom Order"
370 "order": ["color", "-name", "id"],
375 These low-level columns are available:
376 EOTEXT
379 $columns = $query->getOrderableColumns();
380 $rows = array();
381 foreach ($columns as $key => $column) {
382 $rows[] = array(
383 $key,
384 idx($column, 'unique') ? pht('Yes') : pht('No'),
388 $columns_table = id(new AphrontTableView($rows))
389 ->setHeaders(
390 array(
391 pht('Key'),
392 pht('Unique'),
394 ->setColumnClasses(
395 array(
396 'pri',
397 'wide',
400 $title = pht('Result Ordering');
401 $content = array(
402 $this->newRemarkupDocumentationView($orders_info),
403 $orders_table,
404 $this->newRemarkupDocumentationView($columns_info),
405 $columns_table,
408 return $this->newDocumentationBoxPage($viewer, $title, $content)
409 ->setAnchor('ordering')
410 ->setIconIcon('fa-sort-numeric-asc');
413 private function buildFieldsDocumentationPage(
414 PhabricatorUser $viewer,
415 PhabricatorApplicationSearchEngine $engine) {
417 $info = pht(<<<EOTEXT
418 Objects matching your query are returned as a list of dictionaries in the
419 `data` property of the results. Each dictionary has some metadata and a
420 `fields` key, which contains the information about the object that most callers
421 will be interested in.
423 For example, the results may look something like this:
425 ```lang=json, name="Example Results"
428 "data": [
430 "id": 123,
431 "phid": "PHID-WXYZ-1111",
432 "fields": {
433 "name": "First Example Object",
434 "authorPHID": "PHID-USER-2222"
438 "id": 124,
439 "phid": "PHID-WXYZ-3333",
440 "fields": {
441 "name": "Second Example Object",
442 "authorPHID": "PHID-USER-4444"
451 This result structure is standardized across all search methods, but the
452 available fields differ from application to application.
454 These are the fields available on this object type:
455 EOTEXT
458 $specs = $engine->getAllConduitFieldSpecifications();
460 $rows = array();
461 foreach ($specs as $key => $spec) {
462 $type = $spec->getType();
463 $description = $spec->getDescription();
464 $rows[] = array(
465 $key,
466 $type,
467 $description,
471 $table = id(new AphrontTableView($rows))
472 ->setHeaders(
473 array(
474 pht('Key'),
475 pht('Type'),
476 pht('Description'),
478 ->setColumnClasses(
479 array(
480 'pri',
481 'mono',
482 'wide',
485 $title = pht('Object Fields');
486 $content = array(
487 $this->newRemarkupDocumentationView($info),
488 $table,
491 return $this->newDocumentationBoxPage($viewer, $title, $content)
492 ->setAnchor('fields')
493 ->setIconIcon('fa-cube');
496 private function buildAttachmentsDocumentationPage(
497 PhabricatorUser $viewer,
498 PhabricatorApplicationSearchEngine $engine) {
500 $info = pht(<<<EOTEXT
501 By default, only basic information about objects is returned. If you want
502 more extensive information, you can use available `attachments` to get more
503 information in the results (like subscribers and projects).
505 Generally, requesting more information means the query executes more slowly
506 and returns more data (in some cases, much more data). You should normally
507 request only the data you need.
509 To request extra data, specify which attachments you want in the `attachments`
510 parameter:
512 ```lang=json, name="Example Attachments Request"
515 "attachments": {
516 "subscribers": true
522 This example specifies that results should include information about
523 subscribers. In the return value, each object will now have this information
524 filled out in the corresponding `attachments` value:
526 ```lang=json, name="Example Attachments Result"
529 "data": [
532 "attachments": {
533 "subscribers": {
534 "subscriberPHIDs": [
535 "PHID-WXYZ-2222",
537 "subscriberCount": 1,
538 "viewerIsSubscribed": false
549 These attachments are available:
550 EOTEXT
553 $attachments = $engine->getConduitSearchAttachments();
555 $rows = array();
556 foreach ($attachments as $key => $attachment) {
557 $rows[] = array(
558 $key,
559 $attachment->getAttachmentName(),
560 $attachment->getAttachmentDescription(),
564 $table = id(new AphrontTableView($rows))
565 ->setNoDataString(pht('This call does not support any attachments.'))
566 ->setHeaders(
567 array(
568 pht('Key'),
569 pht('Name'),
570 pht('Description'),
572 ->setColumnClasses(
573 array(
574 'prewrap',
575 'pri',
576 'wide',
579 $title = pht('Attachments');
580 $content = array(
581 $this->newRemarkupDocumentationView($info),
582 $table,
585 return $this->newDocumentationBoxPage($viewer, $title, $content)
586 ->setAnchor('attachments')
587 ->setIconIcon('fa-cubes');
590 private function buildPagingDocumentationPage(
591 PhabricatorUser $viewer,
592 PhabricatorApplicationSearchEngine $engine) {
594 $info = pht(<<<EOTEXT
595 Queries are limited to returning 100 results at a time. If you want fewer
596 results than this, you can use `limit` to specify a smaller limit.
598 If you want more results, you'll need to make additional queries to retrieve
599 more pages of results.
601 The result structure contains a `cursor` key with information you'll need in
602 order to fetch the next page of results. After an initial query, it will
603 usually look something like this:
605 ```lang=json, name="Example Cursor Result"
608 "cursor": {
609 "limit": 100,
610 "after": "1234",
611 "before": null,
612 "order": null
618 The `limit` and `order` fields are describing the effective limit and order the
619 query was executed with, and are usually not of much interest. The `after` and
620 `before` fields give you cursors which you can pass when making another API
621 call in order to get the next (or previous) page of results.
623 To get the next page of results, repeat your API call with all the same
624 parameters as the original call, but pass the `after` cursor you received from
625 the first call in the `after` parameter when making the second call.
627 If you do things correctly, you should get the second page of results, and
628 a cursor structure like this:
630 ```lang=json, name="Second Result Page"
633 "cursor": {
634 "limit": 5,
635 "after": "4567",
636 "before": "7890",
637 "order": null
643 You can now continue to the third page of results by passing the new `after`
644 cursor to the `after` parameter in your third call, or return to the previous
645 page of results by passing the `before` cursor to the `before` parameter. This
646 might be useful if you are rendering a web UI for a user and want to provide
647 "Next Page" and "Previous Page" links.
649 If `after` is `null`, there is no next page of results available. Likewise,
650 if `before` is `null`, there are no previous results available.
651 EOTEXT
654 $title = pht('Paging and Limits');
655 $content = array(
656 $this->newRemarkupDocumentationView($info),
659 return $this->newDocumentationBoxPage($viewer, $title, $content)
660 ->setAnchor('paging')
661 ->setIconIcon('fa-clone');