diff --git a/src/applications/conduit/application/PhabricatorConduitApplication.php b/src/applications/conduit/application/PhabricatorConduitApplication.php index 2632648109..7a0ca0e4b9 100644 --- a/src/applications/conduit/application/PhabricatorConduitApplication.php +++ b/src/applications/conduit/application/PhabricatorConduitApplication.php @@ -1,65 +1,65 @@ pht('Conduit Technical Documentation'), - 'href' => PhabricatorEnv::getDoclink('Conduit Technical Documentation'), + 'name' => pht('Conduit API Overview'), + 'href' => PhabricatorEnv::getDoclink('Conduit API Overview'), ), ); } public function getName() { return pht('Conduit'); } public function getShortDescription() { return pht('Developer API'); } public function getTitleGlyph() { return "\xE2\x87\xB5"; } public function getApplicationGroup() { return self::GROUP_DEVELOPER; } public function getApplicationOrder() { return 0.100; } public function getRoutes() { return array( '/conduit/' => array( '(?:query/(?P[^/]+)/)?' => 'PhabricatorConduitListController', 'method/(?P[^/]+)/' => 'PhabricatorConduitConsoleController', 'log/(?:query/(?P[^/]+)/)?' => 'PhabricatorConduitLogController', 'log/view/(?P[^/]+)/' => 'PhabricatorConduitLogController', 'token/' => 'PhabricatorConduitTokenController', 'token/edit/(?:(?P\d+)/)?' => 'PhabricatorConduitTokenEditController', 'token/terminate/(?:(?P\d+)/)?' => 'PhabricatorConduitTokenTerminateController', 'login/' => 'PhabricatorConduitTokenHandshakeController', ), '/api/(?P[^/]+)' => 'PhabricatorConduitAPIController', ); } } diff --git a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php index effe57e47d..a299d90018 100644 --- a/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php +++ b/src/applications/search/engine/PhabricatorSearchEngineAPIMethod.php @@ -1,572 +1,574 @@ newSearchEngine(); $class = $engine->getApplicationClassName(); return PhabricatorApplication::getByClass($class); } public function getMethodStatus() { return self::METHOD_STATUS_UNSTABLE; } public function getMethodStatusDescription() { return pht('ApplicationSearch methods are highly unstable.'); } final protected function defineParamTypes() { return array( 'queryKey' => 'optional string', 'constraints' => 'optional map', 'attachments' => 'optional map', 'order' => 'optional order', ) + $this->getPagerParamTypes(); } final protected function defineReturnType() { return 'map'; } final protected function execute(ConduitAPIRequest $request) { $engine = $this->newSearchEngine() ->setViewer($request->getUser()); return $engine->buildConduitResponse($request); } final public function getMethodDescription() { return pht( 'This is a standard **ApplicationSearch** method which will let you '. - 'list, query, or search for objects.'); + 'list, query, or search for objects. For documentation on these '. + 'endpoints, see **[[ %s | Conduit API: Using Search Endpoints ]]**.', + PhabricatorEnv::getDoclink('Conduit API: Using Edit Endpoints')); } final public function getMethodDocumentation() { $viewer = $this->getViewer(); $engine = $this->newSearchEngine() ->setViewer($viewer); $query = $engine->newQuery(); $out = array(); $out[] = $this->buildQueriesBox($engine); $out[] = $this->buildConstraintsBox($engine); $out[] = $this->buildOrderBox($engine, $query); $out[] = $this->buildFieldsBox($engine); $out[] = $this->buildAttachmentsBox($engine); $out[] = $this->buildPagingBox($engine); return $out; } private function buildQueriesBox( PhabricatorApplicationSearchEngine $engine) { $viewer = $this->getViewer(); $info = pht(<<loadAllNamedQueries(); $rows = array(); foreach ($named_queries as $named_query) { $builtin = $named_query->getIsBuiltin() ? pht('Builtin') : pht('Custom'); $rows[] = array( $named_query->getQueryKey(), $named_query->getQueryName(), $builtin, ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Query Key'), pht('Name'), pht('Builtin'), )) ->setColumnClasses( array( 'prewrap', 'pri wide', null, )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Builtin and Saved Queries')) ->setCollapsed(true) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } private function buildConstraintsBox( PhabricatorApplicationSearchEngine $engine) { $info = pht(<<getSearchFieldsForConduit(); // As a convenience, put these fields at the very top, even if the engine // specifies and alternate display order for the web UI. These fields are // very important in the API and nearly useless in the web UI. $fields = array_select_keys( $fields, array('ids', 'phids')) + $fields; $rows = array(); foreach ($fields as $field) { $key = $field->getConduitKey(); $label = $field->getLabel(); $type_object = $field->getConduitParameterType(); if ($type_object) { $type = $type_object->getTypeName(); $description = $field->getDescription(); } else { $type = null; $description = phutil_tag('em', array(), pht('Not supported.')); } $rows[] = array( $key, $label, $type, $description, ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Label'), pht('Type'), pht('Description'), )) ->setColumnClasses( array( 'prewrap', 'pri', 'prewrap', 'wide', )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Custom Query Constraints')) ->setCollapsed(true) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } private function buildOrderBox( PhabricatorApplicationSearchEngine $engine, $query) { $orders_info = pht(<<getBuiltinOrders(); $rows = array(); foreach ($orders as $key => $order) { $rows[] = array( $key, $order['name'], implode(', ', $order['vector']), ); } $orders_table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Description'), pht('Columns'), )) ->setColumnClasses( array( 'pri', '', 'wide', )); $columns_info = pht(<<getOrderableColumns(); $rows = array(); foreach ($columns as $key => $column) { $rows[] = array( $key, idx($column, 'unique') ? pht('Yes') : pht('No'), ); } $columns_table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Unique'), )) ->setColumnClasses( array( 'pri', 'wide', )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Result Ordering')) ->setCollapsed(true) ->appendChild($this->buildRemarkup($orders_info)) ->appendChild($orders_table) ->appendChild($this->buildRemarkup($columns_info)) ->appendChild($columns_table); } private function buildFieldsBox( PhabricatorApplicationSearchEngine $engine) { $info = pht(<<getAllConduitFieldSpecifications(); $rows = array(); foreach ($specs as $key => $spec) { $type = $spec->getType(); $description = $spec->getDescription(); $rows[] = array( $key, $type, $description, ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Type'), pht('Description'), )) ->setColumnClasses( array( 'pri', 'mono', 'wide', )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Object Fields')) ->setCollapsed(true) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } private function buildAttachmentsBox( PhabricatorApplicationSearchEngine $engine) { $info = pht(<<getConduitSearchAttachments(); $rows = array(); foreach ($attachments as $key => $attachment) { $rows[] = array( $key, $attachment->getAttachmentName(), $attachment->getAttachmentDescription(), ); } $table = id(new AphrontTableView($rows)) ->setHeaders( array( pht('Key'), pht('Name'), pht('Description'), )) ->setColumnClasses( array( 'prewrap', 'pri', 'wide', )); return id(new PHUIObjectBoxView()) ->setHeaderText(pht('Attachments')) ->setCollapsed(true) ->appendChild($this->buildRemarkup($info)) ->appendChild($table); } private function buildPagingBox( PhabricatorApplicationSearchEngine $engine) { $info = pht(<<setHeaderText(pht('Paging and Limits')) ->setCollapsed(true) ->appendChild($this->buildRemarkup($info)); } private function buildRemarkup($remarkup) { $viewer = $this->getViewer(); $view = new PHUIRemarkupView($viewer, $remarkup); return id(new PHUIBoxView()) ->appendChild($view) ->addPadding(PHUI::PADDING_LARGE); } } diff --git a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php index d29ec1c4ac..3cf04aeca8 100644 --- a/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php +++ b/src/applications/transactions/editengine/PhabricatorEditEngineAPIMethod.php @@ -1,202 +1,161 @@ newEditEngine(); $class = $engine->getEngineApplicationClass(); return PhabricatorApplication::getByClass($class); } public function getMethodStatus() { return self::METHOD_STATUS_UNSTABLE; } public function getMethodStatusDescription() { return pht('ApplicationEditor methods are highly unstable.'); } final protected function defineParamTypes() { return array( 'transactions' => 'list>', 'objectIdentifier' => 'optional id|phid|string', ); } final protected function defineReturnType() { return 'map'; } final protected function execute(ConduitAPIRequest $request) { $engine = $this->newEditEngine() ->setViewer($request->getUser()); return $engine->buildConduitResponse($request); } final public function getMethodDescription() { + return pht( + 'This is a standard **ApplicationEditor** method which allows you to '. + 'create and modify objects by applying transactions. For documentation '. + 'on these endpoints, see '. + '**[[ %s | Conduit API: Using Edit Endpoints ]]**.', + PhabricatorEnv::getDoclink('Conduit API: Using Edit Endpoints')); + } + + final public function getMethodDocumentation() { $viewer = $this->getViewer(); $engine = $this->newEditEngine() ->setViewer($viewer); $types = $engine->getConduitEditTypes(); $out = array(); - $out[] = pht(<<buildEditTypesBoxes($engine, $types); -```lang=json, name=Example Single Transaction -{ - "type": "title", - "value": "New Object Title" -} -``` - -By passing a list of transactions in the `transactions` parameter, you can -apply a sequence of edits. For example, you'll often pass a value like this to -create an object with several field values or apply changes to multiple fields: - -```lang=json, name=Example Transaction List -[ - { - "type": "title", - "value": "New Object Title" - }, - { - "type": "body", - "value": "New body text for the object." - }, - { - "type": "projects.add", - "value": ["PHID-PROJ-1111", "PHID-PROJ-2222"] + return $out; } -] -``` - -Exactly which types of edits are available depends on the object you're editing. - - -Creating Objects ----------------- - -To create an object, pass a list of `transactions` but leave `objectIdentifier` -empty. This will create a new object with the initial field values you -specify. - - -Editing Objects ---------------- - -To edit an object, pass a list of `transactions` and specify an object to -apply them to with `objectIdentifier`. This will apply the changes to the -object. - -You may pass an ID (like `123`), PHID (like `PHID-WXYZ-abcdef...`), or -monogram (like `T123`, for objects which have monograms). - - -Return Type ------------ - -WARNING: The structure of the return value from these methods is likely to -change as ApplicationEditor evolves. - -Return values look something like this for now: - -```lang=json, name=Example Return Value -{ - "object": { - "phid": "PHID-XXXX-1111" - }, - "transactions": [ - { - "phid": "PHID-YYYY-1111", - }, - { - "phid": "PHID-YYYY-2222", - } - ] -} -``` - -The `object` key contains information about the object which was created or -edited. -The `transactions` key contains information about the transactions which were -actually applied. For many reasons, the transactions which actually apply may -be greater or fewer in number than the transactions you provided, or may differ -in their nature in other ways. + private function buildEditTypesBoxes( + PhabricatorEditEngine $engine, + array $types) { + $boxes = array(); -Edit Types -========== - -This API method supports these edit types: -EOTEXT - ); + $summary_info = pht( + 'This endpoint supports these types of transactions. See below for '. + 'detailed information about each transaction type.'); - $key = pht('Key'); - $description = pht('Description'); - $head_type = pht('Type'); - - $table = array(); - $table[] = "| {$key} | {$description} |"; - $table[] = '|--------|----------------|'; + $rows = array(); foreach ($types as $type) { - $edit_type = $type->getEditType(); - $edit_description = $type->getConduitDescription(); - $table[] = "| `{$edit_type}` | {$edit_description} |"; + $rows[] = array( + $type->getEditType(), + $type->getConduitDescription(), + ); } - $out[] = implode("\n", $table); + $summary_table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Key'), + pht('Description'), + )) + ->setColumnClasses( + array( + 'prewrap', + 'wide', + )); + + $boxes[] = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Transaction Types')) + ->setCollapsed(true) + ->appendChild($this->buildRemarkup($summary_info)) + ->appendChild($summary_table); foreach ($types as $type) { $section = array(); - $section[] = pht('Edit Type: %s', $type->getEditType()); - $section[] = '---------'; - $section[] = null; + $section[] = $type->getConduitDescription(); - $section[] = null; - $section[] = pht( - 'This edit generates transactions of type `%s` internally.', - $type->getTransactionType()); - $section[] = null; $type_documentation = $type->getConduitDocumentation(); - if ($type_documentation) { + if (strlen($type_documentation)) { $section[] = $type_documentation; - $section[] = null; } - $type_description = pht( - 'Use `%s` to select this edit type.', - $type->getEditType()); + $section = implode("\n\n", $section); + + $rows = array(); - $value_type = $type->getConduitType(); - $value_description = $type->getConduitTypeDescription(); + $rows[] = array( + 'type', + 'const', + $type->getEditType(), + ); - $table = array(); - $table[] = "| {$key} | {$head_type} | {$description} |"; - $table[] = '|--------|--------------|----------------|'; - $table[] = "| `type` | `const` | {$type_description} |"; - $table[] = "| `value` | `{$value_type}` | {$value_description} |"; - $section[] = implode("\n", $table); + $rows[] = array( + 'value', + $type->getConduitType(), + $type->getConduitTypeDescription(), + ); - $out[] = implode("\n", $section); + $type_table = id(new AphrontTableView($rows)) + ->setHeaders( + array( + pht('Key'), + pht('Type'), + pht('Description'), + )) + ->setColumnClasses( + array( + 'prewrap', + 'prewrap', + 'wide', + )); + + $boxes[] = id(new PHUIObjectBoxView()) + ->setHeaderText(pht('Transaction Type: %s', $type->getEditType())) + ->setCollapsed(true) + ->appendChild($this->buildRemarkup($section)) + ->appendChild($type_table); } - $out = implode("\n\n", $out); - return $out; + return $boxes; + } + + + private function buildRemarkup($remarkup) { + $viewer = $this->getViewer(); + + $view = new PHUIRemarkupView($viewer, $remarkup); + + return id(new PHUIBoxView()) + ->appendChild($view) + ->addPadding(PHUI::PADDING_LARGE); } } diff --git a/src/docs/book/user.book b/src/docs/book/user.book index 2312bf65cd..20a72698be 100644 --- a/src/docs/book/user.book +++ b/src/docs/book/user.book @@ -1,39 +1,42 @@ { "name": "phabricator", "title": "Phabricator User Documentation", "short": "Phabricator User Docs", "preface": "Instructions for installing, configuring, and using Phabricator.", "root": "../../../", "uri.source": "https://secure.phabricator.com/diffusion/P/browse/master/%f$%l", "rules": { "(\\.diviner$)": "DivinerArticleAtomizer" }, "exclude": [ "(^externals/)", "(^resources/)", "(^scripts/)", "(^src/docs/contributor/)", "(^src/docs/flavor/)", "(^src/docs/tech/)", "(^support/)", "(^webroot/rsrc/externals/)" ], "groups": { "intro": { "name": "Introduction" }, "config": { "name": "Configuration" }, "userguide": { "name": "Application User Guides" }, + "conduit": { + "name": "API Documentation" + }, "fieldmanual": { "name": "Field Manuals" }, "cellar": { "name": "Musty Cellar" } } } diff --git a/src/docs/tech/conduit.diviner b/src/docs/tech/conduit.diviner deleted file mode 100644 index c31473b684..0000000000 --- a/src/docs/tech/conduit.diviner +++ /dev/null @@ -1,28 +0,0 @@ -@title Conduit Technical Documentation -@group conduit - -Technical overview of the Conduit API. - -Overview -======== - -Conduit is the HTTP API for Phabricator. It is roughly JSON-RPC: you usually -pass a JSON blob, and usually get a JSON blob back, although both call and -result formats are flexible in some cases. - -The easiest way to begin exploring Conduit is by visiting {nav Conduit} in the -web UI. The application provides an API console which you can use to explore -available methods, make calls, read documentation, and see examples. - -The API console has details about how to construct calls and generate API -tokens for authentication. - -The three primary ways to make Conduit calls are: - - - `arc call-conduit`: You can use this `arc` command to execute low-level - Conduit calls. - - `curl`: You can format a call with basic HTTP parameters and cURL. - - `ConduitClient`: - -There are also clients available in other languages. The Arcanist CLI client -for Phabricator is implemented over Conduit. diff --git a/src/docs/user/field/conduit_changes.diviner b/src/docs/user/field/conduit_changes.diviner index 3b78490546..e77a582251 100644 --- a/src/docs/user/field/conduit_changes.diviner +++ b/src/docs/user/field/conduit_changes.diviner @@ -1,51 +1,59 @@ @title Managing Conduit Changes @group fieldmanual Help with managing Conduit API changes. Overview ======== Many parts of the Conduit API are stable, but some parts are subject to change. For example, when we write a new application, it usually adds several new API methods and may update older methods. This document discusses API stability and how to minimize disruption when transitionig between API versions. Method Statuses =============== Methods have one of three statuses: - **Unstable**: This is a new or experimental method which is subject to change. You may call these methods to get access to recently released features, but should expect that you may need to adjust your usage of them before they stabilize. - **Stable**: This is an established method which generally will not change. - **Deprecated**: This method will be removed in a future version of Phabricator and callers should cease using it. Normally, a method is deprecated only when it is obsolete or a new, more powerful method is available to replace it. Finding Deprecated Calls ======================== You can identify calls to deprecated methods in {nav Conduit > Call Logs}. Use {nav My Deprecated Calls} to find calls to deprecated methods you have made, and {nav Deprecated Call Logs} to find deprecated calls by all users. You can also search for calls by specific users. For example, it may be useful to serach for any bot accounts you run to make sure they aren't calling outdated APIs. The most common cause of calls to deprecated methods is users running very old versions of Arcanist. They can normally upgrade by running `arc upgrade`. When the changelogs mention a method deprecation, you can use the call logs to identify callers and notify them to upgrade or switch away. When the changelogs mention a method removal, you can use the call logs to verify that you will not be impacted. + + +Next Steps +========== + +Continue by: + + - returning to @{article:Conduit API Overview}. diff --git a/src/docs/user/userguide/conduit.diviner b/src/docs/user/userguide/conduit.diviner new file mode 100644 index 0000000000..5292736f30 --- /dev/null +++ b/src/docs/user/userguide/conduit.diviner @@ -0,0 +1,68 @@ +@title Conduit API Overview +@group conduit + +Overview of the Conduit API. + +Overview +======== + +Conduit is the HTTP API for Phabricator. It is roughly JSON-RPC: you usually +pass a JSON blob, and usually get a JSON blob back, although both call and +result formats are flexible in some cases. + +API Clients +=========== + +The primary ways to make Conduit calls are: + +**Web Console**: The {nav Conduit} application provides a web UI for exploring +the API and making calls. This is the best starting point for learning about +the API. See the next section for details. + +`ConduitClient`: This is the official client available in `libphutil`, and +the one used by `arc`. + +`arc call-conduit`: You can use this `arc` command to execute low-level +Conduit calls by piping JSON in to stdin. This can provide a simple way +to explore the API, or a quick way to get API access from a script written +in another language without needing a real client. + +`curl`: You can format a call with basic HTTP parameters and cURL. The console +includes examples which show how to format calls. + +**Other Clients**: There are also clients available in other languages. You +can check the [[ https://secure.phabricator.com/w/community_resources/ | +Community Resources ]] page for links. + +API Console +=========== + +The easiest way to begin exploring Conduit is by visiting {nav Conduit} in the +web UI. The application provides an API console which you can use to explore +available methods, make calls, read documentation, and see examples. + +The API console has details about how to construct calls and generate API +tokens for authentication. + + +Querying and Reading Objects +============================ + +For information on searching for objects and reading their properties and +information, see @{article:Conduit API: Using Search Endpoints}. + + +Creating and Editing Objects +============================ + +For information on creating, editing and updating objects, see +@{article:Conduit API: Using Search Endpoints}. + + +Next Steps +========== + +Continue by: + + - reading recommendations on responding to API changes in + @{article:Managing Conduit Changes}. diff --git a/src/docs/user/userguide/conduit_edit.diviner b/src/docs/user/userguide/conduit_edit.diviner new file mode 100644 index 0000000000..01d15873a8 --- /dev/null +++ b/src/docs/user/userguide/conduit_edit.diviner @@ -0,0 +1,115 @@ +@title Conduit API: Using Edit Endpoints +@group conduit + +Describes how to use edit endpoints to create and update objects. + +Overview +======== + +Many applications provide `edit` endpoints, which are the primary way to +create and update objects (like tasks) using the API. + +To create or edit an object, you'll build a list of //transactions// and pass +them to the endpoint. Each transaction applies a change to a field or property +on the object. + +For example, a transaction might change the title of an object or add +subscribers. + +When creating an object, transactions will be applied to an empty object. When +editing an object, transactions will be applied to an existing object. + +The best reference for a particular `edit` endpoint is the Conduit API console. +For example, you can find the console page for `maniphest.edit` by navigating +to {nav Conduit > maniphest.edit} in the web UI. This page contains detailed +information about the endpoint and how it can be used. + +Creating Objects +================ + +To create objects, pass a list of transactions but leave `objectIdentfier` +blank. This tells the endpoint that you want to create a new, empty object and +then apply the transactions to it. + + +Editing Objects +=============== + +To edit objects, pass a list of transactions and use `objectIdentifier` to +specify which object to apply them to. You can normally pass an ID or PHID, +and many applicaitons also allow you to pass a monogram (for example, you can +edit a task by passing `T123`). + + +Building Transactions +===================== + +When creating or editing objects, you'll build a list of transactions to +apply. This transaction list will look something like this: + +```lang=json, name="Example Transaction List" +[ + { + "type": "title", + "value": "Assemble in the barnyard" + }, + { + "type": "description", + "value": "All animals should assemble in the barnyard promptly." + }, + { + "type": "subscribers.add", + "value": ["dog", "cat", "mouse"] + } +] +``` + +Applied to an empty object (say, a task), these transactions would create a new +task with the specified title, description and subscribers. + +Applied to an existing object, they would retitle the task, change its +description, and add new subscribers. + +The particular transactions available on each object are documented on the +Conduit API console page for that object. + + +Return Type +=========== + +WARNING: The structure of the return value from these methods is likely to +change as ApplicationEditor evolves. + +Return values look something like this for now: + +```lang=json, name=Example Return Value +{ + "object": { + "phid": "PHID-XXXX-1111" + }, + "transactions": [ + { + "phid": "PHID-YYYY-1111", + }, + { + "phid": "PHID-YYYY-2222", + } + ] +} +``` + +The `object` key contains information about the object which was created or +edited. + +The `transactions` key contains information about the transactions which were +actually applied. For many reasons, the transactions which actually apply may +be greater or fewer in number than the transactions you provided, or may differ +in their nature in other ways. + + +Next Steps +========== + +Continue by: + + - returning to the @{article:Conduit API Overview}. diff --git a/src/docs/user/userguide/conduit_search.diviner b/src/docs/user/userguide/conduit_search.diviner new file mode 100644 index 0000000000..2253831c65 --- /dev/null +++ b/src/docs/user/userguide/conduit_search.diviner @@ -0,0 +1,74 @@ +@title Conduit API: Using Search Endpoints +@group conduit + +Describes how to use search endpoints to find objects and read information. + +Overview +======== + +Many applications provide `search` endpoints, which are the primary way to +get information about objects (like tasks) using the API. + +To read information about objects, you'll specify a //query// which describes +which objects you want to retrieve. You can query for specific objects by +ID, or for a list of objects satisfying certain constraints (for example, open +tasks in a particular project). + +The best reference for a particular `search` endpoint is the Conduit API +console. For example, you can find the console page for `maniphest.search` by +navigating to {nav Conduit > maniphest.search} in the web UI. This page +contains detailed information about the endpoint and how it can be used. + + +Specifying a Query +================== + +The simplest query you can use is no query at all: just make a request with +no parameters. This will return the first page of visible objects. Most +applications sort objects by creation date by default, so usually this is +the 100 most recent objects. + +The easiest way to constrain results is to use a builtin query or a custom +query that you build using the web UI. To do this, first issue the query in +the web UI (for example, by clicking the builtin link on the left nav of the +list view, or by submitting the query form). + +The results page will include a //query key// in the URL. For builtin queries, +this is usually a human-readable term like `all` or `active`. For custom +queries, it is a hash value which looks something like `MT0Rh0fB2x4I`. + +You can submit this key in the `queryKey` parameter to issue the exact same +query via the Conduit API. This provides a simple way to build complex queries: +just build the via the web UI, then reuse the same query in the API. + +If you need more control or want to build dynamic queries, use the +`constraints` parameter to set constraints for individual query fields. + +For more details, consult the Conduit API console documentation for the +method you're using. It includes documentation on all available constraints +and lists builtin and saved query keys. + + +Attachments +=========== + +By default, queries return basic information about objects. If you want more +detailed information, most applications offer //attachments// which can let +you retrieve more information. + +For example, subscribers and projects are not returned by default, but you +can use subscribers to query them if you need this data. + +Asking for more data means a slower query and a larger result, so usually you +should only ask for data you need. + +The Conduit API console page for each query method has detailed information +on which attachments it supports. + + +Next Steps +========== + +Continue by: + + - returning to the @{article:Conduit API Overview}.