Skip to content

Instantly share code, notes, and snippets.

@jrbasso
Created August 16, 2012 02:41
Show Gist options
  • Star 5 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save jrbasso/3365894 to your computer and use it in GitHub Desktop.
Save jrbasso/3365894 to your computer and use it in GitHub Desktop.
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>API Web Runner</title>
<link href="bootstrap20/css/bootstrap.min.css" rel="stylesheet" />
<link href="bootstrap20/css/bootstrap-responsive.min.css" rel="stylesheet" />
<link href="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.22/themes/redmond/jquery-ui.css" rel="stylesheet" />
<style type="text/css">
/* Override some defaults */
body {
padding-top: 50px; /* 40px to make the container go all the way to the bottom of the topbar */
}
.container > footer p {
text-align: center; /* center align it with the container */
}
.container {
width: 820px; /* downsize our container to make the content feel a bit tighter and more cohesive. NOTE: this removes two full columns from the grid, meaning you only go to 14 columns and not 16. */
}
.ui-menu-item > a > small {
font-size: 10px;
}
.ui-menu-item {
border-bottom: 1px solid black;
}
</style>
</head>
<body>
<div class="navbar navbar-fixed-top">
<div class="navbar-inner">
<div class="container">
<a class="brand" href="#">API Web Runner</a>
</div>
</div>
</div>
<div class="container">
<header>
<h1>API Web Runner</h1>
<p>Simple interface to make requests to interal API.</p>
</header>
<div class="row">
<form class="well">
<div class="row">
<div class="span1">
<label for="request-method">Method</label>
<select id="request-method" class="span1">
<option value="GET">GET</option>
<option value="POST">POST</option>
<option value="PUT">PUT</option>
<option value="DELETE">DELETE</option>
</select>
</div>
<div class="span4">
<label for="request-url">URL</label>
<input id="request-url" type="text" class="span4" placeholder="Type the URL starting with slash">
</div>
</div>
<div class="row">
<div class="span1">
<label>Locale</label>
<input id="request-locale" type="text" class="span1" value="en-US">
</div>
<div class="span6">
<label>Requester Username</label>
<input id="request-username" type="text" class="span3">
<span class="help-inline">Leave empty for anonymous request</span>
</div>
</div>
<div class="row">
<div class="span7">
<table id="request-params" class="table table-striped">
<thead>
<tr>
<th>Parameter</th>
<th>Value</th>
<th>Action</th>
</tr>
</thead>
<tbody></tbody>
</table>
<button id="request-add-param" class="btn">Add Param</button>
</div>
</div>
<br><br>
<button id="request-submit" type="submit" class="btn">Request</button>
</form>
<div id="response" class="well" style="display: none">
<h2>Response</h2>
<p>Code: <span id="response-code">0</span></p>
<pre id="response-body"></pre>
</div>
<div id="response-log" class="well" style="display: none">
<h2>Log messages</h2>
<table id="response-log-messages" class="table table-striped">
<thead>
<tr>
<th>Message</th>
<th>Type</th>
<th>Data</th>
</tr>
</thead>
<tbody>
</tbody>
</table>
</div>
</div>
<footer>
<p>&copy; Foo Bar Company</p>
</footer>
</div>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jquery/1.8.0/jquery.min.js"></script>
<script type="text/javascript" src="//ajax.googleapis.com/ajax/libs/jqueryui/1.8.22/jquery-ui.min.js"></script>
<script type="text/javascript" src="bootstrap20/js/bootstrap.min.js"></script>
<script type="text/javascript">
var searchList = {
GET: [],
POST: [],
PUT: [],
DELETE: []
};
function buildUriRegex(uri, params) {
// Convert the URI to RegExp string
for (var param in params) {
uri = uri.replace('/' + params[param].toUpperCase(), '/(.+)');
}
// Extract the pieces and transform variables in RegExp objects
var pieces = uri.substring(1).split('/');
for (piece in pieces) {
if (pieces[piece] === '(.+)') {
pieces[piece] = new RegExp(pieces[piece]);
}
}
return pieces;
}
function buildSearch(data) {
var controller, method;
if (!data.data) {
return;
}
for (var controllerIndex in data.data) {
controller = data.data[controllerIndex];
for (var methodIndex in controller) {
method = controller[methodIndex];
searchList[method.http_method].push({
uri: method.uri,
uriParams: method.params,
uriRegex: buildUriRegex(method.uri, method.params),
description: method.doc_comment.description || '',
params: method.doc_comment.tags.postParam || method.doc_comment.tags.inputParam || {}
});
}
}
enableAutocomplete();
}
function enableAutocomplete() {
var $url = $('#request-url');
$url.autocomplete({
source: function(criteria, response) {
var SUGGESTION_LIMIT = 8,
method = $('#request-method').val(),
userUrl = criteria.term,
userUrlPieces = userUrl.substring(1).split('/'),
uris = [],
searchMethod;
// Ignore empty or just /
if (userUrl.length < 2) {
return;
}
// Remove the last element if it is empty
if (userUrlPieces[userUrlPieces.length - 1] == "") {
userUrlPieces.pop();
}
loopMethods:
for (var i in searchList[method]) {
var userPiecesLength = userUrlPieces.length;
searchMethod = searchList[method][i];
// Ignore if the number of pieces in method is lesser than user input
if (searchMethod.uriRegex.length < userPiecesLength) {
continue;
}
loopUserPieces:
for (var userPieceIndex in userUrlPieces) {
var methodPiece = searchMethod.uriRegex[userPieceIndex],
userPiece = userUrlPieces[userPieceIndex];
// Just need to validate the strings, the objects will always match
if (typeof(methodPiece) === 'string') {
if (userPieceIndex == userPiecesLength - 1) {
// Last piece can be partial
if (methodPiece.substring(0, userPiece.length) != userPiece) {
continue loopMethods;
}
} else if (methodPiece != userPiece) {
continue loopMethods;
}
}
}
// Nothing else against add the uri in the list
uris.push({
label: searchMethod.uri,
value: i,
method: searchMethod
});
if (uris.length >= SUGGESTION_LIMIT) {
break;
}
}
response(uris);
},
focus: function(event, ui) {
// Do not change the input field while selecting
return false;
},
select: function(event, ui) {
// Let's make a mess with the user url
var $url = $('#request-url'),
userPieces = $url.val().substring(1).split('/'),
userPiecesLength = userPieces.length,
method = ui.item.method,
newUrl = '';
if (method.uriParams.length == 0) {
newUrl = method.uri;
} else {
var paramPos = 0;
for (var i in method.uriRegex) {
if (typeof(method.uriRegex[i]) === 'string') {
newUrl += '/' + method.uriRegex[i];
} else {
newUrl += '/' + (userPieces[i] || method.uriParams[paramPos].toUpperCase());
paramPos++;
}
}
}
$url.val(newUrl);
// Set the params
cleanParams();
for (var key in ui.item.method.params) {
addParam(key, '', ui.item.method.params[key].description, ui.item.method.params[key].optional);
}
return false;
}
}).data('autocomplete')._renderItem = function (ul, item) {
return $('<li></li>')
.data('item.autocomplete', item)
.append('<a><strong>' + item.method.uri + '</strong><br><small>' + item.method.description + '</small></a>')
.appendTo(ul);
};
}
function loadDefaultParams() {
var urlParams = {};
(function () {
var match,
pl = /\+/g, // Regex for replacing addition symbol with a space
search = /([^&=]+)=?([^&]*)/g,
decode = function (s) { return decodeURIComponent(s.replace(pl, " ")); },
query = window.location.search.substring(1);
while (match = search.exec(query)) {
urlParams[decode(match[1])] = decode(match[2]);
}
})();
if (urlParams['method']) {
$('#request-method').val(urlParams['method']);
}
if (urlParams['uri']) {
$('#request-url').val(urlParams['uri']);
}
}
function getSubmitParams() {
var params = {};
$('#request-params > tbody > tr').each(function(index) {
var name = $(this).find('.request-param-name').val(),
value = $(this).find('.request-param-value').val();
if (!name) {
return;
}
params[name] = value;
});
return params;
}
function cleanParams() {
$('#request-params > tbody').html('');
}
function addParam(key, value, help, optional) {
var $paramRows = $('#request-params > tbody'),
tr;
if (optional === false) {
help = '<i class="icon-exclamation-sign"></i> ' + help;
}
tr = '<tr>';
tr += '<td><input class="request-param-name span2" value="' + key + '"></td>';
tr += '<td><input class="request-param-value span4" value="' + value + '"><span class="help-block">' + help + '</span></td>';
tr += '<td><a href="#" class="request-param-remove">Remove</td>';
tr += '</tr>';
$paramRows.append(tr);
}
function serviceRequest(url) {
var serviceRequest, headers = {};
// Set the headers
if ($('#request-locale').val()) {
headers['Accept-Language'] = $('#request-locale').val();
}
if ($('#request-username').val()) {
headers['X-Requested-Username'] = $('#request-username').val();
}
headers['Accept'] = 'application/vnd.custom.api-v1.0';
serviceRequest = $.ajax({
type: $('#request-method').val(),
url: url,
cache: false,
headers: headers,
data: getSubmitParams()
}).always(function(data, status) {
parseServiceResponse(serviceRequest, status, data);
parseServiceResponseLog(serviceRequest);
});
}
function parseServiceResponse(xhr, status, data) {
$('#response').show();
$('#response-code').text(xhr.status + " (" + xhr.statusText + ")");
if (status !== "success") {
data = JSON.parse(data.responseText);
}
$('#response-body').text(JSON.stringify(data, null, 4));
}
function parseServiceResponseLog(xhr) {
var log = xhr.getResponseHeader('X-ChromePhp-Data');
if (!log) {
$('#response-log').hide();
return;
}
var tableRows = $('#response-log-messages > tbody');
$('#response-log').show();
tableRows.html('');
log = JSON.parse(atob(log));
for (var i in log.rows) {
var row = log.rows[i],
tr;
tr = '<tr><td>' + row[0] + '</td><td>' + row[3] + '</td>';
tr = tr + '<td><pre>' + JSON.stringify(row[1], null, 4) + '</pre></td></tr>';
tableRows.append(tr);
}
}
$(document).ready(function() {
var jqxhr = $.ajax({
url: '/discover',
dataType: 'json'
}).done(buildSearch);
loadDefaultParams();
$('#request-add-param').on('click', function() {
addParam('', '', '', null);
return false;
});
$(document).on('click', '.request-param-remove', function(event) {
$(event.currentTarget).parents('tr').remove();
return false;
});
$('form').on('submit', function() {
var url = $('#request-url').val();
if (!url) {
alert('Where is the URL???');
return false;
}
serviceRequest(url);
return false;
});
});
</script>
</body>
</html>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment