Page Menu
Home
Phabricator
Search
Configure Global Search
Log In
Files
F14005476
D12770.id30695.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
D12770.id30695.diff
View Options
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
Details
Attached
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)
Attached To
Mode
D12770: Show how to call Conduit API methods from clients
Attached
Detach File
Event Timeline
Log In to Comment