Compare commits

...
26 Commits
Author SHA1 Message Date
fehguy a2c1c18f75 Merge pull request #20 from aaronmccall/master
Correct issue where top-level (only one initial slash) paths have no name
2012-06-21 15:12:37 -07:00
Tony Tam ef810f5b2d added ignore of baseUrl if not defined or valid 2012-05-27 09:45:21 -07:00
Aaron McCall 6da589612c Corrected bug where top level (only one initial '/') api paths get no name. 2012-04-17 16:21:28 -07:00
Zeke Sikelianos 75d7758c53 Merge pull request #17 from chirino/master
Support a relative url path to the service & Support deeply nested resources.
2012-04-04 10:46:32 -07:00
Zeke Sikelianos d498255d7c Merge pull request #16 from acasademont/master
Make swagger-ui compatible with non-GET requests
2012-04-04 10:46:12 -07:00
Hiram Chirino bbcb230b20 Rebuilt using 'middleman build' 2012-03-19 09:57:38 -04:00
Hiram Chirino 4e1ad46699 Simplify how the name of the resource is constructed so that it can handle resources nested multiple directory levels in. 2012-03-19 09:56:34 -04:00
Hiram Chirino 0061046d3b Support a relative url path to the service. 2012-03-19 09:55:50 -04:00
Albert Casademont 1630abed59 Make swagger-ui compatible with non-GET requests 2012-03-07 18:54:10 +01:00
Albert Casademont 04d0b16815 Make swagger-ui compatible with non-GET requests 2012-03-07 18:52:07 +01:00
zeke b659da04a8 Added development instructions to the README 2012-02-27 14:42:11 -08:00
fehguy eb9e31eb89 Merge pull request #13 from ayush/master
now using basePath returned by the apis
2012-02-19 20:52:17 -08:00
Ayush Gupta 04dad6d23d using basePath from the apis 2012-02-19 18:23:26 -08:00
Zeke Sikelianos 9d12605517 Merge pull request #12 from ayush/master
Support for geting api listing from many variant paths
2012-02-16 17:25:57 -08:00
Ayush Gupta a76f27d7ab using different basePath variants to get API Listing 2012-02-16 16:08:45 -08:00
fehguy 1d6a4a0f3b Merge pull request #9 from zeke/master
Better detection of resource discovery URL components.
2012-01-25 11:00:28 -08:00
zeke a96cc2d39b Infer root resource name from baseUrl. 2012-01-25 10:29:49 -08:00
zeke 063a948c28 Updated sample discovery URL. 2012-01-25 10:28:52 -08:00
Zeke Sikelianos af73beacbc Merge pull request #8 from zeke/master
Updates to allow for presence or absence of .{format} string in discovery and resource URLs
2012-01-24 16:44:50 -08:00
zeke 4ce5b8fe9c new build 2012-01-24 16:30:26 -08:00
zeke 54209dd341 Support resource and discovery URLs with or without .json extension 2012-01-24 16:27:40 -08:00
zeke 2d25217dd2 small templating change 2012-01-24 13:43:10 -08:00
zeke 112576d66e Removed empty CSS rule 2012-01-24 13:42:36 -08:00
bpinkney 739219d306 Fixed conflicts 2012-01-24 11:44:41 -08:00
Tony Tam c71371d016 updated urls 2011-12-07 08:45:18 -08:00
rpidikiti b3b8dffba3 Updated UI to handle object structure for list allowed values 2011-10-19 16:08:27 -07:00
14 changed files with 391 additions and 218 deletions
+16 -6
View File
@@ -17,13 +17,23 @@ open swagger-ui-1.0/index.html
How to Improve It
-----------------
1. [Fork!](https://github.com/wordnik/swagger-ui)
1. Hack.
1. Submit a pull request.
First, create your own fork of [wordnik/swagger-ui](https://github.com/wordnik/swagger-ui)
Note: The HTML and CSS are converted from Haml and Sass, which means there are currently some development
dependencies. We are looking into revamping the source files and eliminating all development
dependencies by using [less.js](http://lesscss.org/)
To hack on swagger-ui, you'll need ruby. Then..
```bash
# Install the middleman gem:
gem install middleman
# Start up a development server on http://localhost:4567
middleman
# Edit the files in `/source`
# Then when you're ready to build, run:
middleman build
```
To share your changes, [submit a pull request](https://github.com/wordnik/swagger-ui/pull/new/master).
License
-------
+16 -18
View File
@@ -14,20 +14,20 @@
</head>
<body>
<div id='header'>
<a id="logo" href="http://swagger.wordnik.com">swagger</a>
<a href="http://swagger.wordnik.com" id="logo">swagger</a>
<form id='api_selector'>
<div class='input'><input placeholder="http://example.com/api" id="input_baseUrl" name="baseUrl" type="text" /></div>
<div class='input'><input placeholder="api_key" id="input_apiKey" name="apiKey" type="text" /></div>
<div class='input'><a id="explore" href="#">Explore</a></div>
<div class='input'><input type="text" placeholder="http://example.com/api" name="baseUrl" id="input_baseUrl" /></div>
<div class='input'><input type="text" placeholder="api_key" name="apiKey" id="input_apiKey" /></div>
<div class='input'><a href="#" id="explore">Explore</a></div>
</form>
</div>
<div class='container' id='resources_container'>
<ul id='resources'></ul>
</div>
<script id="resourceTemplate" type="text/x-jquery-tmpl"><li class='resource' id='resource_${name}'>
<script type="text/x-jquery-tmpl" id="resourceTemplate"><li class='resource' id='resource_${name}'>
<div class='heading'>
<h2>
<a href='#!/${name}' onclick="Docs.toggleEndpointListForResource('${name}');">${name}</a>
<a href='#!/${name}' onclick="Docs.toggleEndpointListForResource('${name}');">${path}</a>
</h2>
<ul class='options'>
<li>
@@ -51,11 +51,11 @@
<ul class='endpoints' id='${name}_endpoint_list' style='display:none'></ul>
</li>
</script>
<script id="apiTemplate" type="text/x-jquery-tmpl"><li class='endpoint'>
<script type="text/x-jquery-tmpl" id="apiTemplate"><li class='endpoint'>
<ul class='operations' id='${name}_endpoint_operations'></ul>
</li>
</script>
<script id="operationTemplate" type="text/x-jquery-tmpl"><li class='${httpMethodLowercase} operation' id='${apiName}_${nickname}_${httpMethod}'>
<script type="text/x-jquery-tmpl" id="operationTemplate"><li class='${httpMethodLowercase} operation' id='${apiName}_${nickname}_${httpMethod}'>
<div class='heading'>
<h3>
<span class='http_method'>
@@ -108,7 +108,7 @@
</div>
</li>
</script>
<script id="paramTemplate" type="text/x-jquery-tmpl"><tr>
<script type="text/x-jquery-tmpl" id="paramTemplate"><tr>
<td class='code'>${name}</td>
<td>
<input minlength='0' name='${name}' placeholder='' type='text' value='' />
@@ -116,15 +116,15 @@
<td width='500'>${description}</td>
</tr>
</script>
<script id="paramTemplateSelect" type="text/x-jquery-tmpl"><tr>
<script type="text/x-jquery-tmpl" id="paramTemplateSelect"><tr>
<td class='code'>${name}</td>
<td>
<select name='${name}'>
{{if required == false }}
<option selected='selected' value=''></option>
{{/if}}
{{each allowableValues}}
{{if $value == defaultValue && required == true}}
{{each allowableValues.values}}
{{if $value == defaultValue && required == true }}
<option selected='selected' value='${$value}'>${$value}</option>
{{else}}
<option value='${$value}'>${$value}</option>
@@ -135,7 +135,7 @@
<td width='500'>${description}</td>
</tr>
</script>
<script id="paramTemplateRequired" type="text/x-jquery-tmpl"><tr>
<script type="text/x-jquery-tmpl" id="paramTemplateRequired"><tr>
<td class='code required'>${name}</td>
<td>
<input class='required' minlength='1' name='${name}' placeholder='(required)' type='text' value='' />
@@ -145,13 +145,13 @@
</td>
</tr>
</script>
<script id="paramTemplateRequiredReadOnly" type="text/x-jquery-tmpl"><tr>
<script type="text/x-jquery-tmpl" id="paramTemplateRequiredReadOnly"><tr>
<td class='code required'>${name}</td>
<td>-</td>
<td width='500'>${description}</td>
</tr>
</script>
<script id="paramTemplateReadOnly" type="text/x-jquery-tmpl"><tr>
<script type="text/x-jquery-tmpl" id="paramTemplateReadOnly"><tr>
<td class='code'>${name}</td>
<td>-</td>
<td width='500'>${description}</td>
@@ -159,9 +159,7 @@
</script>
<div id='content_message'>
Enter the base URL of the API that you wish to explore, or try
<a onclick="$('#input_baseUrl').val('http://swagr.api.wordnik.com/v4'); apiSelectionController.showApi(); return false;" href="#">swagr.api.wordnik.com/v4</a>
or
<a onclick="$('#input_baseUrl').val('http://petstore.swagger.wordnik.com/api'); apiSelectionController.showApi(); return false;" href="#">petstore.swagger.wordnik.com/api</a>.
<a onclick="$('#input_baseUrl').val('http://petstore.swagger.wordnik.com/api/resources.json'); apiSelectionController.showApi(); return false;" href="#">petstore.swagger.wordnik.com/api/resources.json</a>
</div>
<p id='colophon' style='display:none'>
Sexy API documentation from
+7 -8
View File
@@ -2168,10 +2168,10 @@ function clippyCopiedCallback(a) {
}
// Logging function that accounts for browsers that don't have window.console
function log(m) {
if (window.console) console.log(m);
function log() {
if (window.console) console.log.apply(console,arguments);
}
var Docs = {
shebang: function() {
@@ -2530,11 +2530,10 @@ arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return th
// adeed by ayush
createAll: function(values){
values = this.fromJSON(values);
for (var i=0, il = values.length; i < il; i++) {
for (var i=0, il = values.length; i < il; i++) {
var record = values[i];
record.newRecord = false;
record.id = record.id || Spine.guid();
record.id = record.id || Spine.guid();
this.records[record.id] = record;
}
@@ -2578,7 +2577,7 @@ arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return th
},
last: function(){
var values = this.recordsValues()
var values = this.recordsValues();
var record = values[values.length - 1];
return(record && record.clone());
},
@@ -2627,7 +2626,7 @@ arguments),this._chain)}});j.prototype.chain=function(){this._chain=!0;return th
fromJSON: function(objects){
if ( !objects ) return;
if ( typeof objects == "string" )
objects = JSON.parse(objects)
objects = JSON.parse(objects);
if ( isArray(objects) ) {
var results = [];
for (var i=0; i < objects.length; i++)
+3 -3
View File
@@ -62,10 +62,10 @@ function clippyCopiedCallback(a) {
}
// Logging function that accounts for browsers that don't have window.console
function log(m) {
if (window.console) console.log(m);
function log() {
if (window.console) console.log.apply(console,arguments);
}
var Docs = {
shebang: function() {
+4 -5
View File
@@ -234,11 +234,10 @@
// adeed by ayush
createAll: function(values){
values = this.fromJSON(values);
for (var i=0, il = values.length; i < il; i++) {
for (var i=0, il = values.length; i < il; i++) {
var record = values[i];
record.newRecord = false;
record.id = record.id || Spine.guid();
record.id = record.id || Spine.guid();
this.records[record.id] = record;
}
@@ -282,7 +281,7 @@
},
last: function(){
var values = this.recordsValues()
var values = this.recordsValues();
var record = values[values.length - 1];
return(record && record.clone());
},
@@ -331,7 +330,7 @@
fromJSON: function(objects){
if ( !objects ) return;
if ( typeof objects == "string" )
objects = JSON.parse(objects)
objects = JSON.parse(objects);
if ( isArray(objects) ) {
var results = [];
for (var i=0; i < objects.length; i++)
+142 -62
View File
@@ -1,19 +1,18 @@
function SwaggerService(baseUrl, _apiKey, statusCallback) {
if (!baseUrl)
throw new Error("baseUrl must be passed while creating SwaggerService");
function SwaggerService(discoveryUrl, _apiKey, statusCallback) {
if (!discoveryUrl)
throw new Error("discoveryUrl must be passed while creating SwaggerService");
// constants
baseUrl = jQuery.trim(baseUrl);
if (baseUrl.length == 0)
throw new Error("baseUrl must be passed while creating SwaggerService");
discoveryUrl = jQuery.trim(discoveryUrl);
if (discoveryUrl.length == 0)
throw new Error("discoveryUrl must be passed while creating SwaggerService");
if (! (baseUrl.toLowerCase().indexOf("http:") == 0 || baseUrl.toLowerCase().indexOf("https:") == 0)) {
baseUrl = ("http://" + baseUrl);
if ( discoveryUrl.indexOf("/")!=0 && ! (discoveryUrl.toLowerCase().indexOf("http:") == 0 || discoveryUrl.toLowerCase().indexOf("https:") == 0)) {
discoveryUrl = ("http://" + discoveryUrl);
}
baseUrl = baseUrl + "/resources.json";
// log("using base url " + baseUrl);
var apiHost = baseUrl.substr(0, baseUrl.lastIndexOf("/"));
var rootResourcesApiName = baseUrl.substr(baseUrl.lastIndexOf("/") + 1, (baseUrl.lastIndexOf(".") - baseUrl.lastIndexOf("/") - 1));
var baseDiscoveryUrl = "";
var globalBasePath = "";
var formatString = ".{format}";
var statusListener = statusCallback;
var apiKey = _apiKey;
@@ -24,29 +23,30 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
if (apiKey.length > 0)
apiKeySuffix = "?api_key=" + apiKey;
}
// log("apiHost=" + apiHost);
// log("apiKey=" + apiKey);
// log("rootResourcesApiName = " + rootResourcesApiName);
// utility functions
function log(m) {
if (window.console) console.log(m);
function log() {
if (window.console) console.log.apply(console,arguments);
}
function error(m) {
if (window.console) console.log("ERROR: " + m);
log("ERROR: " + m);
}
function updateStatus(status) {
statusListener(status);
}
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
// make some models public
this.ApiResource = function() {
return ApiResource;
};
this.apiHost = function() {
return apiHost;
return globalBasePath;
};
this.formatString = function() {
@@ -58,22 +58,24 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
ApiResource.include({
path_json: null,
path_xml: null,
init: function(atts) {
if (atts) this.load(atts);
this.name = this.path.replace(".{format}", "").replace(/\//g, "_");
this.path_json = this.path.replace("{format}", "json");
this.path_xml = this.path.replace("{format}", "xml");
this.baseUrl = apiHost;
//execluded 9 letters to remove .{format} from name
this.name = this.path.substr(1, this.path.length - formatString.length - 1);
this.baseUrl = globalBasePath;
this.apiList = Api.sub();
this.modelList = ApiModel.sub();
},
addApis: function(apiObjects) {
addApis: function(apiObjects, basePath) {
// log("apiObjects: %o", apiObjects);
this.apiList.createAll(apiObjects);
this.apiList.each(function(api) {
api.setBaseUrl(basePath);
});
},
addModel: function(modelObject) {
this.modelList.create(modelObject);
},
@@ -89,7 +91,8 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
init: function(atts) {
if (atts) this.load(atts);
this.baseUrl = apiHost;
var sep = this.path.lastIndexOf("/");
this.name = this.path.substr(0, sep).replace(".{format}", "").replace(/\//g, "_");
var secondPathSeperatorIndex = this.path.indexOf("/", 1);
if (secondPathSeperatorIndex > 0) {
@@ -98,21 +101,9 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
// log(this.path + ":: " + prefix + "..." + suffix);
this.path_json = prefix.replace("{format}", "json") + suffix;
this.path_xml = prefix.replace("{format}", "xml") + suffix;;
if (this.path.indexOf("/") == 0) {
this.name = this.path.substr(1, secondPathSeperatorIndex - formatString.length - 1);
} else {
this.name = this.path.substr(0, secondPathSeperatorIndex - formatString.length - 1);
}
} else {
this.path_json = this.path.replace("{format}", "json");
this.path_xml = this.path.replace("{format}", "xml");
if (this.path.indexOf("/") == 0) {
this.name = this.path.substr(1, this.path.length - formatString.length - 1);
} else {
this.name = this.path.substr(0, this.path.length - formatString.length - 1);
}
}
var value = this.operations;
@@ -135,6 +126,13 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
},
setBaseUrl: function(u) {
this.baseUrl = u;
this.operations.each(function(o) {
o.baseUrl = u;
});
},
toString: function() {
var opsString = "";
for (var i = 0; i < this.operations.all().length; i++) {
@@ -156,7 +154,6 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
init: function(atts) {
if (atts) this.load(atts);
this.baseUrl = apiHost;
this.httpMethodLowercase = this.httpMethod.toLowerCase();
var value = this.parameters;
@@ -219,17 +216,57 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
if (atts) this.load(atts);
this.name = this.name || this.dataType;
if(this.allowableValues){
var value = this.allowableValues;
if(value.valueType == "LIST"){
this.allowableValues = AllowableListValues.sub();
} else if (value.valueType == "RANGE"){
this.allowableValues = AllowableRangeValues.sub();
}
if (value) this.allowableValues = this.allowableValues.create(value);
}
},
toString: function() {
if (this.allowableValues && this.allowableValues.length > 0)
return this.name + ": " + this.dataType + " [" + this.allowableValues + "]";
if (this.allowableValues)
return this.name + ": " + this.dataType + " " + this.allowableValues;
else
return this.name + ": " + this.dataType;
}
});
var AllowableListValues = Spine.Model.setup("AllowableListValues", ["valueType", "values"]);
AllowableListValues.include({
init: function(atts) {
if (atts) this.load(atts);
this.name = "allowableValues";
},
toString: function() {
if (this.values)
return "["+this.values+"]";
else
return "";
}
});
var AllowableRangeValues = Spine.Model.setup("AllowableRangeValues", ["valueType", "inclusive", "min", "max"]);
AllowableRangeValues.include({
init: function(atts) {
if (atts) this.load(atts);
this.name = "allowableValues";
},
toString: function() {
if (this.min && this.max)
return "[" + min + "," + max + "]";
else
return "";
}
});
// Model: ApiModel
var ApiModel = Spine.Model.setup("ApiModel", ["id", "fields"]);
ApiModel.include({
@@ -314,6 +351,8 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
var ModelController = Spine.Controller.create({
countLoaded: 0,
proxied: ["fetchResources", "loadResources", "apisLoaded", "modelsLoaded"],
discoveryUrlList: [],
discoveryUrlListCursor: 0,
init: function() {
// log("ModelController.init");
@@ -322,26 +361,63 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
},
fetchEndpoints: function() {
var controller = this;
updateStatus("Fetching API List...");
$.getJSON(apiHost + "/" + rootResourcesApiName + ".json" + apiKeySuffix,
function(response) {
//log(response);
ApiResource.createAll(response.apis);
// get rid of the root resource list api since we're not going to document that
var obj = ApiResource.findByAttribute("name", rootResourcesApiName);
if (obj)
obj.destroy();
controller.fetchResources();
});
baseDiscoveryUrl = endsWith(discoveryUrl, "/") ? discoveryUrl.substr(0, discoveryUrl.length - 1) : discoveryUrl;
if(endsWith(baseDiscoveryUrl, "/resources.json"))
baseDiscoveryUrl = baseDiscoveryUrl.substr(0, baseDiscoveryUrl.length - "/resources.json".length);
else if(endsWith(baseDiscoveryUrl, "/resources"))
baseDiscoveryUrl = baseDiscoveryUrl.substr(0, baseDiscoveryUrl.length - "/resources".length);
this.discoveryUrlList.push(discoveryUrl);
this.discoveryUrlList.push(baseDiscoveryUrl);
this.discoveryUrlList.push(baseDiscoveryUrl + "/resources.json");
this.discoveryUrlList.push(baseDiscoveryUrl + "/resources");
log("Will try the following urls to discover api endpoints:")
for(var i = 0; i < this.discoveryUrlList.length; i++)
log(" > " + this.discoveryUrlList[i]);
this.fetchEndpointsSeq();
},
fetchResources: function() {
//log("fetchResources");
fetchEndpointsSeq: function() {
var controller = this;
if(this.discoveryUrlListCursor < this.discoveryUrlList.length) {
var url = this.discoveryUrlList[this.discoveryUrlListCursor++]
updateStatus("Fetching API List from " + url);
log("Trying url " + url);
$.getJSON(url + apiKeySuffix, function(response) {
})
.success(function(response) {
if(response.basePath && response.basePath.slice(0,4) == "http") {
log("Setting globalBasePath to " + response.basePath);
globalBasePath = response.basePath;
}
else {
globalBasePath = baseDiscoveryUrl;
}
ApiResource.createAll(response.apis);
controller.fetchResources(response.basePath);
})
.error(function(response) {
controller.fetchEndpointsSeq();
});
} else {
log ('Error with resource discovery. Exhaused all possible endpoint urls');
var urlsTried = "";
for(var i = 0; i < this.discoveryUrlList.length; i++) {
urlsTried = urlsTried + "<br/>" + this.discoveryUrlList[i];
}
updateStatus("Unable to fetch API Listing. Tried the following urls:" + urlsTried);
}
},
fetchResources: function(basePath) {
log("fetchResources: basePath = " + basePath);
//ApiResource.logAll();
for (var i = 0; i < ApiResource.all().length; i++) {
var apiResource = ApiResource.all()[i];
@@ -352,7 +428,9 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
fetchResource: function(apiResource) {
var controller = this;
updateStatus("Fetching " + apiResource.name + "...");
$.getJSON(apiHost + apiResource.path_json + apiKeySuffix,
var resourceUrl = globalBasePath + apiResource.path_json + apiKeySuffix;
log("resourceUrl: %o", resourceUrl);
$.getJSON(resourceUrl,
function(response) {
controller.loadResources(response, apiResource);
});
@@ -362,7 +440,9 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
try {
this.countLoaded++;
// log(response);
apiResource.addApis(response.apis);
if (response.apis) {
apiResource.addApis(response.apis, response.basePath);
}
// updateStatus("Parsed Apis");
//log(response.models);
if (response.models) {
@@ -378,7 +458,7 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
updateStatus();
} finally {
if (this.countLoaded == ApiResource.count()) {
log("all models/api loaded");
// log("all models/api loaded");
ApiResource.trigger("refresh");
}
}
+20 -13
View File
@@ -1,8 +1,6 @@
jQuery(function($) {
// this.baseUrl = "http://swagr.api.wordnik.com/v4";
// this.baseUrl = "http://petstore.swagger.wordnik.com/api";
// this.baseUrl = "http://petstore.swagger.wordnik.com/api/resources.json";
// this.apiKey = "special-key";
var ApiSelectionController = Spine.Controller.create({
@@ -32,6 +30,22 @@ jQuery(function($) {
apiSelectionController.adaptToScale();
});
this.handleEnter();
},
handleEnter: function(){
var self = this;
var submit = function() {
self.showApi();
};
$('#input_baseUrl').keydown(function(e) {
if(e.which != 13) return;
submit();
});
$('#input_apiKey').keydown(function(e) {
if(e.which != 13) return;
submit();
});
},
adaptToScale: function() {
@@ -44,10 +58,10 @@ jQuery(function($) {
// $('#input_baseUrl').width($('#input_baseUrl').width() + free_width - 50);
},
slapOn: function() {
// messageController.showMessage("Please enter the base URL of the API that you wish to explore.");
$("#content_message").show();
$("#resources_container").hide();
this.showApi();
},
@@ -66,11 +80,9 @@ jQuery(function($) {
if (baseUrl.length == 0) {
$("#input_baseUrl").wiggle();
} else {
if (this.supportsLocalStorage()) {
localStorage.setItem("com.wordnik.swagger.ui.apiKey", apiKey);
localStorage.setItem("com.wordnik.swagger.ui.baseUrl", baseUrl);
}
var resourceListController = ResourceListController.init({
baseUrl: baseUrl,
@@ -165,14 +177,9 @@ jQuery(function($) {
init: function() {
this.render();
this.apiResource = this.item;
this.apiList = this.apiResource.apiList;
this.modelList = this.apiResource.modelList;
// log("------------- apiResource : " + this.apiResource.name);
// this.apiList.logAll();
// this.modelList.logAll();
this.apiList.each(this.renderApi);
},
@@ -237,7 +244,7 @@ jQuery(function($) {
templateName: function(){
var n = "#paramTemplate";
if (this.allowableValues && this.allowableValues.length > 0) {
if (this.allowableValues && this.allowableValues.valueType == "LIST") {
n += "Select";
} else {
if (this.required) n += "Required";
@@ -358,7 +365,7 @@ jQuery(function($) {
});
// Attach controller to window
// Attach controller to window*
window.apiSelectionController = ApiSelectionController.init();
if (this.baseUrl) {
+2 -2
View File
@@ -5,7 +5,7 @@
%li.resource{:id => "resource_${name}"}
.heading
%h2
%a{:href => "#!/${name}", :onclick => "Docs.toggleEndpointListForResource('${name}');"} ${name}
%a{:href => "#!/${name}", :onclick => "Docs.toggleEndpointListForResource('${name}');"} ${path}
%ul.options
%li
%a{:href => "#!/${name}", :id => "endpointListTogger_${name}", :onclick => "Docs.toggleEndpointListForResource('${name}');"} Show/Hide
@@ -80,7 +80,7 @@
{{if required == false }}
%option{:value => "", :selected => 'selected'}
{{/if}}
{{each allowableValues}}
{{each allowableValues.values}}
{{if $value == defaultValue && required == true }}
%option{:value => "${$value}", :selected => 'selected'} ${$value}
{{else}}
+3 -3
View File
@@ -62,10 +62,10 @@ function clippyCopiedCallback(a) {
}
// Logging function that accounts for browsers that don't have window.console
function log(m) {
if (window.console) console.log(m);
function log() {
if (window.console) console.log.apply(console,arguments);
}
var Docs = {
shebang: function() {
+4 -5
View File
@@ -234,11 +234,10 @@
// adeed by ayush
createAll: function(values){
values = this.fromJSON(values);
for (var i=0, il = values.length; i < il; i++) {
for (var i=0, il = values.length; i < il; i++) {
var record = values[i];
record.newRecord = false;
record.id = record.id || Spine.guid();
record.id = record.id || Spine.guid();
this.records[record.id] = record;
}
@@ -282,7 +281,7 @@
},
last: function(){
var values = this.recordsValues()
var values = this.recordsValues();
var record = values[values.length - 1];
return(record && record.clone());
},
@@ -331,7 +330,7 @@
fromJSON: function(objects){
if ( !objects ) return;
if ( typeof objects == "string" )
objects = JSON.parse(objects)
objects = JSON.parse(objects);
if ( isArray(objects) ) {
var results = [];
for (var i=0; i < objects.length; i++)
+142 -62
View File
@@ -1,19 +1,18 @@
function SwaggerService(baseUrl, _apiKey, statusCallback) {
if (!baseUrl)
throw new Error("baseUrl must be passed while creating SwaggerService");
function SwaggerService(discoveryUrl, _apiKey, statusCallback) {
if (!discoveryUrl)
throw new Error("discoveryUrl must be passed while creating SwaggerService");
// constants
baseUrl = jQuery.trim(baseUrl);
if (baseUrl.length == 0)
throw new Error("baseUrl must be passed while creating SwaggerService");
discoveryUrl = jQuery.trim(discoveryUrl);
if (discoveryUrl.length == 0)
throw new Error("discoveryUrl must be passed while creating SwaggerService");
if (! (baseUrl.toLowerCase().indexOf("http:") == 0 || baseUrl.toLowerCase().indexOf("https:") == 0)) {
baseUrl = ("http://" + baseUrl);
if ( discoveryUrl.indexOf("/")!=0 && ! (discoveryUrl.toLowerCase().indexOf("http:") == 0 || discoveryUrl.toLowerCase().indexOf("https:") == 0)) {
discoveryUrl = ("http://" + discoveryUrl);
}
baseUrl = baseUrl + "/resources.json";
// log("using base url " + baseUrl);
var apiHost = baseUrl.substr(0, baseUrl.lastIndexOf("/"));
var rootResourcesApiName = baseUrl.substr(baseUrl.lastIndexOf("/") + 1, (baseUrl.lastIndexOf(".") - baseUrl.lastIndexOf("/") - 1));
var baseDiscoveryUrl = "";
var globalBasePath = "";
var formatString = ".{format}";
var statusListener = statusCallback;
var apiKey = _apiKey;
@@ -24,29 +23,30 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
if (apiKey.length > 0)
apiKeySuffix = "?api_key=" + apiKey;
}
// log("apiHost=" + apiHost);
// log("apiKey=" + apiKey);
// log("rootResourcesApiName = " + rootResourcesApiName);
// utility functions
function log(m) {
if (window.console) console.log(m);
function log() {
if (window.console) console.log.apply(console,arguments);
}
function error(m) {
if (window.console) console.log("ERROR: " + m);
log("ERROR: " + m);
}
function updateStatus(status) {
statusListener(status);
}
function endsWith(str, suffix) {
return str.indexOf(suffix, str.length - suffix.length) !== -1;
}
// make some models public
this.ApiResource = function() {
return ApiResource;
};
this.apiHost = function() {
return apiHost;
return globalBasePath;
};
this.formatString = function() {
@@ -58,22 +58,24 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
ApiResource.include({
path_json: null,
path_xml: null,
init: function(atts) {
if (atts) this.load(atts);
this.name = this.path.replace(".{format}", "").replace(/\//g, "_");
this.path_json = this.path.replace("{format}", "json");
this.path_xml = this.path.replace("{format}", "xml");
this.baseUrl = apiHost;
//execluded 9 letters to remove .{format} from name
this.name = this.path.substr(1, this.path.length - formatString.length - 1).replace(/\//g, "_");
this.baseUrl = globalBasePath;
this.apiList = Api.sub();
this.modelList = ApiModel.sub();
},
addApis: function(apiObjects) {
addApis: function(apiObjects, basePath) {
// log("apiObjects: %o", apiObjects);
this.apiList.createAll(apiObjects);
this.apiList.each(function(api) {
api.setBaseUrl(basePath);
});
},
addModel: function(modelObject) {
this.modelList.create(modelObject);
},
@@ -89,7 +91,8 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
init: function(atts) {
if (atts) this.load(atts);
this.baseUrl = apiHost;
var sep = this.path.lastIndexOf("/");
this.name = (sep!==0?this.path.substr(0, sep):this.path).replace(".{format}", "").replace(/\//g, "_");
var secondPathSeperatorIndex = this.path.indexOf("/", 1);
if (secondPathSeperatorIndex > 0) {
@@ -98,21 +101,9 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
// log(this.path + ":: " + prefix + "..." + suffix);
this.path_json = prefix.replace("{format}", "json") + suffix;
this.path_xml = prefix.replace("{format}", "xml") + suffix;;
if (this.path.indexOf("/") == 0) {
this.name = this.path.substr(1, secondPathSeperatorIndex - formatString.length - 1);
} else {
this.name = this.path.substr(0, secondPathSeperatorIndex - formatString.length - 1);
}
} else {
this.path_json = this.path.replace("{format}", "json");
this.path_xml = this.path.replace("{format}", "xml");
if (this.path.indexOf("/") == 0) {
this.name = this.path.substr(1, this.path.length - formatString.length - 1);
} else {
this.name = this.path.substr(0, this.path.length - formatString.length - 1);
}
}
var value = this.operations;
@@ -135,6 +126,13 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
},
setBaseUrl: function(u) {
this.baseUrl = u;
this.operations.each(function(o) {
o.baseUrl = u;
});
},
toString: function() {
var opsString = "";
for (var i = 0; i < this.operations.all().length; i++) {
@@ -156,7 +154,6 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
init: function(atts) {
if (atts) this.load(atts);
this.baseUrl = apiHost;
this.httpMethodLowercase = this.httpMethod.toLowerCase();
var value = this.parameters;
@@ -219,17 +216,57 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
if (atts) this.load(atts);
this.name = this.name || this.dataType;
if(this.allowableValues){
var value = this.allowableValues;
if(value.valueType == "LIST"){
this.allowableValues = AllowableListValues.sub();
} else if (value.valueType == "RANGE"){
this.allowableValues = AllowableRangeValues.sub();
}
if (value) this.allowableValues = this.allowableValues.create(value);
}
},
toString: function() {
if (this.allowableValues && this.allowableValues.length > 0)
return this.name + ": " + this.dataType + " [" + this.allowableValues + "]";
if (this.allowableValues)
return this.name + ": " + this.dataType + " " + this.allowableValues;
else
return this.name + ": " + this.dataType;
}
});
var AllowableListValues = Spine.Model.setup("AllowableListValues", ["valueType", "values"]);
AllowableListValues.include({
init: function(atts) {
if (atts) this.load(atts);
this.name = "allowableValues";
},
toString: function() {
if (this.values)
return "["+this.values+"]";
else
return "";
}
});
var AllowableRangeValues = Spine.Model.setup("AllowableRangeValues", ["valueType", "inclusive", "min", "max"]);
AllowableRangeValues.include({
init: function(atts) {
if (atts) this.load(atts);
this.name = "allowableValues";
},
toString: function() {
if (this.min && this.max)
return "[" + min + "," + max + "]";
else
return "";
}
});
// Model: ApiModel
var ApiModel = Spine.Model.setup("ApiModel", ["id", "fields"]);
ApiModel.include({
@@ -314,6 +351,8 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
var ModelController = Spine.Controller.create({
countLoaded: 0,
proxied: ["fetchResources", "loadResources", "apisLoaded", "modelsLoaded"],
discoveryUrlList: [],
discoveryUrlListCursor: 0,
init: function() {
// log("ModelController.init");
@@ -322,26 +361,63 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
},
fetchEndpoints: function() {
var controller = this;
updateStatus("Fetching API List...");
$.getJSON(apiHost + "/" + rootResourcesApiName + ".json" + apiKeySuffix,
function(response) {
//log(response);
ApiResource.createAll(response.apis);
// get rid of the root resource list api since we're not going to document that
var obj = ApiResource.findByAttribute("name", rootResourcesApiName);
if (obj)
obj.destroy();
controller.fetchResources();
});
baseDiscoveryUrl = endsWith(discoveryUrl, "/") ? discoveryUrl.substr(0, discoveryUrl.length - 1) : discoveryUrl;
if(endsWith(baseDiscoveryUrl, "/resources.json"))
baseDiscoveryUrl = baseDiscoveryUrl.substr(0, baseDiscoveryUrl.length - "/resources.json".length);
else if(endsWith(baseDiscoveryUrl, "/resources"))
baseDiscoveryUrl = baseDiscoveryUrl.substr(0, baseDiscoveryUrl.length - "/resources".length);
this.discoveryUrlList.push(discoveryUrl);
this.discoveryUrlList.push(baseDiscoveryUrl);
this.discoveryUrlList.push(baseDiscoveryUrl + "/resources.json");
this.discoveryUrlList.push(baseDiscoveryUrl + "/resources");
log("Will try the following urls to discover api endpoints:")
for(var i = 0; i < this.discoveryUrlList.length; i++)
log(" > " + this.discoveryUrlList[i]);
this.fetchEndpointsSeq();
},
fetchResources: function() {
//log("fetchResources");
fetchEndpointsSeq: function() {
var controller = this;
if(this.discoveryUrlListCursor < this.discoveryUrlList.length) {
var url = this.discoveryUrlList[this.discoveryUrlListCursor++]
updateStatus("Fetching API List from " + url);
log("Trying url " + url);
$.getJSON(url + apiKeySuffix, function(response) {
})
.success(function(response) {
if(response.basePath && response.basePath.slice(0,4) == "http") {
log("Setting globalBasePath to " + response.basePath);
globalBasePath = response.basePath;
}
else {
globalBasePath = baseDiscoveryUrl;
}
ApiResource.createAll(response.apis);
controller.fetchResources(response.basePath);
})
.error(function(response) {
controller.fetchEndpointsSeq();
});
} else {
log ('Error with resource discovery. Exhaused all possible endpoint urls');
var urlsTried = "";
for(var i = 0; i < this.discoveryUrlList.length; i++) {
urlsTried = urlsTried + "<br/>" + this.discoveryUrlList[i];
}
updateStatus("Unable to fetch API Listing. Tried the following urls:" + urlsTried);
}
},
fetchResources: function(basePath) {
log("fetchResources: basePath = " + basePath);
//ApiResource.logAll();
for (var i = 0; i < ApiResource.all().length; i++) {
var apiResource = ApiResource.all()[i];
@@ -352,7 +428,9 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
fetchResource: function(apiResource) {
var controller = this;
updateStatus("Fetching " + apiResource.name + "...");
$.getJSON(apiHost + apiResource.path_json + apiKeySuffix,
var resourceUrl = globalBasePath + apiResource.path_json + apiKeySuffix;
log("resourceUrl: %o", resourceUrl);
$.getJSON(resourceUrl,
function(response) {
controller.loadResources(response, apiResource);
});
@@ -362,7 +440,9 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
try {
this.countLoaded++;
// log(response);
apiResource.addApis(response.apis);
if (response.apis) {
apiResource.addApis(response.apis, response.basePath);
}
// updateStatus("Parsed Apis");
//log(response.models);
if (response.models) {
@@ -378,7 +458,7 @@ function SwaggerService(baseUrl, _apiKey, statusCallback) {
updateStatus();
} finally {
if (this.countLoaded == ApiResource.count()) {
log("all models/api loaded");
// log("all models/api loaded");
ApiResource.trigger("refresh");
}
}
+30 -26
View File
@@ -1,8 +1,6 @@
jQuery(function($) {
// this.baseUrl = "http://swagr.api.wordnik.com/v4";
// this.baseUrl = "http://petstore.swagger.wordnik.com/api";
// this.baseUrl = "http://petstore.swagger.wordnik.com/api/resources.json";
// this.apiKey = "special-key";
var ApiSelectionController = Spine.Controller.create({
@@ -32,6 +30,22 @@ jQuery(function($) {
apiSelectionController.adaptToScale();
});
this.handleEnter();
},
handleEnter: function(){
var self = this;
var submit = function() {
self.showApi();
};
$('#input_baseUrl').keydown(function(e) {
if(e.which != 13) return;
submit();
});
$('#input_apiKey').keydown(function(e) {
if(e.which != 13) return;
submit();
});
},
adaptToScale: function() {
@@ -44,10 +58,10 @@ jQuery(function($) {
// $('#input_baseUrl').width($('#input_baseUrl').width() + free_width - 50);
},
slapOn: function() {
// messageController.showMessage("Please enter the base URL of the API that you wish to explore.");
$("#content_message").show();
$("#resources_container").hide();
this.showApi();
},
@@ -66,11 +80,9 @@ jQuery(function($) {
if (baseUrl.length == 0) {
$("#input_baseUrl").wiggle();
} else {
if (this.supportsLocalStorage()) {
localStorage.setItem("com.wordnik.swagger.ui.apiKey", apiKey);
localStorage.setItem("com.wordnik.swagger.ui.baseUrl", baseUrl);
}
var resourceListController = ResourceListController.init({
baseUrl: baseUrl,
@@ -165,14 +177,9 @@ jQuery(function($) {
init: function() {
this.render();
this.apiResource = this.item;
this.apiList = this.apiResource.apiList;
this.modelList = this.apiResource.modelList;
// log("------------- apiResource : " + this.apiResource.name);
// this.apiList.logAll();
// this.modelList.logAll();
this.apiList.each(this.renderApi);
},
@@ -237,7 +244,7 @@ jQuery(function($) {
templateName: function(){
var n = "#paramTemplate";
if (this.allowableValues && this.allowableValues.length > 0) {
if (this.allowableValues && this.allowableValues.valueType == "LIST") {
n += "Select";
} else {
if (this.required) n += "Required";
@@ -277,8 +284,6 @@ jQuery(function($) {
for (var p = 0; p < this.operation.parameters.count(); p++) {
var param = Param.init(this.operation.parameters.all()[p]);
// Only GET operations display forms..
param.readOnly = !this.isGetOperation;
param.cleanup();
$(param.templateName()).tmpl(param).appendTo(operationParamsContainer);
@@ -286,14 +291,7 @@ jQuery(function($) {
}
var submitButtonId = this.elementScope + "_content_sandbox_response_button";
if (this.isGetOperation) {
$(submitButtonId).click(this.submitOperation);
} else {
$(submitButtonId).hide();
var valueHeader = this.elementScope + "_value_header";
$(valueHeader).html("Default Value");
}
$(submitButtonId).click(this.submitOperation);
},
@@ -320,9 +318,15 @@ jQuery(function($) {
});
if (error_free) {
var invocationUrl = this.operation.invocationUrl(form.serializeArray());
$(".request_url", this.elementScope + "_content_sandbox_response").html("<pre>" + invocationUrl + "</pre>");
$.getJSON(invocationUrl, this.showResponse).complete(this.showCompleteStatus).error(this.showErrorStatus);
var invocationData = this.operation.invocationData(form.serializeArray());
$(".request_url", this.elementScope + "_content_sandbox_response").html("<pre>" + invocationData.url + "</pre>");
$.ajax({
url: invocationData.url,
dataType: 'json',
data: invocationData.queryParams,
success: this.showResponse,
type: this.operation.httpMethod.toUpperCase()
}).complete(this.showCompleteStatus).error(this.showErrorStatus);
}
},
@@ -358,7 +362,7 @@ jQuery(function($) {
});
// Attach controller to window
// Attach controller to window*
window.apiSelectionController = ApiSelectionController.init();
if (this.baseUrl) {
+2 -4
View File
@@ -29,10 +29,8 @@
#content_message
Enter the base URL of the API that you wish to explore, or try
= link_to("swagr.api.wordnik.com/v4", "#", :onclick => "$('#input_baseUrl').val('http://swagr.api.wordnik.com/v4'); apiSelectionController.showApi(); return false;")
or
= link_to("petstore.swagger.wordnik.com/api", "#", :onclick => "$('#input_baseUrl').val('http://petstore.swagger.wordnik.com/api'); apiSelectionController.showApi(); return false;") + "."
= link_to("petstore.swagger.wordnik.com/api/resources.json", "#", :onclick => "$('#input_baseUrl').val('http://petstore.swagger.wordnik.com/api/resources.json'); apiSelectionController.showApi(); return false;")
%p#colophon{:style => 'display:none'}
Sexy API documentation from
= link_to("Swagger", "http://swagger.wordnik.com") + "."
= link_to("Swagger", "http://swagger.wordnik.com") + "."
-1
View File
@@ -226,7 +226,6 @@ form
display: block
clear: both
&.select
label
select
display: block
clear: both