mirror of
https://github.com/retailcrm/NelmioApiDocBundle.git
synced 2025-02-02 15:51:48 +03:00
Add: sandbox, to easily try API methods
This commit is contained in:
parent
382f8fa297
commit
e6438d657f
@ -23,7 +23,8 @@ class Configuration implements ConfigurationInterface
|
||||
->root('nelmio_api_doc')
|
||||
->children()
|
||||
->scalarNode('name')->defaultValue('API documentation')->end()
|
||||
;
|
||||
->scalarNode('sandbox_target')->defaultValue('/app_dev.php')->end()
|
||||
->end();
|
||||
|
||||
return $treeBuilder;
|
||||
}
|
||||
|
@ -29,6 +29,7 @@ class NelmioApiDocExtension extends Extension
|
||||
$config = $processor->processConfiguration($configuration, $configs);
|
||||
|
||||
$container->setParameter('nelmio_api_doc.api_name', $config['name']);
|
||||
$container->setParameter('nelmio_api_doc.api_sandbox_target', $config['sandbox_target']);
|
||||
|
||||
$loader = new XmlFileLoader($container, new FileLocator(__DIR__.'/../Resources/config'));
|
||||
$loader->load('formatters.xml');
|
||||
|
@ -20,6 +20,11 @@ class HtmlFormatter extends AbstractFormatter
|
||||
*/
|
||||
private $apiName;
|
||||
|
||||
/**
|
||||
* @var string
|
||||
*/
|
||||
private $sandboxTarget;
|
||||
|
||||
/**
|
||||
* @var \Symfony\Component\Templating\EngineInterface
|
||||
*/
|
||||
@ -33,6 +38,14 @@ class HtmlFormatter extends AbstractFormatter
|
||||
$this->apiName = $apiName;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $sandboxTarget
|
||||
*/
|
||||
public function setSandboxTarget($sandboxTarget)
|
||||
{
|
||||
$this->sandboxTarget = $sandboxTarget;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EngineInterface $engine
|
||||
*/
|
||||
@ -69,9 +82,10 @@ class HtmlFormatter extends AbstractFormatter
|
||||
private function getGlobalVars()
|
||||
{
|
||||
return array(
|
||||
'apiName' => $this->apiName,
|
||||
'date' => date(DATE_RFC822),
|
||||
'css' => file_get_contents(__DIR__ . '/../Resources/public/css/screen.css'),
|
||||
'apiName' => $this->apiName,
|
||||
'sandboxTarget' => $this->sandboxTarget,
|
||||
'date' => date(DATE_RFC822),
|
||||
'css' => file_get_contents(__DIR__ . '/../Resources/public/css/screen.css'),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -30,6 +30,9 @@
|
||||
<call method="setApiName">
|
||||
<argument>%nelmio_api_doc.api_name%</argument>
|
||||
</call>
|
||||
<call method="setSandboxTarget">
|
||||
<argument>%nelmio_api_doc.api_sandbox_target%</argument>
|
||||
</call>
|
||||
</service>
|
||||
</services>
|
||||
|
||||
|
@ -1139,3 +1139,60 @@ body ul#resources li.resource ul.endpoints li.endpoint ul.operations li.operatio
|
||||
#header a {
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
|
||||
/** sandbox */
|
||||
.pane:not(.selected) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.result pre {
|
||||
border: none;
|
||||
padding: 10px;
|
||||
font-size: 0.9em;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.tabs li {
|
||||
display: inline;
|
||||
float: left;
|
||||
margin: 10px;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.tabs li.selected {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.panes {
|
||||
clear: both;
|
||||
border-top: 1px solid #C3D9EC;
|
||||
}
|
||||
|
||||
.pane.sandbox {
|
||||
border: 1px solid #C3D9EC;
|
||||
border-top: none;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.pane.sandbox legend {
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.remove {
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.pane.sandbox .result {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.parameters {
|
||||
float: left;
|
||||
width: 50%;
|
||||
}
|
||||
|
||||
.buttons {
|
||||
clear: both;
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
@ -6,10 +6,12 @@
|
||||
<meta content="IE=edge,chrome=1" http-equiv="X-UA-Compatible" />
|
||||
<title>{{ apiName }}</title>
|
||||
<link href="http://fonts.googleapis.com/css?family=Droid+Sans:400,700" rel="stylesheet" type="text/css" />
|
||||
<link href="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.css" rel="stylesheet" type="text/css"></script>
|
||||
<style type="text/css">
|
||||
{{ css|raw }}
|
||||
</style>
|
||||
<script src="http://ajax.googleapis.com/ajax/libs/jquery/1.7.2/jquery.min.js" type="text/javascript"></script>
|
||||
<script src="http://google-code-prettify.googlecode.com/svn/trunk/src/prettify.js" type="text/javascript"></script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="header">
|
||||
@ -27,6 +29,129 @@
|
||||
$('.toggler').click(function() {
|
||||
$(this).next().slideToggle('slow');
|
||||
});
|
||||
|
||||
$('.tabs li').click(function() {
|
||||
var contentGroup = $(this).parents('.content');
|
||||
|
||||
$('.pane.selected', contentGroup).removeClass('selected');
|
||||
$('.pane.' + $(this).data('pane'), contentGroup).addClass('selected');
|
||||
|
||||
$('li', $(this).parent()).removeClass('selected');
|
||||
$(this).addClass('selected');
|
||||
});
|
||||
|
||||
|
||||
|
||||
var prettifyResponse = function(text) {
|
||||
try {
|
||||
var data = typeof text === 'string' ? JSON.parse(text) : text;
|
||||
text = JSON.stringify(data, undefined, ' ');
|
||||
} catch (err) {
|
||||
}
|
||||
|
||||
// HTML encode the result
|
||||
return $('<div>').text(text).html();
|
||||
};
|
||||
|
||||
var displayFinalUrl = function(xhr, method, url, container) {
|
||||
container.html(method + ' ' + url);
|
||||
};
|
||||
|
||||
var displayResponseData = function(xhr, container) {
|
||||
container.removeClass('prettyprinted');
|
||||
|
||||
container.html(prettifyResponse(xhr.responseText));
|
||||
prettyPrint && prettyPrint();
|
||||
};
|
||||
|
||||
var displayResponseHeaders = function(xhr, container) {
|
||||
var text = xhr.status + ' ' + xhr.statusText + "\n\n";
|
||||
text += xhr.getAllResponseHeaders();
|
||||
|
||||
container.html(text);
|
||||
};
|
||||
|
||||
var displayResponse = function(xhr, method, url, result_container) {
|
||||
displayFinalUrl(xhr, method, url, $('.url', result_container));
|
||||
displayResponseData(xhr, $('.response', result_container));
|
||||
displayResponseHeaders(xhr, $('.headers', result_container));
|
||||
|
||||
result_container.show();
|
||||
};
|
||||
|
||||
$('.pane.sandbox form').submit(function() {
|
||||
var url = $(this).attr('action'),
|
||||
method = $(this).attr('method'),
|
||||
self = this,
|
||||
params = {
|
||||
_format: 'json' // default format if not overriden in the form
|
||||
},
|
||||
headers = {},
|
||||
result_container = $('.result', $(this).parent());
|
||||
|
||||
// retrieve all the parameters to send
|
||||
$('.parameters .tuple', $(this)).each(function() {
|
||||
var key, value;
|
||||
|
||||
key = $('.key', $(this)).val();
|
||||
value = $('.value', $(this)).val();
|
||||
|
||||
if (value) {
|
||||
params[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
// retrieve the additional headers to send
|
||||
$('.headers .tuple', $(this)).each(function() {
|
||||
var key, value;
|
||||
|
||||
key = $('.key', $(this)).val();
|
||||
value = $('.value', $(this)).val();
|
||||
|
||||
if (value) {
|
||||
headers[key] = value;
|
||||
}
|
||||
});
|
||||
|
||||
// fix parameters in URL
|
||||
for (var key in $.extend({}, params)) {
|
||||
if (url.indexOf('{' + key + '}') !== -1) {
|
||||
url = url.replace('{' + key + '}', params[key]);
|
||||
delete params[key];
|
||||
}
|
||||
};
|
||||
|
||||
// disable all the fiels and buttons
|
||||
$('input, button', $(this)).attr('disabled', 'disabled');
|
||||
|
||||
// and trigger the API call
|
||||
$.ajax({
|
||||
url: '{{ sandboxTarget }}' + url,
|
||||
type: method,
|
||||
data: params,
|
||||
headers: headers,
|
||||
complete: function(xhr) {
|
||||
displayResponse(xhr, method, url, result_container);
|
||||
|
||||
// and enable them back
|
||||
$('input, button', $(self)).removeAttr('disabled');
|
||||
}
|
||||
});
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.pane.sandbox').on('click', '.add', function() {
|
||||
var html = $(this).parents('.pane').find('.tuple_template').html();
|
||||
|
||||
$(this).before(html);
|
||||
|
||||
return false;
|
||||
});
|
||||
|
||||
$('.pane.sandbox').on('click', '.remove', function() {
|
||||
$(this).parent().remove();
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
@ -1,92 +1,157 @@
|
||||
<li class="{{ data.method|lower }} operation">
|
||||
<div class="heading toggler">
|
||||
<h3>
|
||||
<span class="http_method">
|
||||
<a>{{ data.method|upper }}</a>
|
||||
</span>
|
||||
<span class="path">
|
||||
{{ data.uri }}
|
||||
</span>
|
||||
</h3>
|
||||
<ul class="options">
|
||||
{% if data.description is defined %}
|
||||
<li>{{ data.description }}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
<h3>
|
||||
<span class="http_method">
|
||||
<a>{{ data.method|upper }}</a>
|
||||
</span>
|
||||
<span class="path">
|
||||
{{ data.uri }}
|
||||
</span>
|
||||
</h3>
|
||||
<ul class="options">
|
||||
{% if data.description is defined %}
|
||||
<li>{{ data.description }}</li>
|
||||
{% endif %}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
<div class="content" style="display: {% if displayContent is defined and displayContent == true %}display{% else %}none{% endif %};">
|
||||
{% if data.requirements is defined and data.requirements is not empty %}
|
||||
<h4>Requirements</h4>
|
||||
<table class="fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name, infos in data.requirements %}
|
||||
<ul class="tabs">
|
||||
<li class="selected" data-pane="content">Documentation</li>
|
||||
<li data-pane="sandbox">Sandbox</li>
|
||||
</ul>
|
||||
|
||||
<div class="panes">
|
||||
<div class="pane content selected">
|
||||
{% if data.requirements is defined and data.requirements is not empty %}
|
||||
<h4>Requirements</h4>
|
||||
<table class="fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Value</th>
|
||||
<th>Type</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name, infos in data.requirements %}
|
||||
<tr>
|
||||
<td>{{ name }}</td>
|
||||
<td>{{ infos.value }}</td>
|
||||
<td>{{ infos.type }}</td>
|
||||
<td>{{ infos.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if data.filters is defined and data.filters is not empty %}
|
||||
<h4>Filters</h4>
|
||||
<table class="fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Information</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name, infos in data.filters %}
|
||||
<tr>
|
||||
<td>{{ name }}</td>
|
||||
<td>{{ infos.value }}</td>
|
||||
<td>{{ infos.type }}</td>
|
||||
<td>{{ infos.description }}</td>
|
||||
<td>
|
||||
<ul>
|
||||
{% for key, value in infos %}
|
||||
<li><em>{{ key }}</em> : {{ value|json_encode|trim('"') }}</li>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
|
||||
{% if data.filters is defined and data.filters is not empty %}
|
||||
<h4>Filters</h4>
|
||||
<table class="fullwidth">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Name</th>
|
||||
<th>Information</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name, infos in data.filters %}
|
||||
<tr>
|
||||
<td>{{ name }}</td>
|
||||
<td>
|
||||
<ul>
|
||||
{% for key, value in infos %}
|
||||
<li><em>{{ key }}</em> : {{ value|json_encode|trim('"') }}</li>
|
||||
{% if data.parameters is defined and data.parameters is not empty %}
|
||||
<h4>Parameters</h4>
|
||||
<table class='fullwidth'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Type</th>
|
||||
<th>Required?</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name, infos in data.parameters %}
|
||||
<tr>
|
||||
<td>{{ name }}</td>
|
||||
<td>{{ infos.dataType }}</td>
|
||||
<td>{{ infos.required ? 'true' : 'false' }}</td>
|
||||
<td>{{ infos.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
</div>
|
||||
|
||||
<div class="pane sandbox">
|
||||
<form method="{{ data.method|upper }}" action="{{ data.uri }}">
|
||||
<fieldset class="parameters">
|
||||
<legend>Parameters</legend>
|
||||
{% if data.filters is defined %}
|
||||
{% for name, infos in data.filters %}
|
||||
<p class="tuple">
|
||||
<input type="text" class="key" value="{{ name }}" placeholder="Key" />
|
||||
<span>=</span>
|
||||
<input type="text" class="value" placeholder="{% if infos.description is defined %}{{ infos.description }}{% else %}Value{% endif %}" /> <span class="remove">-</span>
|
||||
</p>
|
||||
{% endfor %}
|
||||
</ul>
|
||||
</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
{% endif %}
|
||||
|
||||
{% if data.parameters is defined and data.parameters is not empty %}
|
||||
<h4>Parameters</h4>
|
||||
<table class='fullwidth'>
|
||||
<thead>
|
||||
<tr>
|
||||
<th>Parameter</th>
|
||||
<th>Type</th>
|
||||
<th>Required?</th>
|
||||
<th>Description</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{% for name, infos in data.parameters %}
|
||||
<tr>
|
||||
<td>{{ name }}</td>
|
||||
<td>{{ infos.dataType }}</td>
|
||||
<td>{{ infos.required ? 'true' : 'false' }}</td>
|
||||
<td>{{ infos.description }}</td>
|
||||
</tr>
|
||||
{% endfor %}
|
||||
</tbody>
|
||||
</table>
|
||||
{% endif %}
|
||||
<button class="add">New parameter</button>
|
||||
</fieldset>
|
||||
|
||||
<fieldset class="headers">
|
||||
<legend>Headers</legend>
|
||||
|
||||
<p class="tuple">
|
||||
<input type="text" class="key" placeholder="Key" />
|
||||
<span>=</span>
|
||||
<input type="text" class="value" placeholder="Value" /> <span class="remove">-</span>
|
||||
</p>
|
||||
|
||||
<button class="add">New header</button>
|
||||
</fieldset>
|
||||
|
||||
<div class="buttons">
|
||||
|
||||
<input type="submit" value="Try!" />
|
||||
</div>
|
||||
</form>
|
||||
|
||||
<script type="text/x-tmpl" class="tuple_template">
|
||||
<p class="tuple">
|
||||
<input type="text" class="key" placeholder="Key" />
|
||||
<span>=</span>
|
||||
<input type="text" class="value" placeholder="Value" /> <span class="remove">-</span>
|
||||
</p>
|
||||
</script>
|
||||
|
||||
<div class="result">
|
||||
<h4>Request URL</h4>
|
||||
<pre class="url"></pre>
|
||||
|
||||
<h4>Response Body</h4>
|
||||
<pre class="response prettyprint"></pre>
|
||||
|
||||
<h4>Response headers</h4>
|
||||
<pre class="headers"></pre>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</li>
|
||||
|
Loading…
x
Reference in New Issue
Block a user