UNPKG

steady-api

Version:

Configurable REST API built with Express and TypeScript

304 lines (251 loc) 12.3 kB
<!doctype html> <html lang="en"> <head> <meta charset="utf-8"> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"> <meta name="description" content=""> <meta name="author" content=""> <title></title> <link href="/docs-assets/css/bootstrap.min.css" rel="stylesheet"> <link href="/docs-assets/css/custom.css" rel="stylesheet"> </head> <body> <div id="app"> <header> <nav class="navbar navbar-expand-md navbar-dark fixed-top bg-dark"> <a class="navbar-brand" href="/">{{ apiName }}</a> </nav> </header> <div class="container-fluid"> <div class="row"> <nav class="col-sm-4 col-md-3 d-none d-sm-block bg-light sidebar"> <ul class="nav nav-pills flex-column"> <li class="nav-item"> <a class="nav-link active" :href="docsRoot">Routes</a> </li> <li class="nav-item"> <a class="nav-link" :href="docsRoot + '/types'">Type definitions</a> </li> </ul> <ul class="nav nav-pills flex-column"> <li v-for="route in routes" class="nav-item"> <a class="nav-link" v-bind:href="'#'+route.method+'-'+route.url"> <span class="badge float-right" v-bind:class="{ 'badge-success': route.method == 'GET', 'badge-warning': route.method == 'POST', 'badge-info': route.method == 'PUT', 'badge-danger': route.method == 'DELETE' }">{{ route.method }}</span> {{ route.url }} </a> </li> </ul> </nav> <main role="main" class="col-sm-8 ml-sm-auto col-md-9 pt-3"> <h1 class="pb-3 mb-3 mt-5">{{ apiName }}</h1> <div class="card mb-2" v-for="route in routes"> <div class="card-body"> <h2 class="card-title" :id="route.method+'-'+route.url"> <span class="badge float-right" v-bind:class="{ 'badge-success': route.method == 'GET', 'badge-warning': route.method == 'POST', 'badge-info': route.method == 'PUT', 'badge-danger': route.method == 'DELETE' }">{{ route.method }}</span> {{ route.url }} </h2> <p>{{ route.description }}</p> <div class="table-responsive"> <table class="table table-striped"> <thead> <tr> <th>Parameter</th> <th>Required</th> <th>Default</th> <th>Type</th> <th>Example</th> </tr> </thead> <tbody> <tr v-for="param in route.params"> <td>{{ param.name }}</td> <td> <code>{{ param.required }}</code> </td> <td> <pre class="pre-scrollable">{{ param.default }}</pre> </td> <td><a v-bind:href="docsRoot + '/' + param.type">{{ param.type }}</a></td> <td> <pre v-if="param.type != 'enum'" class="pre-scrollable">{{ param.example }}</pre> <div v-if="param.type == 'enum'"> <em>Accepted values:</em><br /> <pre class="pre-scrollable">{{ param.values.join(", ") }}</pre> </div> </td> </tr> </tbody> </table> <button v-on:click="showForm" v-bind:data-route="route.method+'-'+route.url" class="btn btn-sm btn-info">Try it out!</button> </div> <div class="card mt-3" v-if="formRoute == route.method+'-'+route.url"> <div class="card-body"> <form v-on:submit="apiRequest"> <div v-for="param in route.params"> <div v-if="param.complex" class="form-group"> <label class="font-weight-bold">{{ param.name }} <span v-if="param.required" class="badge badge-info">Required</span></label> <textarea class="form-control" v-bind:ref="param.name" v-bind:id="param.name" rows="3"></textarea> </div> <div v-else-if="param.type == 'enum'" class="form-group"> <label class="font-weight-bold">{{ param.name }} <span v-if="param.required" class="badge badge-info">Required</span></label> <select class="form-control" v-bind:ref="param.name" v-bind:id="param.name"> <option v-if="!param.required" value="">- select one -</option> <option :selected="value == param.default" v-for="value in param.values" v-bind:value="value">{{ value }}</option> </select> </div> <div v-else-if="param.type == 'boolean'" class="form-group"> <label class="font-weight-bold">{{ param.name }} <span v-if="param.required" class="badge badge-info">Required</span></label> <select class="form-control" v-bind:ref="param.name" v-bind:id="param.name"> <option v-if="!param.required" value="">- select one -</option> <option value="true">TRUE</option> <option value="false">FALSE</option> </select> </div> <div v-else-if="param.type == 'file'" class="form-group"> <label class="font-weight-bold">{{ param.name }} <span v-if="param.required" class="badge badge-info">Required</span></label> <input type="file" class="form-control" v-bind:ref="param.name" v-bind:id="param.name"> </div> <div v-else class="form-group"> <label class="font-weight-bold">{{ param.name }} <span v-if="param.urlParam" class="badge badge-warning">URL Parameter</span> <span v-if="param.required" class="badge badge-info">Required</span></label> <input type="text" class="form-control" v-bind:urlParam="param.urlParam" v-bind:ref="param.name" v-bind:id="param.name" v-bind:placeholder="param.default ? param.default : param.example"> <small class="form-text text-muted">Example: <em>{{ param.example }}</em></small> <small v-if="param.default" class="form-text text-muted">Default: <em>{{ param.default }}</em></small> </div> </div> <input type="hidden" ref="_method" id="_method" v-bind:value="route.method"> <input type="hidden" ref="_url" id="_url" v-bind:value="route.url"> <button type="submit" class="btn btn-primary">Submit</button> </form> <div class="mt-3 alert" v-bind:class="{ 'alert-success': !apiError, 'alert-danger': apiError }" v-if="formRoute == route.method+'-'+route.url && apiResponse"> <pre class="pre-scrollable"><code>{{ apiResponse }}</code></pre> </div> </div> </div> </div> </div> </main> </div> <div class="row"> <div class="col-sm-4 col-md-3 offset-sm-4 offset-md-3"></div> <div class="col-sm-8 ml-sm-auto col-md-9 pt-3"> <p class="text-muted text-center" v-html="meta.copyright"></p> </div> </div> </div> </div> <script src="/docs-assets/js/jquery-3.2.1.slim.min.js"></script> <script src="/docs-assets/js/popper.min.js"></script> <script src="/docs-assets/js/bootstrap.min.js"></script> <script src='/docs-assets/js/vue.js'></script> <script src='/docs-assets/js/vue-resource.js'></script> <script> var g; var routes = new Vue({ el: '#app', data: { routes: [], types: [], formRoute: "", apiResponse: null, apiError: false, docsRoot: window.location.pathname, apiName: '', meta: {} }, beforeCreate: function() { this.$http.get('/documentation.json') .then( function(response) { this.routes = response.body.routes; this.types = response.body.types; window.document.title = response.body.name; this.apiName = response.body.name; this.meta = response.body.meta; }, function(error) { console.error(response.error); } ) }, methods: { showForm: function(event) { var routeId = event.currentTarget.getAttribute('data-route'); this.formRoute = this.formRoute === routeId ? "" : routeId; this.apiResponse = null; }, apiRequest: function(event) { event.preventDefault(); // data defaults this.apiResponse = null; this.apiError = false; // capture form data var urlParamData = {}; var data = new FormData(); var target = event.target; var paramKeys = Object.keys(target).reverse(); var url = null; var method = null; paramKeys.forEach(function(key) { var input = target[key]; var urlParam = Object.keys(input.attributes).reduce(function(urlParam, key) { if (urlParam) return urlParam; return (input.attributes[key].name === "urlparam" && input.attributes[key].value === "true"); }, false); if (urlParam) { urlParamData[input.id] = input.value; return; } if (!input.value || !input.id) return; if (input.id === '_url') { url = input.value; return; } if (input.id === '_method') { method = input.value; return; } if (input.type === 'file') { data.append(input.id, input.files[0], input.files[0].name); return; } data.append(input.id, input.value); }); // replace URL params with actual data var urlParams = url.match(/:[a-zA-Z0-9_]+/g) || []; urlParams.forEach(function(param) { const paramName = param.substr(1, param.length - 1); url = url.replace(param, urlParamData[paramName]); }) // POST this data to the app var options = { responseType: 'json' } if (method.toLowerCase() === 'get') { var pairs = []; for(var pair of data.entries()) { pairs.push(pair[0] + '=' + pair[1]); } if (pairs.length) { url += "?" + pairs.join("&"); } data = options; } this.$http[method.toLowerCase()](url, data, options) .then( function(response) { this.apiResponse = response.body this.apiError = false; }, function(error) { this.apiError = true; this.apiResponse = error.body; } ) } } }); </script> </body> </html>