├── index.js ├── .gitignore ├── .npmignore ├── example ├── views │ ├── index.jade │ └── layout.jade ├── public │ ├── swagger │ │ ├── images │ │ │ ├── throbber.gif │ │ │ ├── logo_small.png │ │ │ ├── wordnik_api.png │ │ │ └── pet_store_api.png │ │ ├── lib │ │ │ ├── jquery.slideto.min.js │ │ │ ├── jquery.wiggle.min.js │ │ │ ├── jquery.ba-bbq.min.js │ │ │ ├── highlight.7.3.pack.js │ │ │ ├── underscore-min.js │ │ │ ├── backbone-min.js │ │ │ └── swagger.js │ │ ├── css │ │ │ ├── hightlight.default.css │ │ │ └── screen.css │ │ ├── index.html │ │ └── swagger-ui.min.js │ └── stylesheets │ │ └── style.css ├── package.json ├── api.yml ├── api.coffee ├── app.js └── api.js ├── package.json ├── LICENSE ├── jshint.cfg ├── README.md └── lib └── swagger-koa └── index.js /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./lib/swagger-koa'); 2 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | 2 | node_modules/ 3 | .DS_Store 4 | /nbproject/private/ -------------------------------------------------------------------------------- /.npmignore: -------------------------------------------------------------------------------- 1 | 2 | example/ 3 | .DS_Store 4 | .git* 5 | project.sublime-project 6 | project.sublime-workspace -------------------------------------------------------------------------------- /example/views/index.jade: -------------------------------------------------------------------------------- 1 | extends layout 2 | 3 | block content 4 | h1= title 5 | p Welcome to #{title} -------------------------------------------------------------------------------- /example/public/swagger/images/throbber.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janvotava/swagger-koa/HEAD/example/public/swagger/images/throbber.gif -------------------------------------------------------------------------------- /example/public/swagger/images/logo_small.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janvotava/swagger-koa/HEAD/example/public/swagger/images/logo_small.png -------------------------------------------------------------------------------- /example/public/swagger/images/wordnik_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janvotava/swagger-koa/HEAD/example/public/swagger/images/wordnik_api.png -------------------------------------------------------------------------------- /example/public/swagger/images/pet_store_api.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/janvotava/swagger-koa/HEAD/example/public/swagger/images/pet_store_api.png -------------------------------------------------------------------------------- /example/public/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | body { 2 | padding: 50px; 3 | font: 14px "Lucida Grande", Helvetica, Arial, sans-serif; 4 | } 5 | 6 | a { 7 | color: #00B7FF; 8 | } -------------------------------------------------------------------------------- /example/views/layout.jade: -------------------------------------------------------------------------------- 1 | doctype html 2 | html 3 | head 4 | title= title 5 | link(rel='stylesheet', href='/stylesheets/style.css') 6 | body 7 | block content 8 | -------------------------------------------------------------------------------- /example/public/swagger/lib/jquery.slideto.min.js: -------------------------------------------------------------------------------- 1 | (function(b){b.fn.slideto=function(a){a=b.extend({slide_duration:"slow",highlight_duration:3E3,highlight:true,highlight_color:"#FFFF99"},a);return this.each(function(){obj=b(this);b("body").animate({scrollTop:obj.offset().top},a.slide_duration,function(){a.highlight&&b.ui.version&&obj.effect("highlight",{color:a.highlight_color},a.highlight_duration)})})}})(jQuery); 2 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-koa-example", 3 | "description": "Example project to test swagger-koa against.", 4 | "version": "1.0.0", 5 | "author": "Jan Votava , fliptoo , sposmen ", 6 | "dependencies": { 7 | "jade": "^1.11.0", 8 | "koa": "^2.5.0", 9 | "koa-render": "^0.2.1", 10 | "koa-route": "^3.2.0", 11 | "koa-static": "^4.0.2" 12 | }, 13 | "scripts": { 14 | "start": "DEBUG=* node app.js" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /example/public/swagger/lib/jquery.wiggle.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | jQuery Wiggle 3 | Author: WonderGroup, Jordan Thomas 4 | URL: http://labs.wondergroup.com/demos/mini-ui/index.html 5 | License: MIT (http://en.wikipedia.org/wiki/MIT_License) 6 | */ 7 | jQuery.fn.wiggle=function(o){var d={speed:50,wiggles:3,travel:5,callback:null};var o=jQuery.extend(d,o);return this.each(function(){var cache=this;var wrap=jQuery(this).wrap('
').css("position","relative");var calls=0;for(i=1;i<=o.wiggles;i++){jQuery(this).animate({left:"-="+o.travel},o.speed).animate({left:"+="+o.travel*2},o.speed*2).animate({left:"-="+o.travel},o.speed,function(){calls++;if(jQuery(cache).parent().hasClass('wiggle-wrap')){jQuery(cache).parent().replaceWith(cache);} 8 | if(calls==o.wiggles&&jQuery.isFunction(o.callback)){o.callback();}});}});}; -------------------------------------------------------------------------------- /example/api.yml: -------------------------------------------------------------------------------- 1 | resourcePath: /apiYml 2 | description: All about API 3 | apis: 4 | 5 | - path: /login 6 | operations: 7 | 8 | - httpMethod: POST 9 | summary: Login with username and password 10 | notes: Returns a user based on username 11 | responseClass: User 12 | nickname: login 13 | consumes: 14 | - text/html 15 | parameters: 16 | 17 | - name: username 18 | dataType: string 19 | paramType: query 20 | required: true 21 | description: Your username 22 | 23 | - name: password 24 | dataType: string 25 | paramType: query 26 | required: true 27 | description: Your password 28 | 29 | models: 30 | User: 31 | id: User 32 | properties: 33 | username: 34 | type: String 35 | password: 36 | type: String -------------------------------------------------------------------------------- /example/api.coffee: -------------------------------------------------------------------------------- 1 | 2 | ### 3 | * @swagger 4 | * resourcePath: /apiCoffee 5 | * description: All about API 6 | ### 7 | 8 | ### 9 | * @swagger 10 | * path: /login 11 | * operations: 12 | * - httpMethod: POST 13 | * summary: Login with username and password 14 | * notes: Returns a user based on username 15 | * responseClass: User 16 | * nickname: login 17 | * consumes: 18 | * - text/html 19 | * parameters: 20 | * - name: username 21 | * description: Your username 22 | * paramType: query 23 | * required: true 24 | * dataType: string 25 | * - name: password 26 | * description: Your password 27 | * paramType: query 28 | * required: true 29 | * dataType: string 30 | 31 | ### 32 | 33 | ### 34 | * @swagger 35 | * models: 36 | * User: 37 | * id: User 38 | * properties: 39 | * username: 40 | * type: String 41 | * password: 42 | * type: String 43 | ### -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-koa", 3 | "description": "Document your koa rest api by jsDoc or yaml.", 4 | "version": "2.1.0", 5 | "author": "Jan Votava , fliptoo , sposmen ", 6 | "main": "./index", 7 | "license": "MIT", 8 | "dependencies": { 9 | "async": "^2.6.0", 10 | "coffee-script": "*", 11 | "doctrine": "*", 12 | "js-yaml": "^3.10.0", 13 | "koa": "^2.5.0", 14 | "koa-mount": "^3.0.0", 15 | "koa-route": "^3.2.0", 16 | "koa-static": "^4.0.2", 17 | "underscore": "*" 18 | }, 19 | "devDependencies": { 20 | "jshint": "*" 21 | }, 22 | "scripts": { 23 | "test": "jshint --config jshint.cfg $(find . -name *.js | grep -v './node_modules')" 24 | }, 25 | "keywords": [ 26 | "swagger", 27 | "swagger-ui", 28 | "koa", 29 | "api", 30 | "jsdoc", 31 | "yml", 32 | "yaml", 33 | "coffee-script", 34 | "restful" 35 | ], 36 | "repository": "git://github.com/cyner/swagger-koa" 37 | } 38 | -------------------------------------------------------------------------------- /example/app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | 5 | var Koa = require('koa') 6 | , router = require('koa-route') 7 | , views = require('koa-render') 8 | , serve = require('koa-static') 9 | , api = require('./api') 10 | , path = require('path') 11 | , swagger = require('../'); 12 | 13 | var app = new Koa(), 14 | port = 3000; 15 | 16 | app.use(views('views', { default: 'jade' })); 17 | 18 | app.use(swagger.init({ 19 | apiVersion: '1.0', 20 | swaggerVersion: '1.0', 21 | basePath: 'http://localhost:' + port, 22 | swaggerURL: '/swagger', 23 | swaggerJSON: '/api-docs.json', 24 | swaggerUI: './public/swagger/', 25 | apis: ['./api.js', './api.yml', 'api.coffee'] 26 | })); 27 | 28 | app.use(serve(path.join(__dirname, 'public'))); 29 | 30 | app.use(router.get('/', async (ctx, next) => { 31 | ctx.body = await ctx.render('index', { title: 'Koa' }); 32 | })); 33 | 34 | app.use(router.post('/login', api.login)); 35 | 36 | app.listen(port, function() { 37 | console.log('Server running on port ' + port); 38 | }); 39 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | (The MIT License) 2 | 3 | Copyright (c) 2015 Jan Votava 4 | Copyright (c) 2013 Fliptoo 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining 7 | a copy of this software and associated documentation files (the 8 | 'Software'), to deal in the Software without restriction, including 9 | without limitation the rights to use, copy, modify, merge, publish, 10 | distribute, sublicense, and/or sell copies of the Software, and to 11 | permit persons to whom the Software is furnished to do so, subject to 12 | the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be 15 | included in all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 18 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 19 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 20 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 21 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 22 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 23 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /example/api.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * @swagger 4 | * resourcePath: /apiJs 5 | * description: All about API 6 | */ 7 | 8 | /** 9 | * @swagger 10 | * path: /login 11 | * operations: 12 | * - httpMethod: POST 13 | * summary: Login with username and password 14 | * notes: Returns a user based on username 15 | * responseClass: User 16 | * nickname: login 17 | * consumes: 18 | * - text/html 19 | * parameters: 20 | * - name: username 21 | * description: Your username 22 | * paramType: query 23 | * required: true 24 | * dataType: string 25 | * - name: password 26 | * description: Your password 27 | * paramType: query 28 | * required: true 29 | * dataType: string 30 | */ 31 | 32 | exports.login = function *() { 33 | var user = {} 34 | , query = this.request.query; 35 | 36 | user.username = query.username; 37 | user.password = query.password; 38 | 39 | this.body = user; 40 | }; 41 | 42 | /** 43 | * @swagger 44 | * models: 45 | * User: 46 | * id: User 47 | * properties: 48 | * username: 49 | * type: String 50 | * password: 51 | * type: String 52 | */ 53 | -------------------------------------------------------------------------------- /example/public/swagger/css/hightlight.default.css: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | Original style from softwaremaniacs.org (c) Ivan Sagalaev 4 | 5 | */ 6 | 7 | pre code { 8 | display: block; padding: 0.5em; 9 | background: #F0F0F0; 10 | } 11 | 12 | pre code, 13 | pre .subst, 14 | pre .tag .title, 15 | pre .lisp .title, 16 | pre .clojure .built_in, 17 | pre .nginx .title { 18 | color: black; 19 | } 20 | 21 | pre .string, 22 | pre .title, 23 | pre .constant, 24 | pre .parent, 25 | pre .tag .value, 26 | pre .rules .value, 27 | pre .rules .value .number, 28 | pre .preprocessor, 29 | pre .ruby .symbol, 30 | pre .ruby .symbol .string, 31 | pre .aggregate, 32 | pre .template_tag, 33 | pre .django .variable, 34 | pre .smalltalk .class, 35 | pre .addition, 36 | pre .flow, 37 | pre .stream, 38 | pre .bash .variable, 39 | pre .apache .tag, 40 | pre .apache .cbracket, 41 | pre .tex .command, 42 | pre .tex .special, 43 | pre .erlang_repl .function_or_atom, 44 | pre .markdown .header { 45 | color: #800; 46 | } 47 | 48 | pre .comment, 49 | pre .annotation, 50 | pre .template_comment, 51 | pre .diff .header, 52 | pre .chunk, 53 | pre .markdown .blockquote { 54 | color: #888; 55 | } 56 | 57 | pre .number, 58 | pre .date, 59 | pre .regexp, 60 | pre .literal, 61 | pre .smalltalk .symbol, 62 | pre .smalltalk .char, 63 | pre .go .constant, 64 | pre .change, 65 | pre .markdown .bullet, 66 | pre .markdown .link_url { 67 | color: #080; 68 | } 69 | 70 | pre .label, 71 | pre .javadoc, 72 | pre .ruby .string, 73 | pre .decorator, 74 | pre .filter .argument, 75 | pre .localvars, 76 | pre .array, 77 | pre .attr_selector, 78 | pre .important, 79 | pre .pseudo, 80 | pre .pi, 81 | pre .doctype, 82 | pre .deletion, 83 | pre .envvar, 84 | pre .shebang, 85 | pre .apache .sqbracket, 86 | pre .nginx .built_in, 87 | pre .tex .formula, 88 | pre .erlang_repl .reserved, 89 | pre .prompt, 90 | pre .markdown .link_label, 91 | pre .vhdl .attribute, 92 | pre .clojure .attribute, 93 | pre .coffeescript .property { 94 | color: #88F 95 | } 96 | 97 | pre .keyword, 98 | pre .id, 99 | pre .phpdoc, 100 | pre .title, 101 | pre .built_in, 102 | pre .aggregate, 103 | pre .css .tag, 104 | pre .javadoctag, 105 | pre .phpdoc, 106 | pre .yardoctag, 107 | pre .smalltalk .class, 108 | pre .winutils, 109 | pre .bash .variable, 110 | pre .apache .tag, 111 | pre .go .typename, 112 | pre .tex .command, 113 | pre .markdown .strong, 114 | pre .request, 115 | pre .status { 116 | font-weight: bold; 117 | } 118 | 119 | pre .markdown .emphasis { 120 | font-style: italic; 121 | } 122 | 123 | pre .nginx .built_in { 124 | font-weight: normal; 125 | } 126 | 127 | pre .coffeescript .javascript, 128 | pre .javascript .xml, 129 | pre .tex .formula, 130 | pre .xml .javascript, 131 | pre .xml .vbscript, 132 | pre .xml .css, 133 | pre .xml .cdata { 134 | opacity: 0.5; 135 | } 136 | -------------------------------------------------------------------------------- /example/public/swagger/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Swagger UI 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 48 | 49 | 50 | 51 | 69 | 70 |
71 |   72 |
73 | 74 |
75 | 76 |
77 | 78 | 79 | 80 | 81 | -------------------------------------------------------------------------------- /jshint.cfg: -------------------------------------------------------------------------------- 1 | { 2 | // Settings 3 | "passfail" : false, // Stop on first error. 4 | "maxerr" : 100, // Maximum error before stopping. 5 | 6 | // Predefined globals whom JSHint will ignore. 7 | "browser" : true, // Standard browser globals e.g. `window`, `document`. 8 | 9 | "node" : true, 10 | "rhino" : false, 11 | "couch" : false, 12 | "wsh" : false, // Windows Scripting Host. 13 | 14 | "jquery" : true, 15 | "prototypejs" : false, 16 | "mootools" : false, 17 | "dojo" : false, 18 | 19 | "predef" : [ // Custom globals. 20 | "describe", "it" 21 | ], 22 | 23 | 24 | // Development. 25 | "debug" : false, // Allow debugger statements e.g. browser breakpoints. 26 | "devel" : true, // Allow developments statements e.g. `console.log();`. 27 | 28 | 29 | // ECMAScript 5. 30 | "strict" : false, // Require `use strict` pragma in every file. 31 | "globalstrict" : true, // Allow global "use strict" (also enables 'strict'). 32 | 33 | 34 | // The Good Parts. 35 | "asi" : false, // Tolerate Automatic Semicolon Insertion (no semicolons). 36 | "laxbreak" : true, // Tolerate unsafe line breaks e.g. `return [\n] x` without semicolons. 37 | "bitwise" : false, // Prohibit bitwise operators (&, |, ^, etc.). 38 | "boss" : false, // Tolerate assignments inside if, for & while. Usually conditions & loops are for comparison, not assignments. 39 | "curly" : true, // Require {} for every new block or scope. 40 | "eqeqeq" : true, // Require triple equals i.e. `===`. 41 | "eqnull" : false, // Tolerate use of `== null`. 42 | "evil" : false, // Tolerate use of `eval`. 43 | "expr" : false, // Tolerate `ExpressionStatement` as Programs. 44 | "forin" : false, // Tolerate `for in` loops without `hasOwnPrototype`. 45 | "immed" : true, // Require immediate invocations to be wrapped in parens e.g. `( function(){}() );` 46 | "latedef" : true, // Prohipit variable use before definition. 47 | "loopfunc" : false, // Allow functions to be defined within loops. 48 | "noarg" : true, // Prohibit use of `arguments.caller` and `arguments.callee`. 49 | "regexp" : false, // Prohibit `.` and `[^...]` in regular expressions. 50 | "regexdash" : false, // Tolerate unescaped last dash i.e. `[-...]`. 51 | "scripturl" : false, // Tolerate script-targeted URLs. 52 | "shadow" : false, // Allows re-define variables later in code e.g. `var x=1; x=2;`. 53 | "supernew" : false, // Tolerate `new function () { ... };` and `new Object;`. 54 | "undef" : true, // Require all non-global variables be declared before they are used. 55 | 56 | 57 | // Personal styling preferences. 58 | "newcap" : true, // Require capitalization of all constructor functions e.g. `new F()`. 59 | "noempty" : true, // Prohibit use of empty blocks. 60 | "nonew" : true, // Prohibit use of constructors for side-effects. 61 | "nomen" : false, // Prohibit use of initial or trailing underbars in names. 62 | "onevar" : false, // Allow only one `var` statement per function. 63 | "plusplus" : false, // Prohibit use of `++` & `--`. 64 | "sub" : false, // Tolerate all forms of subscript notation besides dot notation e.g. `dict['key']` instead of `dict.key`. 65 | "trailing" : true, // Prohibit trailing whitespaces. 66 | "white" : true, // Check against strict whitespace and indentation rules. 67 | "indent" : 2, // Specify indentation spacing 68 | 69 | "esnext" : true, // Enable ES6 70 | "noyield" : true 71 | } 72 | 73 | -------------------------------------------------------------------------------- /example/public/swagger/lib/jquery.ba-bbq.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery BBQ: Back Button & Query Library - v1.2.1 - 2/17/2010 3 | * http://benalman.com/projects/jquery-bbq-plugin/ 4 | * 5 | * Copyright (c) 2010 "Cowboy" Ben Alman 6 | * Dual licensed under the MIT and GPL licenses. 7 | * http://benalman.com/about/license/ 8 | */ 9 | (function($,p){var i,m=Array.prototype.slice,r=decodeURIComponent,a=$.param,c,l,v,b=$.bbq=$.bbq||{},q,u,j,e=$.event.special,d="hashchange",A="querystring",D="fragment",y="elemUrlAttr",g="location",k="href",t="src",x=/^.*\?|#.*$/g,w=/^.*\#/,h,C={};function E(F){return typeof F==="string"}function B(G){var F=m.call(arguments,1);return function(){return G.apply(this,F.concat(m.call(arguments)))}}function n(F){return F.replace(/^[^#]*#?(.*)$/,"$1")}function o(F){return F.replace(/(?:^[^?#]*\?([^#]*).*$)?.*/,"$1")}function f(H,M,F,I,G){var O,L,K,N,J;if(I!==i){K=F.match(H?/^([^#]*)\#?(.*)$/:/^([^#?]*)\??([^#]*)(#?.*)/);J=K[3]||"";if(G===2&&E(I)){L=I.replace(H?w:x,"")}else{N=l(K[2]);I=E(I)?l[H?D:A](I):I;L=G===2?I:G===1?$.extend({},I,N):$.extend({},N,I);L=a(L);if(H){L=L.replace(h,r)}}O=K[1]+(H?"#":L||!K[1]?"?":"")+L+J}else{O=M(F!==i?F:p[g][k])}return O}a[A]=B(f,0,o);a[D]=c=B(f,1,n);c.noEscape=function(G){G=G||"";var F=$.map(G.split(""),encodeURIComponent);h=new RegExp(F.join("|"),"g")};c.noEscape(",/");$.deparam=l=function(I,F){var H={},G={"true":!0,"false":!1,"null":null};$.each(I.replace(/\+/g," ").split("&"),function(L,Q){var K=Q.split("="),P=r(K[0]),J,O=H,M=0,R=P.split("]["),N=R.length-1;if(/\[/.test(R[0])&&/\]$/.test(R[N])){R[N]=R[N].replace(/\]$/,"");R=R.shift().split("[").concat(R);N=R.length-1}else{N=0}if(K.length===2){J=r(K[1]);if(F){J=J&&!isNaN(J)?+J:J==="undefined"?i:G[J]!==i?G[J]:J}if(N){for(;M<=N;M++){P=R[M]===""?O.length:R[M];O=O[P]=M').hide().insertAfter("body")[0].contentWindow;q=function(){return a(n.document[c][l])};o=function(u,s){if(u!==s){var t=n.document;t.open().close();t[c].hash="#"+u}};o(a())}}m.start=function(){if(r){return}var t=a();o||p();(function s(){var v=a(),u=q(t);if(v!==t){o(t=v,u);$(i).trigger(d)}else{if(u!==t){i[c][l]=i[c][l].replace(/#.*/,"")+"#"+u}}r=setTimeout(s,$[d+"Delay"])})()};m.stop=function(){if(!n){r&&clearTimeout(r);r=0}};return m})()})(jQuery,this); -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | {swagger-koa} 2 | ========= 3 | 4 | [Swagger](http://swagger.io/specification/) is a specification and complete framework 5 | implementation for describing, producing, consuming, and visualizing RESTful web services. 6 | View [demo](http://petstore.swagger.io/). 7 | 8 | __{swagger-koa}__ is a simple and clean solution to integrate swagger with koa. 9 | 10 | ## Installation 11 | 12 | $ npm install swagger-koa 13 | 14 | ## Quick Start 15 | 16 | Configure {swagger-koa} as koa middleware. 17 | 18 | 19 | `apiVersion` -> Your api version. 20 | 21 | `swaggerVersion` -> Swagger version. 22 | 23 | `swaggerUI` -> Where is your swagger-ui? 24 | 25 | `swaggerURL` -> Path to use for swagger ui web interface. 26 | 27 | `swaggerJSON` -> Path to use for swagger ui JSON. 28 | 29 | `basePath` -> The basePath for swagger.js 30 | 31 | `info` -> [Metadata][info] about the API 32 | 33 | `apis` -> Define your api array. 34 | 35 | ``` 36 | var swagger = require('swagger-koa'); 37 | 38 | app.use(swagger.init({ 39 | apiVersion: '1.0', 40 | swaggerVersion: '1.0', 41 | swaggerURL: '/swagger', 42 | swaggerJSON: '/api-docs.json', 43 | swaggerUI: './public/swagger/', 44 | basePath: 'http://localhost:3000', 45 | info: { 46 | title: 'swagger-koa sample app', 47 | description: 'Swagger + Koa = {swagger-koa}' 48 | }, 49 | apis: ['./api.js', './api.yml'] 50 | })); 51 | ... 52 | ``` 53 | 54 | [info]: https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#513-info-object 55 | 56 | ## Read from jsdoc 57 | 58 | Example 'api.js' 59 | 60 | ```js 61 | 62 | /** 63 | * @swagger 64 | * resourcePath: /api 65 | * description: All about API 66 | */ 67 | 68 | /** 69 | * @swagger 70 | * path: /login 71 | * operations: 72 | * - httpMethod: POST 73 | * summary: Login with username and password 74 | * notes: Returns a user based on username 75 | * responseClass: User 76 | * nickname: login 77 | * consumes: 78 | * - text/html 79 | * parameters: 80 | * - name: username 81 | * description: Your username 82 | * paramType: query 83 | * required: true 84 | * dataType: string 85 | * - name: password 86 | * description: Your password 87 | * paramType: query 88 | * required: true 89 | * dataType: string 90 | */ 91 | 92 | exports.login = function *() { 93 | var user = {} 94 | , query = this.request.query; 95 | 96 | user.username = query.username; 97 | user.password = query.password; 98 | 99 | this.body = user; 100 | }; 101 | 102 | /** 103 | * @swagger 104 | * models: 105 | * User: 106 | * id: User 107 | * properties: 108 | * username: 109 | * type: String 110 | * password: 111 | * type: String 112 | */ 113 | ``` 114 | 115 | ## Read from yaml file 116 | 117 | Example 'api.yml' 118 | 119 | ```yml 120 | resourcePath: /api 121 | description: All about API 122 | apis: 123 | 124 | - path: /login 125 | operations: 126 | 127 | - httpMethod: POST 128 | summary: Login with username and password 129 | notes: Returns a user based on username 130 | responseClass: User 131 | nickname: login 132 | consumes: 133 | - text/html 134 | parameters: 135 | 136 | - name: username 137 | dataType: string 138 | paramType: query 139 | required: true 140 | description: Your username 141 | 142 | - name: password 143 | dataType: string 144 | paramType: query 145 | required: true 146 | description: Your password 147 | 148 | models: 149 | User: 150 | id: User 151 | properties: 152 | username: 153 | type: String 154 | password: 155 | type: String 156 | ``` 157 | 158 | ## Read from jsdoc 159 | 160 | Example 'api.coffee' 161 | 162 | ```coffee 163 | 164 | ### 165 | * @swagger 166 | * resourcePath: /api 167 | * description: All about API 168 | ### 169 | 170 | ### 171 | * @swagger 172 | * path: /login 173 | * operations: 174 | * - httpMethod: POST 175 | * summary: Login with username and password 176 | * notes: Returns a user based on username 177 | * responseClass: User 178 | * nickname: login 179 | * consumes: 180 | * - text/html 181 | * parameters: 182 | * - name: username 183 | * description: Your username 184 | * paramType: query 185 | * required: true 186 | * dataType: string 187 | * - name: password 188 | * description: Your password 189 | * paramType: query 190 | * required: true 191 | * dataType: string 192 | ### 193 | 194 | ### 195 | * @swagger 196 | * models: 197 | * User: 198 | * id: User 199 | * properties: 200 | * username: 201 | * type: String 202 | * password: 203 | * type: String 204 | ### 205 | ``` 206 | 207 | 208 | ## Examples 209 | 210 | Clone the {swagger-koa} repo, then install the dev dependencies: 211 | 212 | $ git clone git://github.com/cyner/swagger-koa.git --depth 1 213 | $ cd swagger-koa 214 | $ npm install 215 | 216 | and run the example: 217 | 218 | $ cd example 219 | $ node --harmony app.js 220 | 221 | # Credits 222 | 223 | - [Express](https://github.com/visionmedia/express) 224 | - [Koa](https://github.com/koajs/koa) 225 | - [swagger-jack](https://github.com/feugy/swagger-jack) 226 | - [swagger-express](https://github.com/fliptoo/swagger-express) 227 | 228 | ## License 229 | 230 | (The MIT License) 231 | 232 | Copyright (c) 2015 Jan Votava <jan@sensible.io> 233 | 234 | Copyright (c) 2013 Fliptoo <fliptoo.studio@gmail.com> 235 | 236 | Permission is hereby granted, free of charge, to any person obtaining 237 | a copy of this software and associated documentation files (the 238 | 'Software'), to deal in the Software without restriction, including 239 | without limitation the rights to use, copy, modify, merge, publish, 240 | distribute, sublicense, and/or sell copies of the Software, and to 241 | permit persons to whom the Software is furnished to do so, subject to 242 | the following conditions: 243 | 244 | The above copyright notice and this permission notice shall be 245 | included in all copies or substantial portions of the Software. 246 | 247 | THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 248 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 249 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 250 | IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY 251 | CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, 252 | TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE 253 | SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 254 | -------------------------------------------------------------------------------- /example/public/swagger/lib/highlight.7.3.pack.js: -------------------------------------------------------------------------------- 1 | var hljs=new function(){function l(o){return o.replace(/&/gm,"&").replace(//gm,">")}function b(p){for(var o=p.firstChild;o;o=o.nextSibling){if(o.nodeName=="CODE"){return o}if(!(o.nodeType==3&&o.nodeValue.match(/\s+/))){break}}}function h(p,o){return Array.prototype.map.call(p.childNodes,function(q){if(q.nodeType==3){return o?q.nodeValue.replace(/\n/g,""):q.nodeValue}if(q.nodeName=="BR"){return"\n"}return h(q,o)}).join("")}function a(q){var p=(q.className+" "+q.parentNode.className).split(/\s+/);p=p.map(function(r){return r.replace(/^language-/,"")});for(var o=0;o"}while(x.length||v.length){var u=t().splice(0,1)[0];y+=l(w.substr(p,u.offset-p));p=u.offset;if(u.event=="start"){y+=s(u.node);r.push(u.node)}else{if(u.event=="stop"){var o,q=r.length;do{q--;o=r[q];y+=("")}while(o!=u.node);r.splice(q,1);while(q'+L[0]+""}else{r+=L[0]}N=A.lR.lastIndex;L=A.lR.exec(K)}return r+K.substr(N)}function z(){if(A.sL&&!e[A.sL]){return l(w)}var r=A.sL?d(A.sL,w):g(w);if(A.r>0){v+=r.keyword_count;B+=r.r}return''+r.value+""}function J(){return A.sL!==undefined?z():G()}function I(L,r){var K=L.cN?'':"";if(L.rB){x+=K;w=""}else{if(L.eB){x+=l(r)+K;w=""}else{x+=K;w=r}}A=Object.create(L,{parent:{value:A}});B+=L.r}function C(K,r){w+=K;if(r===undefined){x+=J();return 0}var L=o(r,A);if(L){x+=J();I(L,r);return L.rB?0:r.length}var M=s(A,r);if(M){if(!(M.rE||M.eE)){w+=r}x+=J();do{if(A.cN){x+=""}A=A.parent}while(A!=M.parent);if(M.eE){x+=l(r)}w="";if(M.starts){I(M.starts,"")}return M.rE?0:r.length}if(t(r,A)){throw"Illegal"}w+=r;return r.length||1}var F=e[D];f(F);var A=F;var w="";var B=0;var v=0;var x="";try{var u,q,p=0;while(true){A.t.lastIndex=p;u=A.t.exec(E);if(!u){break}q=C(E.substr(p,u.index-p),u[0]);p=u.index+q}C(E.substr(p));return{r:B,keyword_count:v,value:x,language:D}}catch(H){if(H=="Illegal"){return{r:0,keyword_count:0,value:l(E)}}else{throw H}}}function g(s){var o={keyword_count:0,r:0,value:l(s)};var q=o;for(var p in e){if(!e.hasOwnProperty(p)){continue}var r=d(p,s);r.language=p;if(r.keyword_count+r.r>q.keyword_count+q.r){q=r}if(r.keyword_count+r.r>o.keyword_count+o.r){q=o;o=r}}if(q.language){o.second_best=q}return o}function i(q,p,o){if(p){q=q.replace(/^((<[^>]+>|\t)+)/gm,function(r,v,u,t){return v.replace(/\t/g,p)})}if(o){q=q.replace(/\n/g,"
")}return q}function m(r,u,p){var v=h(r,p);var t=a(r);if(t=="no-highlight"){return}var w=t?d(t,v):g(v);t=w.language;var o=c(r);if(o.length){var q=document.createElement("pre");q.innerHTML=w.value;w.value=j(o,c(q),v)}w.value=i(w.value,u,p);var s=r.className;if(!s.match("(\\s|^)(language-)?"+t+"(\\s|$)")){s=s?(s+" "+t):t}r.innerHTML=w.value;r.className=s;r.result={language:t,kw:w.keyword_count,re:w.r};if(w.second_best){r.second_best={language:w.second_best.language,kw:w.second_best.keyword_count,re:w.second_best.r}}}function n(){if(n.called){return}n.called=true;Array.prototype.map.call(document.getElementsByTagName("pre"),b).filter(Boolean).forEach(function(o){m(o,hljs.tabReplace)})}function k(){window.addEventListener("DOMContentLoaded",n,false);window.addEventListener("load",n,false)}var e={};this.LANGUAGES=e;this.highlight=d;this.highlightAuto=g;this.fixMarkup=i;this.highlightBlock=m;this.initHighlighting=n;this.initHighlightingOnLoad=k;this.IR="[a-zA-Z][a-zA-Z0-9_]*";this.UIR="[a-zA-Z_][a-zA-Z0-9_]*";this.NR="\\b\\d+(\\.\\d+)?";this.CNR="(\\b0[xX][a-fA-F0-9]+|(\\b\\d+(\\.\\d*)?|\\.\\d+)([eE][-+]?\\d+)?)";this.BNR="\\b(0b[01]+)";this.RSR="!|!=|!==|%|%=|&|&&|&=|\\*|\\*=|\\+|\\+=|,|\\.|-|-=|/|/=|:|;|<|<<|<<=|<=|=|==|===|>|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\[\\s\\S]",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(q,r){var o={};for(var p in q){o[p]=q[p]}if(r){for(var p in r){o[p]=r[p]}}return o}}();hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[b],starts:{e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},b]}]}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{c:d,k:e,i:"\\S"}}(hljs); -------------------------------------------------------------------------------- /lib/swagger-koa/index.js: -------------------------------------------------------------------------------- 1 | var _ = require('underscore'); 2 | var async = require('async'); 3 | var fs = require('fs'); 4 | var path = require('path'); 5 | var yaml = require('js-yaml'); 6 | var coffee = require('coffee-script'); 7 | var url = require('url'); 8 | var Koa = require('koa'); 9 | var mount = require('koa-mount'); 10 | var serve = require('koa-static'); 11 | var route = require('koa-route'); 12 | 13 | var doctrine = require('doctrine'); 14 | var descriptor = {}; 15 | var resources = {}; 16 | 17 | /** 18 | * Read from yml file 19 | * @api private 20 | * @param {String} file 21 | * @param {Function} fn 22 | */ 23 | function readYml(file, fn) { 24 | fs.readFile(path.resolve(process.cwd(), file), function(err, data) { 25 | 26 | if (err) { 27 | fn(err); 28 | } 29 | const resource = yaml.load(data.toString()); 30 | let api = {}; 31 | 32 | api.resourcePath = resource.resourcePath; 33 | api.description = resource.description; 34 | descriptor.apis.push(api); 35 | resources[resource.resourcePath] = resource; 36 | 37 | fn(); 38 | }); 39 | } 40 | 41 | /** 42 | * Parse jsDoc from a js file 43 | * @api private 44 | * @param {String} file 45 | * @param {Function} fn 46 | */ 47 | function parseJsDocs(file, fn) { 48 | fs.readFile(file, function(err, data) { 49 | if (err) { 50 | fn(err); 51 | } 52 | 53 | var js = data.toString(); 54 | var regex = /\/\*\*([\s\S]*?)\*\//gm; 55 | var fragments = js.match(regex); 56 | var docs = []; 57 | 58 | if (!fragments) { 59 | fn(null, docs); 60 | return; 61 | } 62 | 63 | for (var i = 0; i < fragments.length; i++) { 64 | var fragment = fragments[i]; 65 | var doc = doctrine.parse(fragment, {unwrap: true}); 66 | 67 | docs.push(doc); 68 | 69 | if (i === fragments.length - 1) { 70 | fn(null, docs); 71 | } 72 | } 73 | }); 74 | } 75 | 76 | /** 77 | * Parse coffeeDoc from a coffee file 78 | * @api private 79 | * @param {String} file 80 | * @param {Function} fn 81 | */ 82 | function parseCoffeeDocs(file, fn) { 83 | fs.readFile(file, function(err, data) { 84 | if (err) { 85 | fn(err); 86 | } 87 | 88 | var js = coffee.compile(data.toString()); 89 | var regex = /\/\**([\s\S]*?)\*\//gm; 90 | var fragments = js.match(regex); 91 | var docs = []; 92 | 93 | for (var i = 0; i < fragments.length; i++) { 94 | var fragment = fragments[i]; 95 | var doc = doctrine.parse(fragment, {unwrap: true}); 96 | 97 | docs.push(doc); 98 | 99 | if (i === fragments.length - 1) { 100 | fn(null, docs); 101 | } 102 | } 103 | }); 104 | } 105 | 106 | /** 107 | * Get jsDoc tag with title '@swagger' 108 | * @api private 109 | * @param {Object} fragment 110 | * @param {Function} fn 111 | */ 112 | function getSwagger(fragment, fn) { 113 | for (var i = 0; i < fragment.tags.length; i++) { 114 | var tag = fragment.tags[i]; 115 | if ('swagger' === tag.title) { 116 | return yaml.safeLoadAll(tag.description, fn); 117 | } 118 | } 119 | 120 | return fn(false); 121 | } 122 | 123 | /** 124 | * 125 | * @param {Object} api 126 | */ 127 | function pushApiIfDoesNotExist(api) { 128 | var found = _.findWhere(descriptor.apis, {resourcePath: api.resourcePath}); 129 | 130 | if (found) { 131 | return; 132 | } 133 | 134 | descriptor.apis.push(api); 135 | } 136 | 137 | /** 138 | * 139 | * @param {Function} fn 140 | * @returns {Function} 141 | */ 142 | function createParserCb(fn) { 143 | return function(err, docs) { 144 | 145 | if (err) { 146 | fn(err); 147 | } 148 | 149 | var resource = {apis: []}; 150 | 151 | async.eachSeries(docs, function(doc, cb) { 152 | getSwagger(doc, function(api) { 153 | 154 | if (!api) { 155 | return cb(); 156 | } 157 | 158 | // do not rewrite existing resource 159 | if (api.resourcePath && resources[api.resourcePath]) { 160 | resource = resources[api.resourcePath]; 161 | } 162 | 163 | if (api.resourcePath) { 164 | pushApiIfDoesNotExist(api); 165 | resource.resourcePath = api.resourcePath; 166 | } else if (api.models) { 167 | resource.models = Object.assign({}, resource.models || {}, api.models); 168 | } else { 169 | resource.apis.push(api); 170 | } 171 | 172 | cb(); 173 | }); 174 | }, function() { 175 | if (resource.resourcePath) { 176 | resources[resource.resourcePath] = resource; 177 | } 178 | fn(); 179 | }); 180 | }; 181 | } 182 | 183 | /** 184 | * Read from jsDoc 185 | * @api private 186 | * @param {String} file 187 | * @param {Function} fn 188 | */ 189 | function readJsDoc(file, fn) { 190 | parseJsDocs(file, createParserCb(fn)); 191 | } 192 | 193 | /** 194 | * Read from coffeeDoc 195 | * @api private 196 | * @param {String} file 197 | * @param {Function} fn 198 | */ 199 | function readCoffee(file, fn) { 200 | parseCoffeeDocs(file, createParserCb(fn)); 201 | } 202 | 203 | /** 204 | * Read API from file 205 | * @api private 206 | * @param {String} file 207 | * @param {Function} fn 208 | */ 209 | function readApi(file, fn) { 210 | var ext = path.extname(file); 211 | if ('.js' === ext || '.ts' === ext) { 212 | readJsDoc(file, fn); 213 | } else if ('.yml' === ext) { 214 | readYml(file, fn); 215 | } else if ('.coffee' === ext) { 216 | readCoffee(file, fn); 217 | } else { 218 | throw new Error('Unsupported extension \'' + ext + '\''); 219 | } 220 | } 221 | 222 | /** 223 | * Generate Swagger documents 224 | * @api private 225 | * @param {Object} opt 226 | */ 227 | function generate(opt) { 228 | if (!opt) { 229 | throw new Error('\'option\' is required.'); 230 | } 231 | 232 | if (!opt.swaggerUI) { 233 | throw new Error('\'swaggerUI\' is required.'); 234 | } 235 | 236 | if (!opt.basePath) { 237 | throw new Error('\'basePath\' is required.'); 238 | } 239 | 240 | descriptor.basePath = opt.basePath; 241 | descriptor.apiVersion = (opt.apiVersion) ? opt.apiVersion : '1.0'; 242 | descriptor.swaggerVersion = (opt.swaggerVersion) ? opt.swaggerVersion : '1.0'; 243 | descriptor.swaggerURL = (opt.swaggerURL) ? opt.swaggerURL : '/swagger'; 244 | descriptor.swaggerJSON = (opt.swaggerJSON) ? opt.swaggerJSON : '/api-docs.json'; 245 | descriptor.apis = []; 246 | 247 | if (opt.info) { 248 | descriptor.info = opt.info; 249 | } 250 | 251 | opt.apiVersion = descriptor.apiVersion; 252 | opt.swaggerVersion = descriptor.swaggerVersion; 253 | opt.swaggerURL = descriptor.swaggerURL; 254 | opt.swaggerJSON = descriptor.swaggerJSON; 255 | 256 | if (!opt.fullSwaggerJSONPath) { 257 | opt.fullSwaggerJSONPath = url.parse(opt.basePath + opt.swaggerJSON).path; 258 | } 259 | 260 | if (opt.apis) { 261 | opt.apis.forEach(function(api) { 262 | readApi(api, function(err) { 263 | if (err) { 264 | throw err; 265 | } 266 | }); 267 | }); 268 | } 269 | } 270 | 271 | /** 272 | * Koa middleware 273 | * @api public 274 | * @param {Object} opt 275 | * @return {Function} 276 | */ 277 | exports.init = function(opt) { 278 | // generate resources 279 | generate(opt); 280 | 281 | const swaggerJSON = async (ctx, nextOrResourceName) => { 282 | 283 | let result = _.clone(descriptor); 284 | let resourceName = null; 285 | 286 | if (typeof nextOrResourceName === 'string') { 287 | resourceName = nextOrResourceName; 288 | } 289 | 290 | if (resourceName) { 291 | if (opt.singlePagePath && resourceName === opt.singlePagePath) { 292 | result.apis = []; 293 | result.models = {}; 294 | 295 | Object.keys(resources).forEach((name)=> { 296 | var resource = resources[name]; 297 | for (var attribute in resource) { 298 | if (Array.isArray(resource[attribute])) { 299 | //arrays 300 | result[attribute] = result[attribute] || []; 301 | result[attribute] = result[attribute].concat(resource[attribute]) 302 | } else if (typeof(resource[attribute]) == 'object') { 303 | // objects 304 | result[attribute] = result[attribute] || {}; 305 | result[attribute] = Object.assign(result[attribute], resource[attribute]) 306 | } else { 307 | // primatives (Strings / Numbers etc') 308 | result[attribute] = result[attribute] || resource[attribute]; 309 | } 310 | 311 | } 312 | }); 313 | } else { 314 | var resource = resources['/' + resourceName]; 315 | 316 | if (!resource) { 317 | ctx.status = 404; 318 | return; 319 | } 320 | 321 | result.resourcePath = resource.resourcePath; 322 | result.apis = resource.apis; 323 | result.models = resource.models; 324 | } 325 | } else { 326 | result.apis = _.map(result.apis, function(api) { 327 | return { 328 | path: opt.swaggerJSON + api.resourcePath, 329 | description: api.description 330 | }; 331 | }); 332 | } 333 | 334 | ctx.body = result; 335 | }; 336 | 337 | 338 | const app = new Koa(); 339 | 340 | app.use(async (ctx, next) => { 341 | if (ctx.path === opt.swaggerURL) { // koa static barfs on root url w/o trailing slash 342 | ctx.redirect(ctx.path + '/'); 343 | } else { 344 | await next(); 345 | } 346 | }); 347 | 348 | app.use(route.get(opt.fullSwaggerJSONPath + '/:resourceName*', swaggerJSON)); 349 | app.use(mount(opt.swaggerURL, serve(opt.swaggerUI))); 350 | 351 | return mount(app, '/'); 352 | }; 353 | 354 | exports.descriptor = descriptor; 355 | exports.resources = resources; 356 | -------------------------------------------------------------------------------- /example/public/swagger/lib/underscore-min.js: -------------------------------------------------------------------------------- 1 | // Underscore.js 1.3.3 2 | // (c) 2009-2012 Jeremy Ashkenas, DocumentCloud Inc. 3 | // Underscore is freely distributable under the MIT license. 4 | // Portions of Underscore are inspired or borrowed from Prototype, 5 | // Oliver Steele's Functional, and John Resig's Micro-Templating. 6 | // For all details and documentation: 7 | // http://documentcloud.github.com/underscore 8 | (function(){function r(a,c,d){if(a===c)return 0!==a||1/a==1/c;if(null==a||null==c)return a===c;a._chain&&(a=a._wrapped);c._chain&&(c=c._wrapped);if(a.isEqual&&b.isFunction(a.isEqual))return a.isEqual(c);if(c.isEqual&&b.isFunction(c.isEqual))return c.isEqual(a);var e=l.call(a);if(e!=l.call(c))return!1;switch(e){case "[object String]":return a==""+c;case "[object Number]":return a!=+a?c!=+c:0==a?1/a==1/c:a==+c;case "[object Date]":case "[object Boolean]":return+a==+c;case "[object RegExp]":return a.source== 9 | c.source&&a.global==c.global&&a.multiline==c.multiline&&a.ignoreCase==c.ignoreCase}if("object"!=typeof a||"object"!=typeof c)return!1;for(var f=d.length;f--;)if(d[f]==a)return!0;d.push(a);var f=0,g=!0;if("[object Array]"==e){if(f=a.length,g=f==c.length)for(;f--&&(g=f in a==f in c&&r(a[f],c[f],d)););}else{if("constructor"in a!="constructor"in c||a.constructor!=c.constructor)return!1;for(var h in a)if(b.has(a,h)&&(f++,!(g=b.has(c,h)&&r(a[h],c[h],d))))break;if(g){for(h in c)if(b.has(c,h)&&!f--)break; 10 | g=!f}}d.pop();return g}var s=this,I=s._,o={},k=Array.prototype,p=Object.prototype,i=k.slice,J=k.unshift,l=p.toString,K=p.hasOwnProperty,y=k.forEach,z=k.map,A=k.reduce,B=k.reduceRight,C=k.filter,D=k.every,E=k.some,q=k.indexOf,F=k.lastIndexOf,p=Array.isArray,L=Object.keys,t=Function.prototype.bind,b=function(a){return new m(a)};"undefined"!==typeof exports?("undefined"!==typeof module&&module.exports&&(exports=module.exports=b),exports._=b):s._=b;b.VERSION="1.3.3";var j=b.each=b.forEach=function(a, 11 | c,d){if(a!=null)if(y&&a.forEach===y)a.forEach(c,d);else if(a.length===+a.length)for(var e=0,f=a.length;e2;a==null&&(a=[]);if(A&& 12 | a.reduce===A){e&&(c=b.bind(c,e));return f?a.reduce(c,d):a.reduce(c)}j(a,function(a,b,i){if(f)d=c.call(e,d,a,b,i);else{d=a;f=true}});if(!f)throw new TypeError("Reduce of empty array with no initial value");return d};b.reduceRight=b.foldr=function(a,c,d,e){var f=arguments.length>2;a==null&&(a=[]);if(B&&a.reduceRight===B){e&&(c=b.bind(c,e));return f?a.reduceRight(c,d):a.reduceRight(c)}var g=b.toArray(a).reverse();e&&!f&&(c=b.bind(c,e));return f?b.reduce(g,c,d,e):b.reduce(g,c)};b.find=b.detect=function(a, 13 | c,b){var e;G(a,function(a,g,h){if(c.call(b,a,g,h)){e=a;return true}});return e};b.filter=b.select=function(a,c,b){var e=[];if(a==null)return e;if(C&&a.filter===C)return a.filter(c,b);j(a,function(a,g,h){c.call(b,a,g,h)&&(e[e.length]=a)});return e};b.reject=function(a,c,b){var e=[];if(a==null)return e;j(a,function(a,g,h){c.call(b,a,g,h)||(e[e.length]=a)});return e};b.every=b.all=function(a,c,b){var e=true;if(a==null)return e;if(D&&a.every===D)return a.every(c,b);j(a,function(a,g,h){if(!(e=e&&c.call(b, 14 | a,g,h)))return o});return!!e};var G=b.some=b.any=function(a,c,d){c||(c=b.identity);var e=false;if(a==null)return e;if(E&&a.some===E)return a.some(c,d);j(a,function(a,b,h){if(e||(e=c.call(d,a,b,h)))return o});return!!e};b.include=b.contains=function(a,c){var b=false;if(a==null)return b;if(q&&a.indexOf===q)return a.indexOf(c)!=-1;return b=G(a,function(a){return a===c})};b.invoke=function(a,c){var d=i.call(arguments,2);return b.map(a,function(a){return(b.isFunction(c)?c||a:a[c]).apply(a,d)})};b.pluck= 15 | function(a,c){return b.map(a,function(a){return a[c]})};b.max=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.max.apply(Math,a);if(!c&&b.isEmpty(a))return-Infinity;var e={computed:-Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;b>=e.computed&&(e={value:a,computed:b})});return e.value};b.min=function(a,c,d){if(!c&&b.isArray(a)&&a[0]===+a[0])return Math.min.apply(Math,a);if(!c&&b.isEmpty(a))return Infinity;var e={computed:Infinity};j(a,function(a,b,h){b=c?c.call(d,a,b,h):a;bd?1:0}),"value")};b.groupBy=function(a,c){var d={},e=b.isFunction(c)?c:function(a){return a[c]}; 17 | j(a,function(a,b){var c=e(a,b);(d[c]||(d[c]=[])).push(a)});return d};b.sortedIndex=function(a,c,d){d||(d=b.identity);for(var e=0,f=a.length;e>1;d(a[g])=0})})};b.difference=function(a){var c=b.flatten(i.call(arguments,1),true);return b.filter(a,function(a){return!b.include(c,a)})};b.zip=function(){for(var a= 20 | i.call(arguments),c=b.max(b.pluck(a,"length")),d=Array(c),e=0;e=0;d--)b=[a[d].apply(this,b)];return b[0]}};b.after=function(a,b){return a<=0?b():function(){if(--a<1)return b.apply(this,arguments)}};b.keys=L||function(a){if(a!==Object(a))throw new TypeError("Invalid object");var c=[],d;for(d in a)b.has(a,d)&&(c[c.length]=d);return c};b.values=function(a){return b.map(a,b.identity)};b.functions=b.methods=function(a){var c=[],d;for(d in a)b.isFunction(a[d])&& 25 | c.push(d);return c.sort()};b.extend=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]=b[d]});return a};b.pick=function(a){var c={};j(b.flatten(i.call(arguments,1)),function(b){b in a&&(c[b]=a[b])});return c};b.defaults=function(a){j(i.call(arguments,1),function(b){for(var d in b)a[d]==null&&(a[d]=b[d])});return a};b.clone=function(a){return!b.isObject(a)?a:b.isArray(a)?a.slice():b.extend({},a)};b.tap=function(a,b){b(a);return a};b.isEqual=function(a,b){return r(a,b,[])};b.isEmpty= 26 | function(a){if(a==null)return true;if(b.isArray(a)||b.isString(a))return a.length===0;for(var c in a)if(b.has(a,c))return false;return true};b.isElement=function(a){return!!(a&&a.nodeType==1)};b.isArray=p||function(a){return l.call(a)=="[object Array]"};b.isObject=function(a){return a===Object(a)};b.isArguments=function(a){return l.call(a)=="[object Arguments]"};b.isArguments(arguments)||(b.isArguments=function(a){return!(!a||!b.has(a,"callee"))});b.isFunction=function(a){return l.call(a)=="[object Function]"}; 27 | b.isString=function(a){return l.call(a)=="[object String]"};b.isNumber=function(a){return l.call(a)=="[object Number]"};b.isFinite=function(a){return b.isNumber(a)&&isFinite(a)};b.isNaN=function(a){return a!==a};b.isBoolean=function(a){return a===true||a===false||l.call(a)=="[object Boolean]"};b.isDate=function(a){return l.call(a)=="[object Date]"};b.isRegExp=function(a){return l.call(a)=="[object RegExp]"};b.isNull=function(a){return a===null};b.isUndefined=function(a){return a===void 0};b.has=function(a, 28 | b){return K.call(a,b)};b.noConflict=function(){s._=I;return this};b.identity=function(a){return a};b.times=function(a,b,d){for(var e=0;e/g,">").replace(/"/g,""").replace(/'/g,"'").replace(/\//g,"/")};b.result=function(a,c){if(a==null)return null;var d=a[c];return b.isFunction(d)?d.call(a):d};b.mixin=function(a){j(b.functions(a),function(c){M(c,b[c]=a[c])})};var N=0;b.uniqueId= 29 | function(a){var b=N++;return a?a+b:b};b.templateSettings={evaluate:/<%([\s\S]+?)%>/g,interpolate:/<%=([\s\S]+?)%>/g,escape:/<%-([\s\S]+?)%>/g};var u=/.^/,n={"\\":"\\","'":"'",r:"\r",n:"\n",t:"\t",u2028:"\u2028",u2029:"\u2029"},v;for(v in n)n[n[v]]=v;var O=/\\|'|\r|\n|\t|\u2028|\u2029/g,P=/\\(\\|'|r|n|t|u2028|u2029)/g,w=function(a){return a.replace(P,function(a,b){return n[b]})};b.template=function(a,c,d){d=b.defaults(d||{},b.templateSettings);a="__p+='"+a.replace(O,function(a){return"\\"+n[a]}).replace(d.escape|| 30 | u,function(a,b){return"'+\n_.escape("+w(b)+")+\n'"}).replace(d.interpolate||u,function(a,b){return"'+\n("+w(b)+")+\n'"}).replace(d.evaluate||u,function(a,b){return"';\n"+w(b)+"\n;__p+='"})+"';\n";d.variable||(a="with(obj||{}){\n"+a+"}\n");var a="var __p='';var print=function(){__p+=Array.prototype.join.call(arguments, '')};\n"+a+"return __p;\n",e=new Function(d.variable||"obj","_",a);if(c)return e(c,b);c=function(a){return e.call(this,a,b)};c.source="function("+(d.variable||"obj")+"){\n"+a+"}";return c}; 31 | b.chain=function(a){return b(a).chain()};var m=function(a){this._wrapped=a};b.prototype=m.prototype;var x=function(a,c){return c?b(a).chain():a},M=function(a,c){m.prototype[a]=function(){var a=i.call(arguments);J.call(a,this._wrapped);return x(c.apply(b,a),this._chain)}};b.mixin(b);j("pop,push,reverse,shift,sort,splice,unshift".split(","),function(a){var b=k[a];m.prototype[a]=function(){var d=this._wrapped;b.apply(d,arguments);var e=d.length;(a=="shift"||a=="splice")&&e===0&&delete d[0];return x(d, 32 | this._chain)}});j(["concat","join","slice"],function(a){var b=k[a];m.prototype[a]=function(){return x(b.apply(this._wrapped,arguments),this._chain)}});m.prototype.chain=function(){this._chain=true;return this};m.prototype.value=function(){return this._wrapped}}).call(this); 33 | -------------------------------------------------------------------------------- /example/public/swagger/lib/backbone-min.js: -------------------------------------------------------------------------------- 1 | // Backbone.js 0.9.2 2 | 3 | // (c) 2010-2012 Jeremy Ashkenas, DocumentCloud Inc. 4 | // Backbone may be freely distributed under the MIT license. 5 | // For all details and documentation: 6 | // http://backbonejs.org 7 | (function(){var l=this,y=l.Backbone,z=Array.prototype.slice,A=Array.prototype.splice,g;g="undefined"!==typeof exports?exports:l.Backbone={};g.VERSION="0.9.2";var f=l._;!f&&"undefined"!==typeof require&&(f=require("underscore"));var i=l.jQuery||l.Zepto||l.ender;g.setDomLibrary=function(a){i=a};g.noConflict=function(){l.Backbone=y;return this};g.emulateHTTP=!1;g.emulateJSON=!1;var p=/\s+/,k=g.Events={on:function(a,b,c){var d,e,f,g,j;if(!b)return this;a=a.split(p);for(d=this._callbacks||(this._callbacks= 8 | {});e=a.shift();)f=(j=d[e])?j.tail:{},f.next=g={},f.context=c,f.callback=b,d[e]={tail:g,next:j?j.next:f};return this},off:function(a,b,c){var d,e,h,g,j,q;if(e=this._callbacks){if(!a&&!b&&!c)return delete this._callbacks,this;for(a=a?a.split(p):f.keys(e);d=a.shift();)if(h=e[d],delete e[d],h&&(b||c))for(g=h.tail;(h=h.next)!==g;)if(j=h.callback,q=h.context,b&&j!==b||c&&q!==c)this.on(d,j,q);return this}},trigger:function(a){var b,c,d,e,f,g;if(!(d=this._callbacks))return this;f=d.all;a=a.split(p);for(g= 9 | z.call(arguments,1);b=a.shift();){if(c=d[b])for(e=c.tail;(c=c.next)!==e;)c.callback.apply(c.context||this,g);if(c=f){e=c.tail;for(b=[b].concat(g);(c=c.next)!==e;)c.callback.apply(c.context||this,b)}}return this}};k.bind=k.on;k.unbind=k.off;var o=g.Model=function(a,b){var c;a||(a={});b&&b.parse&&(a=this.parse(a));if(c=n(this,"defaults"))a=f.extend({},c,a);b&&b.collection&&(this.collection=b.collection);this.attributes={};this._escapedAttributes={};this.cid=f.uniqueId("c");this.changed={};this._silent= 10 | {};this._pending={};this.set(a,{silent:!0});this.changed={};this._silent={};this._pending={};this._previousAttributes=f.clone(this.attributes);this.initialize.apply(this,arguments)};f.extend(o.prototype,k,{changed:null,_silent:null,_pending:null,idAttribute:"id",initialize:function(){},toJSON:function(){return f.clone(this.attributes)},get:function(a){return this.attributes[a]},escape:function(a){var b;if(b=this._escapedAttributes[a])return b;b=this.get(a);return this._escapedAttributes[a]=f.escape(null== 11 | b?"":""+b)},has:function(a){return null!=this.get(a)},set:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c||(c={});if(!d)return this;d instanceof o&&(d=d.attributes);if(c.unset)for(e in d)d[e]=void 0;if(!this._validate(d,c))return!1;this.idAttribute in d&&(this.id=d[this.idAttribute]);var b=c.changes={},h=this.attributes,g=this._escapedAttributes,j=this._previousAttributes||{};for(e in d){a=d[e];if(!f.isEqual(h[e],a)||c.unset&&f.has(h,e))delete g[e],(c.silent?this._silent: 12 | b)[e]=!0;c.unset?delete h[e]:h[e]=a;!f.isEqual(j[e],a)||f.has(h,e)!=f.has(j,e)?(this.changed[e]=a,c.silent||(this._pending[e]=!0)):(delete this.changed[e],delete this._pending[e])}c.silent||this.change(c);return this},unset:function(a,b){(b||(b={})).unset=!0;return this.set(a,null,b)},clear:function(a){(a||(a={})).unset=!0;return this.set(f.clone(this.attributes),a)},fetch:function(a){var a=a?f.clone(a):{},b=this,c=a.success;a.success=function(d,e,f){if(!b.set(b.parse(d,f),a))return!1;c&&c(b,d)}; 13 | a.error=g.wrapError(a.error,b,a);return(this.sync||g.sync).call(this,"read",this,a)},save:function(a,b,c){var d,e;f.isObject(a)||null==a?(d=a,c=b):(d={},d[a]=b);c=c?f.clone(c):{};if(c.wait){if(!this._validate(d,c))return!1;e=f.clone(this.attributes)}a=f.extend({},c,{silent:!0});if(d&&!this.set(d,c.wait?a:c))return!1;var h=this,i=c.success;c.success=function(a,b,e){b=h.parse(a,e);if(c.wait){delete c.wait;b=f.extend(d||{},b)}if(!h.set(b,c))return false;i?i(h,a):h.trigger("sync",h,a,c)};c.error=g.wrapError(c.error, 14 | h,c);b=this.isNew()?"create":"update";b=(this.sync||g.sync).call(this,b,this,c);c.wait&&this.set(e,a);return b},destroy:function(a){var a=a?f.clone(a):{},b=this,c=a.success,d=function(){b.trigger("destroy",b,b.collection,a)};if(this.isNew())return d(),!1;a.success=function(e){a.wait&&d();c?c(b,e):b.trigger("sync",b,e,a)};a.error=g.wrapError(a.error,b,a);var e=(this.sync||g.sync).call(this,"delete",this,a);a.wait||d();return e},url:function(){var a=n(this,"urlRoot")||n(this.collection,"url")||t(); 15 | return this.isNew()?a:a+("/"==a.charAt(a.length-1)?"":"/")+encodeURIComponent(this.id)},parse:function(a){return a},clone:function(){return new this.constructor(this.attributes)},isNew:function(){return null==this.id},change:function(a){a||(a={});var b=this._changing;this._changing=!0;for(var c in this._silent)this._pending[c]=!0;var d=f.extend({},a.changes,this._silent);this._silent={};for(c in d)this.trigger("change:"+c,this,this.get(c),a);if(b)return this;for(;!f.isEmpty(this._pending);){this._pending= 16 | {};this.trigger("change",this,a);for(c in this.changed)!this._pending[c]&&!this._silent[c]&&delete this.changed[c];this._previousAttributes=f.clone(this.attributes)}this._changing=!1;return this},hasChanged:function(a){return!arguments.length?!f.isEmpty(this.changed):f.has(this.changed,a)},changedAttributes:function(a){if(!a)return this.hasChanged()?f.clone(this.changed):!1;var b,c=!1,d=this._previousAttributes,e;for(e in a)if(!f.isEqual(d[e],b=a[e]))(c||(c={}))[e]=b;return c},previous:function(a){return!arguments.length|| 17 | !this._previousAttributes?null:this._previousAttributes[a]},previousAttributes:function(){return f.clone(this._previousAttributes)},isValid:function(){return!this.validate(this.attributes)},_validate:function(a,b){if(b.silent||!this.validate)return!0;var a=f.extend({},this.attributes,a),c=this.validate(a,b);if(!c)return!0;b&&b.error?b.error(this,c,b):this.trigger("error",this,c,b);return!1}});var r=g.Collection=function(a,b){b||(b={});b.model&&(this.model=b.model);b.comparator&&(this.comparator=b.comparator); 18 | this._reset();this.initialize.apply(this,arguments);a&&this.reset(a,{silent:!0,parse:b.parse})};f.extend(r.prototype,k,{model:o,initialize:function(){},toJSON:function(a){return this.map(function(b){return b.toJSON(a)})},add:function(a,b){var c,d,e,g,i,j={},k={},l=[];b||(b={});a=f.isArray(a)?a.slice():[a];c=0;for(d=a.length;c=b))this.iframe=i('