Page MenuHomePhabricator

D12770.id30695.diff
No OneTemporary

D12770.id30695.diff

diff --git a/resources/celerity/map.php b/resources/celerity/map.php
--- a/resources/celerity/map.php
+++ b/resources/celerity/map.php
@@ -39,6 +39,7 @@
'rsrc/css/application/base/phabricator-application-launch-view.css' => '16ca323f',
'rsrc/css/application/base/standard-page-view.css' => 'd3e1abe9',
'rsrc/css/application/chatlog/chatlog.css' => '852140ff',
+ 'rsrc/css/application/conduit/conduit-api.css' => '7bc725c4',
'rsrc/css/application/config/config-options.css' => '7fedf08b',
'rsrc/css/application/config/config-template.css' => '8e6c6fcd',
'rsrc/css/application/config/config-welcome.css' => '6abd79be',
@@ -507,6 +508,7 @@
'aphront-typeahead-control-css' => '0e403212',
'auth-css' => '1e655982',
'changeset-view-manager' => '58562350',
+ 'conduit-api-css' => '7bc725c4',
'config-options-css' => '7fedf08b',
'config-welcome-css' => '6abd79be',
'conpherence-durable-column-view' => '2e68a92f',
diff --git a/src/applications/conduit/call/ConduitCall.php b/src/applications/conduit/call/ConduitCall.php
--- a/src/applications/conduit/call/ConduitCall.php
+++ b/src/applications/conduit/call/ConduitCall.php
@@ -150,5 +150,9 @@
return $method;
}
+ public function getMethodImplementation() {
+ return $this->handler;
+ }
+
}
diff --git a/src/applications/conduit/controller/PhabricatorConduitAPIController.php b/src/applications/conduit/controller/PhabricatorConduitAPIController.php
--- a/src/applications/conduit/controller/PhabricatorConduitAPIController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitAPIController.php
@@ -21,6 +21,7 @@
$method = $this->method;
$api_request = null;
+ $method_implementation = null;
$log = new PhabricatorConduitMethodCallLog();
$log->setMethod($method);
@@ -36,6 +37,7 @@
list($metadata, $params) = $this->decodeConduitParams($request, $method);
$call = new ConduitCall($method, $params);
+ $method_implementation = $call->getMethodImplementation();
$result = null;
@@ -151,7 +153,8 @@
return $this->buildHumanReadableResponse(
$method,
$api_request,
- $response->toDictionary());
+ $response->toDictionary(),
+ $method_implementation);
case 'json':
default:
return id(new AphrontJSONResponse())
@@ -525,7 +528,8 @@
private function buildHumanReadableResponse(
$method,
ConduitAPIRequest $request = null,
- $result = null) {
+ $result = null,
+ ConduitAPIMethod $method_implementation = null) {
$param_rows = array();
$param_rows[] = array('Method', $this->renderAPIValue($method));
@@ -574,11 +578,20 @@
->addTextCrumb($method, $method_uri)
->addTextCrumb(pht('Call'));
+ $example_panel = null;
+ if ($request && $method_implementation) {
+ $params = $request->getAllParameters();
+ $example_panel = $this->renderExampleBox(
+ $method_implementation,
+ $params);
+ }
+
return $this->buildApplicationPage(
array(
$crumbs,
$param_panel,
$result_panel,
+ $example_panel,
),
array(
'title' => pht('Method Call Result'),
diff --git a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php
--- a/src/applications/conduit/controller/PhabricatorConduitConsoleController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitConsoleController.php
@@ -3,31 +3,23 @@
final class PhabricatorConduitConsoleController
extends PhabricatorConduitController {
- private $method;
-
public function shouldAllowPublic() {
return true;
}
- public function willProcessRequest(array $data) {
- $this->method = $data['method'];
- }
-
- public function processRequest() {
-
- $request = $this->getRequest();
- $viewer = $request->getUser();
+ public function handleRequest(AphrontRequest $request) {
+ $viewer = $this->getViewer();
+ $method_name = $request->getURIData('method');
$method = id(new PhabricatorConduitMethodQuery())
->setViewer($viewer)
- ->withMethods(array($this->method))
+ ->withMethods(array($method_name))
->executeOne();
-
if (!$method) {
return new Aphront404Response();
}
- $can_call_method = false;
+ $call_uri = '/api/'.$method->getAPIMethodName();
$status = $method->getMethodStatus();
$reason = $method->getMethodStatusDescription();
@@ -48,37 +40,13 @@
break;
}
- $error_types = $method->getErrorTypes();
- $error_types['ERR-CONDUIT-CORE'] = pht('See error message for details.');
- $error_description = array();
- foreach ($error_types as $error => $meaning) {
- $error_description[] = hsprintf(
- '<li><strong>%s:</strong> %s</li>',
- $error,
- $meaning);
- }
- $error_description = phutil_tag('ul', array(), $error_description);
-
- $form = new AphrontFormView();
- $form
+ $form = id(new AphrontFormView())
+ ->setAction($call_uri)
->setUser($request->getUser())
- ->setAction('/api/'.$this->method)
- ->appendChild(
- id(new AphrontFormStaticControl())
- ->setLabel('Description')
- ->setValue($method->getMethodDescription()))
- ->appendChild(
- id(new AphrontFormStaticControl())
- ->setLabel('Returns')
- ->setValue($method->getReturnType()))
- ->appendChild(
- id(new AphrontFormMarkupControl())
- ->setLabel('Errors')
- ->setValue($error_description))
- ->appendChild(hsprintf(
- '<p class="aphront-form-instructions">Enter parameters using '.
- '<strong>JSON</strong>. For instance, to enter a list, type: '.
- '<tt>["apple", "banana", "cherry"]</tt>'));
+ ->appendRemarkupInstructions(
+ pht(
+ 'Enter parameters using **JSON**. For instance, to enter a '.
+ 'list, type: `["apple", "banana", "cherry"]`'));
$params = $method->getParamTypes();
foreach ($params as $param => $desc) {
@@ -117,12 +85,22 @@
->setHeader($method->getAPIMethodName());
$form_box = id(new PHUIObjectBoxView())
- ->setHeader($header)
- ->setFormErrors($errors)
+ ->setHeaderText(pht('Call Method'))
->appendChild($form);
$content = array();
+ $properties = $this->buildMethodProperties($method);
+
+ $info_box = id(new PHUIObjectBoxView())
+ ->setHeaderText(pht('API Method: %s', $method->getAPIMethodName()))
+ ->setFormErrors($errors)
+ ->appendChild($properties);
+
+ $content[] = $info_box;
+ $content[] = $form_box;
+ $content[] = $this->renderExampleBox($method, null);
+
$query = $method->newQueryObject();
if ($query) {
$orders = $query->getBuiltinOrders();
@@ -185,7 +163,6 @@
return $this->buildApplicationPage(
array(
$crumbs,
- $form_box,
$content,
),
array(
@@ -193,4 +170,41 @@
));
}
+ private function buildMethodProperties(ConduitAPIMethod $method) {
+ $viewer = $this->getViewer();
+
+ $view = id(new PHUIPropertyListView());
+
+ $view->addProperty(
+ pht('Returns'),
+ $method->getReturnType());
+
+ $error_types = $method->getErrorTypes();
+ $error_types['ERR-CONDUIT-CORE'] = pht('See error message for details.');
+ $error_description = array();
+ foreach ($error_types as $error => $meaning) {
+ $error_description[] = hsprintf(
+ '<li><strong>%s:</strong> %s</li>',
+ $error,
+ $meaning);
+ }
+ $error_description = phutil_tag('ul', array(), $error_description);
+
+ $view->addProperty(
+ pht('Errors'),
+ $error_description);
+
+
+ $description = $method->getMethodDescription();
+ $description = PhabricatorMarkupEngine::renderOneObject(
+ id(new PhabricatorMarkupOneOff())->setContent($description),
+ 'default',
+ $viewer);
+ $view->addSectionHeader(pht('Description'));
+ $view->addTextContent($description);
+
+ return $view;
+ }
+
+
}
diff --git a/src/applications/conduit/controller/PhabricatorConduitController.php b/src/applications/conduit/controller/PhabricatorConduitController.php
--- a/src/applications/conduit/controller/PhabricatorConduitController.php
+++ b/src/applications/conduit/controller/PhabricatorConduitController.php
@@ -24,4 +24,250 @@
return $this->buildSideNavView()->getMenu();
}
+ protected function renderExampleBox(ConduitAPIMethod $method, $params) {
+ $arc_example = id(new PHUIPropertyListView())
+ ->addRawContent($this->renderExample($method, 'arc', $params));
+
+ $curl_example = id(new PHUIPropertyListView())
+ ->addRawContent($this->renderExample($method, 'curl', $params));
+
+ $php_example = id(new PHUIPropertyListView())
+ ->addRawContent($this->renderExample($method, 'php', $params));
+
+ $panel_link = phutil_tag(
+ 'a',
+ array(
+ 'href' => '/settings/panel/apitokens/',
+ ),
+ pht('Conduit API Tokens'));
+
+ $panel_link = phutil_tag('strong', array(), $panel_link);
+
+ $messages = array(
+ pht(
+ 'Use the %s panel in Settings to generate or manage API tokens.',
+ $panel_link),
+ );
+
+ $info_view = id(new PHUIInfoView())
+ ->setErrors($messages)
+ ->setSeverity(PHUIInfoView::SEVERITY_NOTICE);
+
+ return id(new PHUIObjectBoxView())
+ ->setHeaderText(pht('Examples'))
+ ->setInfoView($info_view)
+ ->addPropertyList($arc_example, pht('arc call-conduit'))
+ ->addPropertyList($curl_example, pht('cURL'))
+ ->addPropertyList($php_example, pht('PHP'));
+ }
+
+ private function renderExample(
+ ConduitAPIMethod $method,
+ $kind,
+ $params) {
+
+ switch ($kind) {
+ case 'arc':
+ $example = $this->buildArcanistExample($method, $params);
+ break;
+ case 'php':
+ $example = $this->buildPHPExample($method, $params);
+ break;
+ case 'curl':
+ $example = $this->buildCURLExample($method, $params);
+ break;
+ default:
+ throw new Exception(pht('Conduit client "%s" is not known.', $kind));
+ }
+
+ return $example;
+ }
+
+ private function buildArcanistExample(
+ ConduitAPIMethod $method,
+ $params) {
+
+ $parts = array();
+
+ $parts[] = '$ echo ';
+ if ($params === null) {
+ $parts[] = phutil_tag('strong', array(), '<json-parameters>');
+ } else {
+ $params = $this->simplifyParams($params);
+ $params = id(new PhutilJSON())->encodeFormatted($params);
+ $params = trim($params);
+ $params = csprintf('%s', $params);
+ $parts[] = phutil_tag('strong', array('class' => 'real'), $params);
+ }
+
+ $parts[] = ' | ';
+ $parts[] = 'arc call-conduit ';
+
+ $parts[] = '--conduit-uri ';
+ $parts[] = phutil_tag(
+ 'strong',
+ array('class' => 'real'),
+ PhabricatorEnv::getURI('/'));
+ $parts[] = ' ';
+
+ $parts[] = '--conduit-token ';
+ $parts[] = phutil_tag('strong', array(), '<conduit-token>');
+ $parts[] = ' ';
+
+ $parts[] = $method->getAPIMethodName();
+
+ return $this->renderExampleCode($parts);
+ }
+
+ private function buildPHPExample(
+ ConduitAPIMethod $method,
+ $params) {
+
+ $parts = array();
+
+ $libphutil_path = 'path/to/libphutil/src/__phutil_library_init__.php';
+
+ $parts[] = '<?php';
+ $parts[] = "\n\n";
+
+ $parts[] = 'require_once ';
+ $parts[] = phutil_var_export($libphutil_path, true);
+ $parts[] = ";\n\n";
+
+ $parts[] = '$api_token = "';
+ $parts[] = phutil_tag('strong', array(), pht('<api-token>'));
+ $parts[] = "\";\n";
+
+ $parts[] = '$api_parameters = ';
+ if ($params === null) {
+ $parts[] = 'array(';
+ $parts[] = phutil_tag('strong', array(), pht('<parameters>'));
+ $parts[] = ');';
+ } else {
+ $params = $this->simplifyParams($params);
+ $params = phutil_var_export($params, true);
+ $parts[] = phutil_tag('strong', array('class' => 'real'), $params);
+ $parts[] = ';';
+ }
+ $parts[] = "\n\n";
+
+ $parts[] = '$client = new ConduitClient(';
+ $parts[] = phutil_tag(
+ 'strong',
+ array('class' => 'real'),
+ phutil_var_export(PhabricatorEnv::getURI('/'), true));
+ $parts[] = ");\n";
+
+ $parts[] = '$client->setConduitToken($api_token);';
+ $parts[] = "\n\n";
+
+ $parts[] = '$result = $client->callMethodSynchronous(';
+ $parts[] = phutil_tag(
+ 'strong',
+ array('class' => 'real'),
+ phutil_var_export($method->getAPIMethodName(), true));
+ $parts[] = ', ';
+ $parts[] = '$api_parameters';
+ $parts[] = ");\n";
+
+ $parts[] = 'print_r($result);';
+
+ return $this->renderExampleCode($parts);
+ }
+
+ private function buildCURLExample(
+ ConduitAPIMethod $method,
+ $params) {
+
+ $call_uri = '/api/'.$method->getAPIMethodName();
+
+ $parts = array();
+
+ $linebreak = array('\\', phutil_tag('br'), ' ');
+
+ $parts[] = '$ curl ';
+ $parts[] = phutil_tag(
+ 'strong',
+ array('class' => 'real'),
+ csprintf('%R', PhabricatorEnv::getURI($call_uri)));
+ $parts[] = ' ';
+ $parts[] = $linebreak;
+
+ $parts[] = '-d api.token=';
+ $parts[] = phutil_tag('strong', array(), 'api-token');
+ $parts[] = ' ';
+ $parts[] = $linebreak;
+
+ if ($params === null) {
+ $parts[] = '-d ';
+ $parts[] = phutil_tag('strong', array(), 'param');
+ $parts[] = '=';
+ $parts[] = phutil_tag('strong', array(), 'value');
+ $parts[] = ' ';
+ $parts[] = $linebreak;
+ $parts[] = phutil_tag('strong', array(), '...');
+ } else {
+ $lines = array();
+ $params = $this->simplifyParams($params);
+
+ foreach ($params as $key => $value) {
+ $pieces = $this->getQueryStringParts(null, $key, $value);
+ foreach ($pieces as $piece) {
+ $lines[] = array(
+ '-d ',
+ phutil_tag('strong', array('class' => 'real'), $piece),
+ );
+ }
+ }
+
+ $parts[] = phutil_implode_html(array(' ', $linebreak), $lines);
+ }
+
+ return $this->renderExampleCode($parts);
+ }
+
+ private function renderExampleCode($example) {
+ require_celerity_resource('conduit-api-css');
+
+ return phutil_tag(
+ 'div',
+ array(
+ 'class' => 'PhabricatorMonospaced conduit-api-example-code',
+ ),
+ $example);
+ }
+
+ private function simplifyParams(array $params) {
+ foreach ($params as $key => $value) {
+ if ($value === null) {
+ unset($params[$key]);
+ }
+ }
+ return $params;
+ }
+
+ private function getQueryStringParts($prefix, $key, $value) {
+ if ($prefix === null) {
+ $head = phutil_escape_uri($key);
+ } else {
+ $head = $prefix.'['.phutil_escape_uri($key).']';
+ }
+
+ if (!is_array($value)) {
+ return array(
+ $head.'='.phutil_escape_uri($value),
+ );
+ }
+
+ $results = array();
+ foreach ($value as $subkey => $subvalue) {
+ $subparts = $this->getQueryStringParts($head, $subkey, $subvalue);
+ foreach ($subparts as $subpart) {
+ $results[] = $subpart;
+ }
+ }
+
+ return $results;
+ }
+
}
diff --git a/webroot/rsrc/css/application/conduit/conduit-api.css b/webroot/rsrc/css/application/conduit/conduit-api.css
new file mode 100644
--- /dev/null
+++ b/webroot/rsrc/css/application/conduit/conduit-api.css
@@ -0,0 +1,16 @@
+/**
+ * @provides conduit-api-css
+ */
+.conduit-api-example-code {
+ margin: 16px;
+ white-space: pre;
+ color: {$darkgreytext};
+}
+
+.conduit-api-example-code strong {
+ color: {$red};
+}
+
+.conduit-api-example-code strong.real {
+ color: {$blue};
+}

File Metadata

Mime Type
text/plain
Expires
Mon, Oct 28, 1:47 PM (3 w, 4 h ago)
Storage Engine
blob
Storage Format
Encrypted (AES-256-CBC)
Storage Handle
6741543
Default Alt Text
D12770.id30695.diff (15 KB)

Event Timeline