├── index.js ├── .travis.yml ├── dev ├── karmaConfig.js └── jsHintConfig.js ├── .gitignore ├── gulpfile.js ├── example ├── schemaUpdater.js ├── index.html └── schema.json ├── LICENSE-APACHE2 ├── bower.json ├── package.json ├── src ├── getRequestBody.js ├── getRequestUrl.js ├── applyAuthData.js ├── getRequestHeaders.js ├── getRequestUrlSpec.js ├── errorTypes.js ├── createClientSpec.js ├── getRequestHeadersSpec.js ├── createClient.js ├── getRequestBodySpec.js ├── applyAuthDataSpec.js ├── createOperationHandler.js └── createOperationHandlerSpec.js ├── README.md └── dist ├── swagger-client-generator.min.js └── swagger-client-generator.min.js.map /index.js: -------------------------------------------------------------------------------- 1 | module.exports = require('./src/createClient'); -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: node_js 2 | node_js: 3 | - "0.10" 4 | -------------------------------------------------------------------------------- /dev/karmaConfig.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | files: [ 3 | 'src/**/*.js', 4 | 'node_modules/swagger-validate/src/*.js' 5 | ] 6 | }; -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Local-only directory 2 | /local/ 3 | 4 | # Module manager 5 | /node_modules/ 6 | /bower_components/ 7 | 8 | # Generated 9 | /build/ 10 | /reports/ 11 | -------------------------------------------------------------------------------- /gulpfile.js: -------------------------------------------------------------------------------- 1 | var gulp = require('gulp'), 2 | boilerplate = require('boilerplate-gulp'); 3 | 4 | boilerplate(gulp, { 5 | pkg: require('./package.json'), 6 | jsMain: './src/createClient.js', 7 | karmaConfig: require('./dev/karmaConfig'), 8 | jsHintConfig: require('./dev/jsHintConfig'), 9 | disableCss: true 10 | }); -------------------------------------------------------------------------------- /example/schemaUpdater.js: -------------------------------------------------------------------------------- 1 | // run npm install fetch-swagger-schema first 2 | var path = require('path'); 3 | var fs = require('fs'); 4 | var fetchSchema = module.require('fetch-swagger-schema'); 5 | 6 | var apiDocs = 'http://petstore.swagger.io/api/api-docs'; 7 | var destination = __dirname + '/schema.json'; 8 | 9 | fetchSchema(apiDocs, function(err, schema){ 10 | if(err) return console.error(err); 11 | 12 | destination = path.resolve(process.cwd(), destination); 13 | fs.writeFileSync(destination, JSON.stringify(schema)); 14 | }); -------------------------------------------------------------------------------- /LICENSE-APACHE2: -------------------------------------------------------------------------------- 1 | Copyright 2014 SignalFuse 2 | 3 | Licensed under the Apache License, Version 2.0 (the "License"); 4 | you may not use this file except in compliance with the License. 5 | You may obtain a copy of the License at 6 | 7 | http://www.apache.org/licenses/LICENSE-2.0 8 | 9 | Unless required by applicable law or agreed to in writing, software 10 | distributed under the License is distributed on an "AS IS" BASIS, 11 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 | See the License for the specific language governing permissions and 13 | limitations under the License. 14 | -------------------------------------------------------------------------------- /bower.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-client-generator", 3 | "version": "0.2.12", 4 | "description": "Automatically generate an api object from a given swagger schema", 5 | "keywords": [ 6 | "swagger", 7 | "api", 8 | "generator" 9 | ], 10 | "main": [ 11 | "dist/swagger-client-generator.js" 12 | ], 13 | "homepage": "https://github.com/signalfx/swagger-client-generator", 14 | "authors": [ 15 | "Ozan Turgut " 16 | ], 17 | "moduleType": [ 18 | "amd", 19 | "node", 20 | "globals" 21 | ], 22 | "license": "APACHE 2", 23 | "ignore": [ 24 | "**/.*", 25 | "node_modules", 26 | "bower_components", 27 | "example", 28 | "src", 29 | "build", 30 | "dev", 31 | "index.js", 32 | "package.json", 33 | "gulpfile.js" 34 | ], 35 | "repository": { 36 | "type": "git", 37 | "url": "git@github.com:signalfx/swagger-client-generator.git" 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /example/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Swagger JS API Example 6 | 7 | 8 | Example: 9 | 10 | 11 | 12 | 42 | 43 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "swagger-client-generator", 3 | "version": "0.2.13", 4 | "description": "", 5 | "keywords": [], 6 | "main": "./index.js", 7 | "devDependencies": { 8 | "gulp": "^3.8.5", 9 | "boilerplate-gulp": "^0.2.1", 10 | "bower": "^1.3.8" 11 | }, 12 | "directories": { 13 | "build": "./build", 14 | "dist": "./dist", 15 | "reports": "./reports", 16 | "dev": "./dev", 17 | "example": "./example" 18 | }, 19 | "author": { 20 | "name": "Ozan Turgut", 21 | "email": "ozanturgut@gmail.com", 22 | "url": "http://ozan.io" 23 | }, 24 | "scripts": { 25 | "prepublish": "node ./node_modules/bower/bin/bower install && node ./node_modules/gulp/bin/gulp.js", 26 | "test": "node ./node_modules/gulp/bin/gulp.js test", 27 | "dev": "node ./node_modules/gulp/bin/gulp.js dev", 28 | "dist": "node ./node_modules/gulp/bin/gulp.js" 29 | }, 30 | "licenses": [ 31 | { 32 | "type": "Apache License 2.0", 33 | "url": "https://github.com/signalfx/swagger-client-generator/blob/master/LICENSE-APACHE2" 34 | } 35 | ], 36 | "repository": { 37 | "type": "git", 38 | "url": "git://github.com/signalfx/swagger-client-generator.git" 39 | }, 40 | "bugs": { 41 | "url": "https://github.com/signalfx/swagger-client-generator/issues" 42 | }, 43 | "homepage": "https://github.com/signalfx/swagger-client-generator", 44 | "dependencies": { 45 | "swagger-validate": "^0.1.1" 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /dev/jsHintConfig.js: -------------------------------------------------------------------------------- 1 | // Modified from the Airbnb JS Styleguide 2 | module.exports = { 3 | /* 4 | * ENVIRONMENTS 5 | * ================= 6 | */ 7 | 8 | // Define globals exposed by modern browsers. 9 | browser: true, 10 | 11 | // Define globals exposed by Node.js. 12 | node: true, 13 | 14 | 15 | /* 16 | * ENFORCING OPTIONS 17 | * ================= 18 | */ 19 | 20 | // Prohibit use of == and != in favor of === and !==. 21 | eqeqeq: true, 22 | 23 | // Enforce tab width of 2 spaces. 24 | indent: 2, 25 | 26 | // Require capitalized names for constructor functions. 27 | newcap: true, 28 | 29 | // Prohibit trailing whitespace. 30 | trailing: true, 31 | 32 | // Force all variable names to use either camelCase style or UPPER_CASE 33 | // with underscores. 34 | camelcase: true, 35 | 36 | // Enforce use of single quotation marks for strings. 37 | quotmark: 'single', 38 | 39 | // Prohibit use of explicitly undeclared variables. 40 | undef: true, 41 | 42 | // Warn when variables are defined but never used. 43 | unused: true, 44 | 45 | // Enforce line length to 120 characters 46 | maxlen: 120, 47 | 48 | // Enforce placing 'use strict' at the top function scope 49 | strict: true, 50 | 51 | 52 | /* 53 | * RELAXING OPTIONS 54 | * ================ 55 | */ 56 | 57 | // Suppress warnings about == null comparisons. 58 | eqnull: true, 59 | 60 | // Prohibit use of a variable before it is defined. 61 | latedef: false, 62 | 63 | globals: { 64 | // Jasmine Globals 65 | beforeEach: true, 66 | afterEach: true, 67 | describe: true, 68 | expect: true, 69 | it: true, 70 | spyOn: true, 71 | xdescribe: true, 72 | xit: true 73 | } 74 | }; -------------------------------------------------------------------------------- /src/getRequestBody.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | module.exports = function getRequestBody(operation, data, headers){ 4 | var body = operation.parameters.filter(function(param){ 5 | return param.paramType === 'body' && data[param.name] != null; 6 | }).map(function(param){ 7 | return data[param.name]; 8 | })[0]; 9 | 10 | if(!(headers && headers['Content-Type'])) return body; 11 | 12 | var contentType = headers['Content-Type']; 13 | var presentFormParams = operation.parameters.filter(function(param){ 14 | return param.paramType === 'form' && data[param.name] != null; 15 | }); 16 | 17 | if(contentType.indexOf('application/x-www-form-urlencoded') !== -1){ 18 | body = presentFormParams.map(function(param){ 19 | var key = param.name, 20 | value = data[key]; 21 | return encodeURIComponent(key) + '=' + encodeURIComponent(value); 22 | }).join('&'); 23 | } else if(contentType.indexOf('multipart/form-data') !== -1){ 24 | var randomness = Math.random().toString(16).substr(2); 25 | var boundary = 'SwaggerBoundary' + randomness; 26 | 27 | body = presentFormParams.map(function(param){ 28 | var key = param.name, 29 | value = data[key], 30 | result = '--' + boundary; 31 | 32 | result += '\nContent-Disposition: form-data; name="' + key + '"'; 33 | 34 | if(value.contentType){ 35 | if(value.name){ 36 | result += '; filename="' + value.name + '"'; 37 | } 38 | 39 | result += '\nContent-Type: ' + value.contentType; 40 | } 41 | 42 | if(value.contentTransferEncoding){ 43 | result += '\nContent-Transfer-Encoding: ' + value.contentTransferEncoding; 44 | } 45 | 46 | if(value.body){ 47 | result += '\n\n' + value.body; 48 | } else { 49 | result += '\n\n' + value; 50 | } 51 | 52 | return result; 53 | }).join('\n'); 54 | 55 | body += '\n--' + boundary + '--\n'; 56 | 57 | headers['Content-Type'] = contentType.replace( 58 | 'multipart/form-data', 59 | 'multipart/form-data; boundary=' + boundary 60 | ); 61 | } else if(contentType.indexOf('application/json') !== -1){ 62 | if(typeof body !== 'string'){ 63 | body = JSON.stringify(body); 64 | } 65 | } 66 | 67 | return body; 68 | }; -------------------------------------------------------------------------------- /src/getRequestUrl.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var errorTypes = require('./errorTypes'), 4 | MissingPathParamsError = errorTypes.MissingPathParamsError; 5 | 6 | module.exports = function getRequestUrl(operation, data){ 7 | var url = getUrlTemplate(operation); 8 | 9 | url = applyPathParams(url, operation, data); 10 | 11 | if(!data) return url; 12 | 13 | var queryParams = operation.parameters.filter(function(param){ 14 | return param.paramType === 'query' && data[param.name] !== undefined; 15 | }).map(function(param){ 16 | var key = param.name; 17 | var encodedKey = encodeURIComponent(key); 18 | var value = data[key]; 19 | 20 | // For arrays, create multiple of the same query params to accomodate 21 | // the spec ambiguity on the issue: http://docs.oracle.com/javaee/6/api/ 22 | // javax/servlet/ServletRequest.html#getParameterValues(java.lang.String) 23 | if(param.type === 'array' && Array.isArray(value)){ 24 | return value.map(function(item){ 25 | return encodedKey + '=' + encodeURIComponent(item); 26 | }).join('&'); 27 | } else { 28 | return encodedKey + '=' + encodeURIComponent(value); 29 | } 30 | }).join('&'); 31 | 32 | if(queryParams) url += '?' + queryParams; 33 | 34 | return url; 35 | }; 36 | 37 | function applyPathParams(url, operation, data){ 38 | var pathParams = operation.parameters.filter(function(param){ 39 | return param.paramType === 'path'; 40 | }); 41 | 42 | var missingParams = pathParams.filter(function(param){ 43 | return data[param.name] === undefined; 44 | }); 45 | 46 | if(missingParams.length){ 47 | throw new MissingPathParamsError(missingParams.map(function(param){ 48 | return param.name; 49 | })); 50 | } 51 | 52 | pathParams.forEach(function(param){ 53 | var key = param.name; 54 | 55 | var exp = new RegExp('{' + key + '[^}]*}', 'gi'); 56 | 57 | var value = data[key].toString(); 58 | delete data[key]; 59 | value = value.split('/').map(encodeURIComponent).join('/'); 60 | 61 | url = url.replace(exp, value); 62 | }); 63 | 64 | return url; 65 | } 66 | 67 | function getUrlTemplate(operation){ 68 | var apiObject = operation.apiObject; 69 | 70 | var basePath = apiObject.apiDeclaration.basePath; 71 | var path = apiObject.path.replace('{format}', 'json'); 72 | 73 | return basePath + path; 74 | } 75 | -------------------------------------------------------------------------------- /src/applyAuthData.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var MissingAuthorizationError = require('./errorTypes').MissingAuthorizationError; 4 | 5 | module.exports = function applyAuthData(operation, authData, request){ 6 | var authMap = operation.authorizations; 7 | if(!authMap) authMap = operation.apiObject.apiDeclaration.authorizations; 8 | if(!authMap) return; 9 | 10 | var authNames = Object.keys(authMap).filter(function(authName){ 11 | // Currently unable to handle oauth2 12 | return authMap[authName].type !== 'oauth2'; 13 | }); 14 | 15 | if(authNames.length === 0) return; 16 | 17 | if(authNames.length === 1){ 18 | var authName = authNames[0]; 19 | var auth = authMap[authName]; 20 | 21 | if(!authData) throw new MissingAuthorizationError(authName, auth); 22 | 23 | // Unpack nested authData for single auth ops: { apiKey: '123' } -> '123' 24 | if(authData[authName]) authData = authData[authName]; 25 | 26 | if(auth.type === 'apiKey'){ 27 | applyApiKey(auth, authName, authData, request); 28 | } else if(auth.type === 'basicAuth') { 29 | applyBasicAuth(auth, authName, authData.username, authData.password, request); 30 | } 31 | } else { 32 | var hasAuth = authNames.some(function(authName){ 33 | var auth = authMap[authName]; 34 | var data = authData[authName]; 35 | 36 | if(!data) return false; 37 | 38 | if(auth.type === 'apiKey'){ 39 | applyApiKey(auth, authName, data, request); 40 | } else if(auth.type === 'basicAuth'){ 41 | applyBasicAuth(auth, authName, data.username, data.password, request); 42 | } 43 | 44 | return true; 45 | }); 46 | 47 | if(!hasAuth){ 48 | throw new MissingAuthorizationError(authNames.join(', '), authMap); 49 | } 50 | } 51 | }; 52 | 53 | function applyApiKey(auth, authName, apiKey, request){ 54 | if(!apiKey) throw new MissingAuthorizationError(authName, auth); 55 | 56 | if(auth.passAs === 'header'){ 57 | request.headers[auth.keyname] = apiKey; 58 | } else if(auth.passAs === 'query'){ 59 | var url = request.url; 60 | var queryParam = auth.keyname + '=' + encodeURIComponent(apiKey); 61 | if(url.indexOf('?') === -1){ 62 | url += '?' + queryParam; 63 | } else { 64 | url = url.replace('?', '?' + queryParam + '&'); 65 | } 66 | 67 | request.url = url; 68 | } 69 | } 70 | 71 | function applyBasicAuth(auth, authName, username, password, request){ 72 | if(!username || !password) throw new MissingAuthorizationError(authName, auth); 73 | 74 | var url = request.url; 75 | 76 | // Only add basic auth once 77 | if(url.indexOf('@') === -1){ 78 | url = url.replace('://', '://' + username + ':' + password + '@'); 79 | } 80 | 81 | request.url = url; 82 | } -------------------------------------------------------------------------------- /src/getRequestHeaders.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var errorTypes = require('./errorTypes'), 4 | ContentTypeNotSupportedError = errorTypes.ContentTypeNotSupportedError, 5 | AcceptsNotSupportedError = errorTypes.AcceptsNotSupportedError; 6 | 7 | var DEFAULT_ACCEPT = 'application/json'; 8 | module.exports = function getRequestHeaders(operation, data, options){ 9 | data = data || {}; 10 | options = options || {}; 11 | 12 | var headers = {}; 13 | 14 | operation.parameters.forEach(function(param){ 15 | if(param.paramType === 'header' && data[param.name] != null){ 16 | headers[param.name] = data[param.name]; 17 | } 18 | }); 19 | 20 | // Passed headers 21 | if(options.headers){ 22 | Object.keys(options.headers).forEach(function(key){ 23 | headers[key] = options.headers[key]; 24 | }); 25 | } 26 | 27 | // Content-Type 28 | var contentType = options.contentType || getContentType(operation, data, options); 29 | if(contentType) { 30 | if(hasAccept(operation, contentType)){ 31 | headers['Content-Type'] = contentType; 32 | } else { 33 | throw new ContentTypeNotSupportedError(contentType, operation); 34 | } 35 | } 36 | 37 | // Accept 38 | var accept = options.accept || DEFAULT_ACCEPT; 39 | if(accept){ 40 | if(hasContentType(operation, accept)){ 41 | headers.Accept = accept; 42 | } else { 43 | throw new AcceptsNotSupportedError(accept, operation); 44 | } 45 | } 46 | 47 | return headers; 48 | }; 49 | 50 | function getContentType(operation, data){ 51 | var hasBody = operation.parameters.some(function(param){ 52 | return param.paramType === 'body' && data[param.name] !== undefined; 53 | }); 54 | 55 | if (hasBody){ 56 | return 'application/json'; 57 | } else { 58 | var hasFormParams = operation.parameters.some(function(param){ 59 | return param.paramType === 'form' && data[param.name] !== undefined; 60 | }); 61 | 62 | var hasFileParam = hasFormParams && 63 | operation.parameters.some(function(param){ 64 | return param.type === 'File' && data[param.name] !== undefined; 65 | }); 66 | 67 | if(hasFileParam) return 'multipart/form-data'; 68 | else if(hasFormParams) return 'application/x-www-form-urlencoded'; 69 | } 70 | } 71 | 72 | // Accepts is an optional field in the spec, but must be enforced when present 73 | function hasAccept(operation, contentType){ 74 | var apiDeclaration = operation.apiObject.apiDeclaration; 75 | var accepts = operation.consumes || apiDeclaration.consumes; 76 | 77 | if(accepts && accepts.length){ 78 | return accepts.indexOf(contentType) !== -1; 79 | } else { 80 | return true; 81 | } 82 | } 83 | exports.hasAccept = hasAccept; 84 | 85 | // Content-Type (produces) is an optional field in the spec, but must be enforced when present 86 | function hasContentType(operation, contentType){ 87 | var apiDeclaration = operation.apiObject.apiDeclaration, 88 | contentTypes = operation.produces || apiDeclaration.produces; 89 | 90 | if(contentTypes && contentTypes.length){ 91 | return contentTypes.indexOf(contentType) !== -1; 92 | } else { 93 | return true; 94 | } 95 | } 96 | exports.hasContentType = hasContentType; -------------------------------------------------------------------------------- /src/getRequestUrlSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var getRequestUrl = require('./getRequestUrl'); 4 | describe('get request url', function(){ 5 | var basicOperation, 6 | complexOperation, 7 | arrayOperation; 8 | 9 | beforeEach(function(){ 10 | basicOperation = { 11 | apiObject: { 12 | apiDeclaration: { 13 | basePath: 'http://example.com/api' 14 | }, 15 | path: '/do/it' 16 | }, 17 | parameters: [ 18 | { 19 | paramType: 'query', 20 | type: 'string', 21 | name: 'what' 22 | } 23 | ] 24 | }; 25 | 26 | arrayOperation = { 27 | apiObject: { 28 | apiDeclaration: { 29 | basePath: 'http://example.com/api' 30 | }, 31 | path: '/do/it' 32 | }, 33 | parameters: [ 34 | { 35 | paramType: 'query', 36 | type: 'array', 37 | name: 'listOfStuff', 38 | items: { 39 | type: 'string' 40 | } 41 | } 42 | ] 43 | }; 44 | 45 | complexOperation = { 46 | apiObject: { 47 | apiDeclaration: { 48 | basePath: 'http://example.com/api' 49 | }, 50 | path: '/do/{what}.{format}' 51 | }, 52 | parameters: [ 53 | { 54 | paramType: 'path', 55 | type: 'string', 56 | name: 'what' 57 | }, 58 | { 59 | paramType: 'form', 60 | type: 'string', 61 | name: 'notRelevant' 62 | }, 63 | { 64 | paramType: 'query', 65 | type: 'string', 66 | name: 'where' 67 | }, 68 | { 69 | paramType: 'query', 70 | type: 'string', 71 | name: 'when' 72 | } 73 | ] 74 | }; 75 | }); 76 | 77 | it('provides the operation url when there is no data', function(){ 78 | expect(getRequestUrl(basicOperation)) 79 | .toBe('http://example.com/api/do/it'); 80 | }); 81 | 82 | it('fills out path params from given data', function(){ 83 | expect(getRequestUrl(complexOperation, {what: 'that'})) 84 | .toBe('http://example.com/api/do/that.json'); 85 | }); 86 | 87 | it('fills out query params from given array data', function(){ 88 | expect(getRequestUrl(arrayOperation, {listOfStuff: ['a', 'b', 'c']})) 89 | .toBe('http://example.com/api/do/it?listOfStuff=a&listOfStuff=b&listOfStuff=c'); 90 | }); 91 | 92 | it('throws an error if a required path param is missing', function(){ 93 | expect(function(){ 94 | getRequestUrl(complexOperation); 95 | }).toThrow(); 96 | }); 97 | 98 | it('adds present query params', function(){ 99 | expect(getRequestUrl(complexOperation, { 100 | what: 'that', where: 'there', when: 'then' 101 | })) 102 | .toBe('http://example.com/api/do/that.json?where=there&when=then'); 103 | }); 104 | 105 | it('doesn\'t mind the presence of other params (such as form params)', function(){ 106 | expect(getRequestUrl(complexOperation, { 107 | what: 'that', where: 'there', when: 'then', notRelevant: 'popcorn' 108 | })) 109 | .toBe('http://example.com/api/do/that.json?where=there&when=then'); 110 | }); 111 | }); -------------------------------------------------------------------------------- /src/errorTypes.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | function InvalidRequestError(message){ 4 | this.name = 'InvalidRequestError'; 5 | this.message = message || 'Invalid request'; 6 | } 7 | InvalidRequestError.prototype = Object.create(Error.prototype); 8 | InvalidRequestError.prototype.constructor = InvalidRequestError; 9 | 10 | exports.InvalidRequestError = InvalidRequestError; 11 | 12 | 13 | function MissingAuthorizationError(authName, auth){ 14 | this.name = 'MissingAuthorizationError'; 15 | this.message = 'No data found for authorization: ' + authName; 16 | this.authorization = auth; 17 | } 18 | MissingAuthorizationError.prototype = Object.create(InvalidRequestError.prototype); 19 | MissingAuthorizationError.prototype.constructor = MissingAuthorizationError; 20 | 21 | exports.MissingAuthorizationError = MissingAuthorizationError; 22 | 23 | 24 | function MissingPathParamsError(pathParams){ 25 | this.name = 'MissingPathParamsError'; 26 | this.message = 'Missing the following required path parameters: ' + pathParams.join(''); 27 | } 28 | MissingPathParamsError.prototype = Object.create(InvalidRequestError.prototype); 29 | MissingPathParamsError.prototype.constructor = MissingPathParamsError; 30 | 31 | exports.MissingPathParamsError = MissingPathParamsError; 32 | 33 | 34 | function ContentTypeNotSupportedError(contentType, operation){ 35 | var apiDeclaration = operation.apiObject.apiDeclaration; 36 | var consumes = operation.consumes || apiDeclaration.consumes || []; 37 | 38 | this.name = 'ContentTypeNotSupportedError'; 39 | this.message = 'Operation [' + operation.nickname + '] does not accept ' + contentType + '. It supports: ' + 40 | consumes.join(', '); 41 | } 42 | ContentTypeNotSupportedError.prototype = Object.create(InvalidRequestError.prototype); 43 | ContentTypeNotSupportedError.prototype.constructor = ContentTypeNotSupportedError; 44 | 45 | exports.ContentTypeNotSupportedError = ContentTypeNotSupportedError; 46 | 47 | 48 | function AcceptsNotSupportedError(accepts, operation){ 49 | var apiDeclaration = operation.apiObject.apiDeclaration; 50 | var produces = operation.produces || apiDeclaration.produces || []; 51 | 52 | this.name = 'AcceptsNotSupportedError'; 53 | this.message = 'Operation [' + operation.nickname + '] does not produce ' + accepts + '. It supports: ' + 54 | produces.join(', '); 55 | } 56 | AcceptsNotSupportedError.prototype = Object.create(InvalidRequestError.prototype); 57 | AcceptsNotSupportedError.prototype.constructor = AcceptsNotSupportedError; 58 | 59 | exports.AcceptsNotSupportedError = AcceptsNotSupportedError; 60 | 61 | 62 | function OperationValidationError(operation, errors){ 63 | this.name = 'OperationValidationError'; 64 | this.message = operation.nickname + ' failed validation: \n\t' + errors.join('\n\t'); 65 | } 66 | OperationValidationError.prototype = Object.create(InvalidRequestError.prototype); 67 | OperationValidationError.prototype.constructor = OperationValidationError; 68 | 69 | exports.OperationValidationError = OperationValidationError; 70 | 71 | 72 | function ParameterValidationError(parameter, errors){ 73 | this.name = 'ParameterValidationError'; 74 | this.message = parameter.name + ' failed validation: \n\t' + errors.join('\n\t'); 75 | } 76 | ParameterValidationError.prototype = Object.create(InvalidRequestError.prototype); 77 | ParameterValidationError.prototype.constructor = ParameterValidationError; 78 | 79 | exports.ParameterValidationError = ParameterValidationError; 80 | 81 | 82 | function DataTypeValidationError(message){ 83 | this.name = 'DataTypeValidationError'; 84 | this.message = message || 'Invalid data type'; 85 | } 86 | DataTypeValidationError.prototype = Object.create(Error.prototype); 87 | DataTypeValidationError.prototype.constructor = DataTypeValidationError; 88 | 89 | exports.DataTypeValidationError = DataTypeValidationError; -------------------------------------------------------------------------------- /src/createClientSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* global jasmine */ 3 | 4 | var createClient = require('./createClient'); 5 | 6 | describe('create client', function(){ 7 | var schema, 8 | promise, 9 | requestHandler; 10 | 11 | beforeEach(function(){ 12 | promise = { 13 | then: jasmine.createSpy('promise.then') 14 | }; 15 | 16 | requestHandler = jasmine.createSpy('requestHandler').and.returnValue(promise); 17 | 18 | schema = { 19 | apis: [{ 20 | apiDeclaration: { 21 | resourcePath: '/resource', 22 | basePath: 'http://example.com/api', 23 | apis: [{ 24 | path: '/resource/all-of-it', 25 | operations: [{ 26 | method: 'GET', 27 | nickname: 'doIt', 28 | parameters: [{ 29 | paramType: 'query', 30 | type: 'string', 31 | name: 'queryParam' 32 | }] 33 | }] 34 | }] 35 | } 36 | }] 37 | }; 38 | }); 39 | 40 | it('uses the resource path if it\'s available for the api name', function(){ 41 | var client = createClient(schema, requestHandler); 42 | expect(client.resource).toBeDefined(); 43 | }); 44 | 45 | it('uses the apiObject path as a fallback', function(){ 46 | delete schema.apis[0].apiDeclaration.resourcePath; 47 | var client = createClient(schema, requestHandler); 48 | expect(client.resourceAllOfIt).toBeDefined(); 49 | }); 50 | 51 | it('uses the operation nickname for the operation name', function(){ 52 | var client = createClient(schema, requestHandler); 53 | expect(client.resource.doIt).toBeDefined(); 54 | }); 55 | 56 | it('has the ability to set auth at many levels', function(){ 57 | var client = createClient(schema, requestHandler); 58 | 59 | expect(function(){ 60 | client.auth('api-level-auth'); 61 | client.resource.auth('resource-level-auth'); 62 | client.resource.doIt.auth('operation-level-auth'); 63 | }).not.toThrow(); 64 | }); 65 | 66 | it('uses "authorize" instead of "auth" for the auth method name if the api already' + 67 | 'makes use of "auth" in the schema', function(){ 68 | schema.apis[0].apiDeclaration.resourcePath = '/auth'; 69 | var client = createClient(schema, requestHandler); 70 | expect(client.auth).toBeDefined(); 71 | expect(client.authorization).toBeDefined(); 72 | }); 73 | 74 | it('provides the most specific auth data passed in to it (resource-level)', function(){ 75 | schema.apis[0].apiDeclaration.authorizations = { 76 | apiKey: { 77 | type: 'apiKey', 78 | passAs: 'query', 79 | keyname: 'token' 80 | } 81 | }; 82 | var client = createClient(schema, requestHandler); 83 | 84 | client.auth('api-level-auth'); 85 | client.resource.auth('resource-level-auth'); 86 | client.resource.doIt('1'); 87 | expect(requestHandler.calls.mostRecent().args[1].url) 88 | .toBe('http://example.com/api/resource/all-of-it?token=resource-level-auth&queryParam=1'); 89 | }); 90 | 91 | it('provides the most specific auth data passed in to it (op-level)', function(){ 92 | schema.apis[0].apiDeclaration.authorizations = { 93 | apiKey: { 94 | type: 'apiKey', 95 | passAs: 'query', 96 | keyname: 'token' 97 | } 98 | }; 99 | var client = createClient(schema, requestHandler); 100 | 101 | client.auth('api-level-auth'); 102 | client.resource.auth('resource-level-auth'); 103 | client.resource.doIt.auth('operation-level-auth'); 104 | client.resource.doIt('1'); 105 | expect(requestHandler.calls.mostRecent().args[1].url) 106 | .toBe('http://example.com/api/resource/all-of-it?token=operation-level-auth&queryParam=1'); 107 | }); 108 | }); -------------------------------------------------------------------------------- /src/getRequestHeadersSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var getRequestHeaders = require('./getRequestHeaders'); 4 | 5 | describe('get request headers', function(){ 6 | var basicOperation, 7 | complexOperation; 8 | 9 | beforeEach(function(){ 10 | basicOperation = { 11 | apiObject: { 12 | apiDeclaration: { 13 | basePath: 'http://example.com/api' 14 | }, 15 | path: '/do/it' 16 | }, 17 | parameters: [ 18 | { 19 | paramType: 'query', 20 | type: 'string', 21 | name: 'queryParam' 22 | } 23 | ] 24 | }; 25 | 26 | complexOperation = { 27 | apiObject: { 28 | apiDeclaration: { 29 | basePath: 'http://example.com/api' 30 | }, 31 | path: '/do/{what}.{format}' 32 | }, 33 | consumes: [ 34 | 'application/json', 35 | 'multipart/form-data', 36 | 'application/x-www-form-urlencoded', 37 | 'text/plain' 38 | ], 39 | produces: ['application/json'], 40 | parameters: [ 41 | { 42 | paramType: 'path', 43 | type: 'string', 44 | name: 'pathParam' 45 | }, 46 | { 47 | paramType: 'form', 48 | type: 'string', 49 | name: 'formParam' 50 | }, 51 | { 52 | paramType: 'body', 53 | type: 'string', 54 | name: 'theBody' 55 | }, 56 | { 57 | paramType: 'query', 58 | type: 'string', 59 | name: 'queryParam' 60 | }, 61 | { 62 | paramType: 'form', 63 | type: 'File', 64 | name: 'theFile' 65 | } 66 | ] 67 | }; 68 | }); 69 | 70 | it('sets the default accept header to json', function(){ 71 | var headers = getRequestHeaders( 72 | basicOperation, 73 | {} 74 | ); 75 | 76 | expect(headers).toEqual({'Accept': 'application/json'}); 77 | }); 78 | 79 | it('allows for the explicit specification of the accept header', function(){ 80 | var headers = getRequestHeaders( 81 | basicOperation, 82 | {}, 83 | { accept: 'text/plain' } 84 | ); 85 | 86 | expect(headers).toEqual({'Accept': 'text/plain'}); 87 | }); 88 | 89 | it('doesn\'t set a content type header if it\'s not sending anything in the body', function(){ 90 | var headers = getRequestHeaders( 91 | basicOperation, 92 | {} 93 | ); 94 | 95 | expect(headers['Content-Type']).not.toBeDefined(); 96 | }); 97 | 98 | it('sets the content type to application/json if there is a body param present', function(){ 99 | var headers = getRequestHeaders( 100 | complexOperation, 101 | { theBody: {} } 102 | ); 103 | 104 | expect(headers['Content-Type']).toBe('application/json'); 105 | }); 106 | 107 | it('allows for the explicit specification of content type', function(){ 108 | var headers = getRequestHeaders( 109 | complexOperation, 110 | { theBody: 'plain text' }, 111 | { contentType: 'text/plain' } 112 | ); 113 | 114 | expect(headers['Content-Type']).toBe('text/plain'); 115 | }); 116 | 117 | it('sets the content type to multipart/form-data if there is a file param present', function(){ 118 | var headers = getRequestHeaders( 119 | complexOperation, 120 | { 121 | formParam: 'plain text', 122 | theFile: 'plain text' 123 | } 124 | ); 125 | 126 | expect(headers['Content-Type']).toBe('multipart/form-data'); 127 | }); 128 | 129 | it('sets the content type to application/x-www-form-urlencoded if there is a form param present', function(){ 130 | var headers = getRequestHeaders( 131 | complexOperation, 132 | { formParam: 'plain text' } 133 | ); 134 | 135 | expect(headers['Content-Type']).toBe('application/x-www-form-urlencoded'); 136 | }); 137 | 138 | it('raises an exception if an unsupported accept type is passed in', function(){ 139 | expect(function(){ 140 | getRequestHeaders( 141 | complexOperation, 142 | {}, 143 | { accept: 'popcorn' } 144 | ); 145 | }).toThrow(); 146 | }); 147 | 148 | it('raises an exception if an unsupported content type is used', function(){ 149 | expect(function(){ 150 | getRequestHeaders( 151 | complexOperation, 152 | {}, 153 | { contentType: 'popcorn' } 154 | ); 155 | }).toThrow(); 156 | }); 157 | }); -------------------------------------------------------------------------------- /src/createClient.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var createOperationHandler = require('./createOperationHandler'); 4 | 5 | function createClient(schema, requestHandler){ 6 | var api = {}, 7 | apiAuthData, 8 | authMethodName = 'auth'; 9 | 10 | schema = processSchema(schema); 11 | 12 | // If the 'auth' key is used for any resource or operation, we'll use 13 | // 'authorization' instead for the auth methods 14 | var authIsInUse = schema.apis.some(function(resourceObject){ 15 | return resourceObject.apiDeclaration.apis.some(function(apiObject){ 16 | var resourceApiName = getApiName(apiObject.apiDeclaration.resourcePath || apiObject.path); 17 | if(resourceApiName === 'auth') return true; 18 | return apiObject.operations.some(function(operation){ 19 | return operation.nickname === 'auth'; 20 | }); 21 | }); 22 | }); 23 | 24 | if(authIsInUse) authMethodName = 'authorization'; 25 | 26 | api[authMethodName] = function(){ 27 | if(arguments.length === 0) return apiAuthData; 28 | apiAuthData = processApiAuthArgs(arguments); 29 | }; 30 | 31 | schema.apis.forEach(function(resourceObject){ 32 | var resourceName, 33 | resourceApi, 34 | resourceAuthData; 35 | 36 | if(resourceObject.apiDeclaration.resourcePath){ 37 | resourceName = getApiName(resourceObject.apiDeclaration.resourcePath); 38 | resourceApi = api[resourceName] = {}; 39 | resourceApi[authMethodName] = function(){ 40 | if(arguments.length === 0) return resourceAuthData; 41 | resourceAuthData = processApiAuthArgs(arguments); 42 | }; 43 | } 44 | 45 | resourceObject.apiDeclaration.apis.forEach(function(apiObject){ 46 | var apiObjectName = resourceName, 47 | apiObjectApi = resourceApi, 48 | apiObjectAuthData; 49 | 50 | if(!apiObjectName){ 51 | apiObjectName = getApiName(apiObject.path); 52 | apiObjectApi = api[apiObjectName] = {}; 53 | apiObjectApi[authMethodName] = function(){ 54 | if(arguments.length === 0) return apiObjectAuthData; 55 | 56 | apiObjectAuthData = processApiAuthArgs(arguments); 57 | }; 58 | } 59 | 60 | apiObject.operations.forEach(function(operation){ 61 | var operationHandlerName = operation.nickname, 62 | operationAuthData, 63 | operationHandler; 64 | 65 | function getAuthData(){ 66 | return operationAuthData || apiObjectAuthData || resourceAuthData || apiAuthData; 67 | } 68 | 69 | operationHandler = createOperationHandler(operation, getAuthData, requestHandler); 70 | 71 | operationHandler[authMethodName] = function(){ 72 | if(arguments.length === 0) return operationAuthData; 73 | 74 | operationAuthData = processApiAuthArgs(arguments); 75 | }; 76 | 77 | apiObjectApi[operationHandlerName] = operationHandler; 78 | }); 79 | }); 80 | }); 81 | 82 | return api; 83 | } 84 | module.exports = createClient; 85 | 86 | function processApiAuthArgs(args){ 87 | // for basic auth, allow calls with two args (username, password) 88 | if(typeof args[0] === 'string' && typeof args[1] === 'string') { 89 | return { 90 | username: args[0], 91 | password: args[1] 92 | }; 93 | } else { 94 | return args[0]; 95 | } 96 | } 97 | 98 | // Helpper method which assings back pointer to object parents and returns 99 | // the api objects within the given schema. 100 | function processSchema(schema){ 101 | schema.apis.forEach(function(resourceObject){ 102 | resourceObject.resourceListing = schema; 103 | 104 | resourceObject.apiDeclaration.apis.forEach(function(apiObject){ 105 | apiObject.resourceObject = resourceObject; 106 | apiObject.apiDeclaration = resourceObject.apiDeclaration; 107 | 108 | apiObject.operations.forEach(function(operation){ 109 | operation.apiObject = apiObject; 110 | 111 | operation.parameters.forEach(function(parameter){ 112 | parameter.operation = operation; 113 | }); 114 | }); 115 | }); 116 | }); 117 | 118 | return schema; 119 | } 120 | 121 | // Takes a path and returns a JavaScript-friendly variable name 122 | function getApiName(name){ 123 | // String non-word characters 124 | name = name.replace(/\W/g, '/'); 125 | 126 | // Turn paths which look/like/this to lookLikeThis 127 | name = name.replace(/(\w)\/(\w)/g, function(match, p1, p2){ 128 | return p1 + p2.toUpperCase(); 129 | }); 130 | 131 | name = name.replace(/\//g, ''); 132 | 133 | return name; 134 | } -------------------------------------------------------------------------------- /src/getRequestBodySpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var getRequestBody = require('./getRequestBody'); 4 | 5 | describe('get request body', function(){ 6 | var basicOperation, 7 | complexOperation; 8 | 9 | beforeEach(function(){ 10 | basicOperation = { 11 | apiObject: { 12 | apiDeclaration: { 13 | basePath: 'http://example.com/api' 14 | }, 15 | path: '/do/it' 16 | }, 17 | parameters: [ 18 | { 19 | paramType: 'query', 20 | type: 'string', 21 | name: 'queryParam' 22 | } 23 | ] 24 | }; 25 | 26 | complexOperation = { 27 | apiObject: { 28 | apiDeclaration: { 29 | basePath: 'http://example.com/api' 30 | }, 31 | path: '/do/{what}.{format}' 32 | }, 33 | consumes: [ 34 | 'application/json', 35 | 'multipart/form-data', 36 | 'application/x-www-form-urlencoded', 37 | 'text/plain' 38 | ], 39 | produces: ['application/json'], 40 | parameters: [ 41 | { 42 | paramType: 'path', 43 | type: 'string', 44 | name: 'pathParam' 45 | }, 46 | { 47 | paramType: 'form', 48 | type: 'string', 49 | name: 'formParam' 50 | }, 51 | { 52 | paramType: 'form', 53 | type: 'string', 54 | name: 'otherFormParam' 55 | }, 56 | { 57 | paramType: 'body', 58 | type: 'string', 59 | name: 'theBody' 60 | }, 61 | { 62 | paramType: 'query', 63 | type: 'string', 64 | name: 'queryParam' 65 | }, 66 | { 67 | paramType: 'form', 68 | type: 'File', 69 | name: 'theFile' 70 | } 71 | ] 72 | }; 73 | }); 74 | 75 | it('returns body as-is if there is no content type', function(){ 76 | var result = getRequestBody(basicOperation, {}, {}); 77 | expect(result).toBeUndefined(); 78 | }); 79 | 80 | it('returns body as-is if the content type isn\'t a form content-type', function(){ 81 | var result = getRequestBody(complexOperation, { 82 | theBody: 'hello world' 83 | }, {'Content-Type': 'text/plain'}); 84 | 85 | expect(result).toEqual('hello world'); 86 | }); 87 | 88 | it('returns strigified body if the content type is application/json', function(){ 89 | var result = getRequestBody(complexOperation, { 90 | theBody: { hello: 'world' } 91 | }, {'Content-Type': 'application/json'}); 92 | 93 | expect(result).toEqual('{"hello":"world"}'); 94 | }); 95 | 96 | it('returns url-encoded body if the content type is application/x-www-form-urlencoded', function(){ 97 | var result = getRequestBody(complexOperation, { 98 | formParam: 'hello', 99 | otherFormParam: 'world' 100 | }, {'Content-Type': 'application/x-www-form-urlencoded'}); 101 | 102 | expect(result).toEqual('formParam=hello&otherFormParam=world'); 103 | }); 104 | 105 | it('returns multipart body if the content type is multipart/form-data', function(){ 106 | var result = getRequestBody(complexOperation, { 107 | formParam: 'hello', 108 | otherFormParam: 'world', 109 | theFile: 'this is the file' 110 | }, {'Content-Type': 'multipart/form-data'}); 111 | 112 | var expected = '--SwaggerBoundary{{random}}' + 113 | '\nContent-Disposition: form-data; name="formParam"' + 114 | '\n' + 115 | '\nhello' + 116 | '\n--SwaggerBoundary{{random}}' + 117 | '\nContent-Disposition: form-data; name="otherFormParam"' + 118 | '\n' + 119 | '\nworld' + 120 | '\n--SwaggerBoundary{{random}}' + 121 | '\nContent-Disposition: form-data; name="theFile"' + 122 | '\n' + 123 | '\nthis is the file' + 124 | '\n--SwaggerBoundary{{random}}--\n'; 125 | 126 | expected = expected.replace(/{{random}}/g, result.match('SwaggerBoundary(.*)')[1]); 127 | expect(result).toEqual(expected); 128 | }); 129 | 130 | 131 | it('can handle individual content-types in multipart bodies', function(){ 132 | var result = getRequestBody(complexOperation, { 133 | formParam: 'hello', 134 | otherFormParam: 'world', 135 | theFile: { 136 | body: 'this is the file', 137 | name: 'myfile.txt', 138 | contentType: 'text/plain' 139 | 140 | } 141 | }, {'Content-Type': 'multipart/form-data'}); 142 | 143 | var expected = '--SwaggerBoundary{{random}}' + 144 | '\nContent-Disposition: form-data; name="formParam"' + 145 | '\n' + 146 | '\nhello' + 147 | '\n--SwaggerBoundary{{random}}' + 148 | '\nContent-Disposition: form-data; name="otherFormParam"' + 149 | '\n' + 150 | '\nworld' + 151 | '\n--SwaggerBoundary{{random}}' + 152 | '\nContent-Disposition: form-data; name="theFile"; filename="myfile.txt"' + 153 | '\nContent-Type: text/plain' + 154 | '\n' + 155 | '\nthis is the file' + 156 | '\n--SwaggerBoundary{{random}}--\n'; 157 | 158 | expected = expected.replace(/{{random}}/g, result.match('SwaggerBoundary(.*)')[1]); 159 | expect(result).toEqual(expected); 160 | }); 161 | }); -------------------------------------------------------------------------------- /src/applyAuthDataSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var applyAuthData = require('./applyAuthData'); 4 | 5 | describe('apply auth data', function(){ 6 | var request; 7 | 8 | beforeEach(function(){ 9 | request = { 10 | url: 'http://example.com?param=value', 11 | headers: { 12 | 'Content-Type': 'text/plain' 13 | }, 14 | body: 'Hello, world' 15 | }; 16 | }); 17 | 18 | it('doesn\'t change the request if there is no auth for the op', function(){ 19 | var noAuthOperation = { 20 | apiObject: { 21 | apiDeclaration: { 22 | authorizations: {} 23 | } 24 | } 25 | }; 26 | 27 | applyAuthData(noAuthOperation, undefined, request); 28 | 29 | expect(request.url).toBe('http://example.com?param=value'); 30 | expect(request.headers).toEqual({'Content-Type': 'text/plain'}); 31 | expect(request.body).toBe('Hello, world'); 32 | }); 33 | 34 | it('respects the op-level auth override if it exists', function(){ 35 | var operation = { 36 | authorizations: { 37 | apiKey: { 38 | type: 'apiKey', 39 | keyname: 'opToken', 40 | passAs: 'query' 41 | } 42 | }, 43 | apiObject: { 44 | apiDeclaration: { 45 | authorizations: { 46 | apiKey: { 47 | type: 'apiKey', 48 | keyname: 'apiToken', 49 | passAs: 'query' 50 | } 51 | } 52 | } 53 | } 54 | }; 55 | 56 | applyAuthData(operation, '123', request); 57 | expect(request.url).toBe('http://example.com?opToken=123¶m=value'); 58 | }); 59 | 60 | it('uses the api-level auth if no op-level auth exists', function(){ 61 | var operation = { 62 | apiObject: { 63 | apiDeclaration: { 64 | authorizations: { 65 | apiKey: { 66 | type: 'apiKey', 67 | keyname: 'apiToken', 68 | passAs: 'query' 69 | } 70 | } 71 | } 72 | } 73 | }; 74 | 75 | applyAuthData(operation, '123', request); 76 | expect(request.url).toBe('http://example.com?apiToken=123¶m=value'); 77 | }); 78 | 79 | it('throws a missing auth error if required auth params are not present', function(){ 80 | var operation = { 81 | apiObject: { 82 | apiDeclaration: { 83 | authorizations: { 84 | apiKey: { 85 | type: 'apiKey', 86 | keyname: 'apiToken', 87 | passAs: 'query' 88 | } 89 | } 90 | } 91 | } 92 | }; 93 | expect(function(){ 94 | applyAuthData(operation, undefined, request); 95 | }).toThrow(); 96 | }); 97 | 98 | it('does not throw a missing auth error if only one of many auth methods present', function(){ 99 | var operation = { 100 | apiObject: { 101 | apiDeclaration: { 102 | authorizations: { 103 | apiKey: { 104 | type: 'apiKey', 105 | keyname: 'apiToken', 106 | passAs: 'query' 107 | }, 108 | basicAuth: { 109 | type: 'basicAuth' 110 | } 111 | } 112 | } 113 | } 114 | }; 115 | 116 | expect(function(){ 117 | applyAuthData(operation, {basicAuth: {username: 'Bob', password: 'secret' }}, request); 118 | }).not.toThrow(); 119 | }); 120 | 121 | it('can apply apikeys to headers', function(){ 122 | var operation = { 123 | apiObject: { 124 | apiDeclaration: { 125 | authorizations: { 126 | apiKey: { 127 | type: 'apiKey', 128 | keyname: 'apiToken', 129 | passAs: 'header' 130 | } 131 | } 132 | } 133 | } 134 | }; 135 | 136 | applyAuthData(operation, '123', request); 137 | expect(request.headers).toEqual({ 138 | 'Content-Type': 'text/plain', 139 | 'apiToken': '123' 140 | }); 141 | }); 142 | 143 | it('can apply basic auth to urls', function(){ 144 | var operation = { 145 | apiObject: { 146 | apiDeclaration: { 147 | authorizations: { 148 | apiKey: { 149 | type: 'basicAuth', 150 | } 151 | } 152 | } 153 | } 154 | }; 155 | 156 | applyAuthData(operation, {username: 'Bob', password: 'secret' }, request); 157 | expect(request.url).toEqual('http://Bob:secret@example.com?param=value'); 158 | }); 159 | 160 | it('can apply multiple auths to a request', function(){ 161 | var operation = { 162 | apiObject: { 163 | apiDeclaration: { 164 | authorizations: { 165 | basicAuth: { 166 | type: 'basicAuth', 167 | }, 168 | apiKeyHeader: { 169 | type: 'apiKey', 170 | keyname: 'headerToken', 171 | passAs: 'header' 172 | }, 173 | apiKeyQuery: { 174 | type: 'apiKey', 175 | keyname: 'queryToken', 176 | passAs: 'header' 177 | } 178 | } 179 | } 180 | } 181 | }; 182 | 183 | applyAuthData(operation, { 184 | basicAuth: {username: 'Bob', password: 'secret' }, 185 | apiKeyQuery: 'query', 186 | apiKeyHeader: 'header' 187 | }, request); 188 | 189 | expect(request.url).toEqual('http://Bob:secret@example.com?param=value'); 190 | }); 191 | }); -------------------------------------------------------------------------------- /src/createOperationHandler.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | 3 | var getRequestHeaders = require('./getRequestHeaders'), 4 | getRequestUrl = require('./getRequestUrl'), 5 | getRequestBody = require('./getRequestBody'), 6 | applyAuthData = require('./applyAuthData'), 7 | errorTypes = require('./errorTypes'), 8 | swaggerValidate = require('swagger-validate'); 9 | 10 | var allErrorTypes = {}; 11 | Object.keys(swaggerValidate.errors).forEach(function(errorName){ 12 | allErrorTypes[errorName] = swaggerValidate.errors[errorName]; 13 | }); 14 | 15 | Object.keys(errorTypes).forEach(function(errorName){ 16 | allErrorTypes[errorName] = errorTypes[errorName]; 17 | }); 18 | 19 | function createOperationHandler(operation, getAuthData, requestHandler){ 20 | function Request(data, options){ 21 | this.method = operation.method; 22 | this.operation = operation; 23 | this.errorTypes = allErrorTypes; 24 | this.data = data; 25 | this.options = options; 26 | } 27 | 28 | var operationHandler = function(data, options){ 29 | var error, 30 | request; 31 | 32 | options = options || {}; 33 | 34 | if(data == null) data = {}; 35 | 36 | // if a function is passed in as options, assume it's a callback function 37 | // for convenience 38 | if(typeof options === 'function'){ 39 | options.callback = options; 40 | } 41 | 42 | try{ 43 | data = prune(data); 44 | data = singleParamConvenienceProcessor(operation, data); 45 | data = removeUnknownParams(operation, data); 46 | 47 | error = swaggerValidate.operation(data, operation, operation.apiObject.apiDeclaration.models); 48 | 49 | request = new Request(data, options); 50 | 51 | // If we know there is an error, don't attempt to craft the request params. 52 | // The request param generators assume valid data to work properly. 53 | if(!error){ 54 | request.url = getRequestUrl(operation, data); 55 | request.headers = getRequestHeaders(operation, data, options); 56 | request.body = getRequestBody(operation, data, request.headers); 57 | 58 | applyAuthData(operation, getAuthData(), request); 59 | } 60 | } catch(e){ 61 | error = e; 62 | } 63 | 64 | return requestHandler(error, request); 65 | }; 66 | 67 | // Useful for instanceof checks 68 | operationHandler.Request = Request; 69 | operationHandler.errorTypes = allErrorTypes; 70 | 71 | // Useful for reflection 72 | operationHandler.operation = operation; 73 | 74 | operationHandler.getUrl = function(data){ 75 | data = prune(data); 76 | data = singleParamConvenienceProcessor(operation, data); 77 | data = removeUnknownParams(operation, data); 78 | 79 | var error = swaggerValidate.operation(data, operation, operation.apiObject.apiDeclaration.models); 80 | if(error) throw error; 81 | 82 | return getRequestUrl(operation, data); 83 | }; 84 | 85 | 86 | // Can be used to preemptively validate without action 87 | operationHandler.validate = function(data){ 88 | return swaggerValidate.operation(data, operation, operation.apiObject.apiDeclaration.models); 89 | }; 90 | 91 | return operationHandler; 92 | } 93 | module.exports = createOperationHandler; 94 | 95 | function noop(){} 96 | createOperationHandler.logger = { 97 | debug: noop, 98 | info: noop, 99 | warn: noop, 100 | error: noop 101 | }; 102 | 103 | // Stringify and parse the data to clean up undefined, and non-scalar properties 104 | function prune(data){ 105 | return JSON.parse(JSON.stringify(data)); 106 | } 107 | 108 | // Enables data to be passed directly for single param operations. 109 | function singleParamConvenienceProcessor(operation, data){ 110 | // If there are more than one params, bail 111 | var requiredParams = operation.parameters.filter(function(param){ 112 | return param.required; 113 | }); 114 | 115 | // If there are more than one required params, or if there is no required param 116 | // and there are many optional params, bail 117 | if(requiredParams.length > 1) return data; 118 | 119 | if(requiredParams.length !== 1 && operation.parameters.length !== 1) return data; 120 | 121 | var param = requiredParams[0] || operation.parameters[0]; 122 | 123 | // If the param is already defined explicitly, bail 124 | if(typeof data === 'object' && data[param.name] !== undefined) return data; 125 | 126 | var models = operation.apiObject.apiDeclaration.models; 127 | 128 | // If the data passed is is not valid for the param data type, bail 129 | var error; 130 | 131 | try { 132 | error = swaggerValidate.dataType(data, param, models); 133 | } catch(e){ 134 | return data; 135 | } 136 | 137 | // If the data passed is a valid param data type, bail 138 | if(!error){ 139 | var wrapper = {}; 140 | wrapper[param.name] = data; 141 | return wrapper; 142 | } else { 143 | return data; 144 | } 145 | } 146 | 147 | 148 | function removeUnknownParams(operation, data){ 149 | if(!data || typeof data !== 'object') return data; 150 | 151 | var paramNames = {}; 152 | operation.parameters.forEach(function(param){ 153 | paramNames[param.name] = true; 154 | }); 155 | 156 | var unknownKeys = Object.keys(data).filter(function(key){ 157 | return !(key in paramNames); 158 | }); 159 | 160 | createOperationHandler.logger.warn('Unknown parameters removed from request:', 161 | unknownKeys.join(', ')); 162 | 163 | unknownKeys.forEach(function(key){ 164 | delete data[key]; 165 | }); 166 | 167 | return data; 168 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Swagger Client Generator 2 | [![Build Status](https://travis-ci.org/signalfx/swagger-client-generator.svg?branch=master)](https://travis-ci.org/signalfx/swagger-client-generator) 3 | 4 | Generates a client given a requests handler and a [Swagger API Specification](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md) schema (which can be generated with [fetch-swagger-schema](https://github.com/signalfx/fetch-swagger-schema): `npm install -g fetch-swagger-schema; fetch-swagger-schema `). 5 | 6 | This is intended to be a helper for creating swagger clients by passing in a request handler for the platform being used (e.g., you XHR request handler in browsers, and request in node). It provides [robust client-side validation](https://github.com/signalfx/swagger-validate) according to the API endpoints and converts given data into appropriate url, header, and body information to be consumed by the request handler. 7 | 8 | ## Example 9 | ```js 10 | function requestHandler(error, request){ 11 | if(error) return console.error(error.toString()); 12 | 13 | var xhr = new XMLHttpRequest(); 14 | xhr.open(request.method, request.url) 15 | 16 | if(request.headers){ 17 | Object.keys(request.headers).forEach(function(header){ 18 | xhr.setRequestHeader(header, request.headers[header]); 19 | }); 20 | } 21 | 22 | xhr.onloadend = function(){ 23 | request.options.callback(this.response); 24 | }; 25 | 26 | xhr.send(request.body); 27 | } 28 | 29 | // assumes that 'schema' already exists in scope and is the schema object for 30 | // http://petstore.swagger.io/api/api-docs 31 | var api = swaggerClientGenerator(schema, requestHandler); 32 | 33 | // for apiKey authorization use: api.auth('my-token') 34 | // for basicAuth use: api.auth('username', 'password') 35 | // authorization may be set for any level (api, api.resource, or api.operation) 36 | 37 | api.pet.getPetById(2, function(response){ 38 | console.log(response); 39 | }); 40 | ``` 41 | ## Creating a schema object 42 | A schema is just a compilation of the Swagger Resource Listing object with each Resource object embedded directly within it. You can fetch one and save it automatically by using [fetch-swagger-schema](https://github.com/signalfx/fetch-swagger-schema): 43 | ```shell 44 | # install the fetch-swagger-schema tool 45 | npm install -g fetch-swagger-schema 46 | fetch-swagger-schema 47 | # the generated schema json file will be at 48 | ``` 49 | 50 | ## API 51 | #### `api = swaggerClientGenerator(schemaObject, requestHandler)` 52 | * *schemaObject* - A json object describing the schema (generally generated by [fetch-swagger-schema](https://github.com/signalfx/fetch-swagger-schema)). 53 | * *requestHandler* - A function which accepts two parameters `error` and `request`. The error object will be a [ValidationErrors](https://github.com/signalfx/swagger-validate#swaggervalidateerrorsvalidationerrors) object if an validation error occurs, otherwise it will be undefined. The request object is defined below. 54 | * *api* - An object which can be used as the api for the given schema. The first-level objects are the resources within the schema and the second-level functions are the operations which can be performed on those resources. 55 | 56 | #### `api.` 57 | A map of all the resources as defined in the schema to their operation handler (e.g. `api.pet`). 58 | 59 | #### `requestHandlerResponse = api..(data, options)` 60 | This is the actual operation handler invoked by the clients (e.g., `api.pet.getPetById`). 61 | 62 | The operation handler takes two parameters: 63 | * *data* - A map of the operation data parameters. If the operation only has one parameter, the value may be used directly (i.e. `api.pet.getPetById(1, callback)` is the same as `api.pet.getPetById({petId: 1}, callback)`). 64 | * *options* - A map of the options to use when calling the operation. This differs based on operation but it may include parameters such as `contentType` or `accept` and it commonly includes options to pass to the request handler (such as a callback function when the request completes). If options is a function, then the `callback` property of options will be a reference to the function for convenience (e.i. `api.pet.getPetById(1, callback)` is the same as `api.pet.getPetById(1, { callback: callback })`. 65 | 66 | The operation handler processes the data and options, passes the processed `request` data to the `requestHandler` and returns the result of the requestHandler back to the caller. The operation handler does not process the results of the requestHandler before returning it to the caller. 67 | 68 | #### `url = api...getUrl(data)` 69 | Intended for advanced use-cases. Returns the URL which would be called if the operation were to be called using this operation handler. The acceptable HTTP method to call this url can be found at `api...operation.method`. 70 | 71 | #### `requestHandler(error, request)` 72 | The request handler is the second parameter for the swaggerClientGenerator and will be called whenever an operation is invoked. If there are any validation errors, the errors parameter be a [ValidationErrors](https://github.com/signalfx/swagger-validate#swaggervalidateerrorsvalidationerrors) object describing the validation errors in detail. 73 | 74 | The request object will have the following properties which should be used to issue the actual HTTP request: 75 | * *method* - The HTTP method string for the operation 76 | * *url* - The url string to call for the operation 77 | * *headers* - A map of the request headers 78 | * *body* - The body of the request 79 | * *options* - Options passed in as the second parameter to the operation handler, commonly used for callbacks 80 | * *operation* - The metadata of the operation being invoked 81 | * *data* - The raw data used to generate the url, headers, and body 82 | * *errorTypes* - A map of [all possible error types](https://github.com/signalfx/swagger-validate#swaggervalidateerrorsvalidationerrors) which are used in the error parameter (useful for instanceof checks) 83 | 84 | ##### `operation` objects 85 | This object, which is found in either `request.operation` or `api...operation` contains references to the entire schema object: 86 | * [Operation](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#523-operation-object) - `operation` 87 | * [API Object](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#522-api-object) - `operation.apiObject` 88 | * [API Declaration](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#52-api-declaration) - `operation.apiObject.apiDeclaration` 89 | * [Resource Object](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#512-resource-object) - `operation.apiObject.resourceObject` 90 | * [Resource Listing](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md#51-resource-listing)- `operation.apiObject.resourceObject.resourceListing` 91 | 92 | ## Developing 93 | After installing [nodejs](http://nodejs.org) execute the following: 94 | 95 | ```shell 96 | git clone https://github.com/signalfx/swagger-ajax-client.git 97 | cd swagger-ajax-client 98 | npm install 99 | npm run dev 100 | ``` 101 | The build engine will test and build everything, start a server hosting the `example` folder on [localhost:3000](http://localhost:3000), and watch for any changes and rebuild when nescessary. 102 | 103 | To generate minified files in `dist`: 104 | ```shell 105 | npm run dist 106 | ``` 107 | -------------------------------------------------------------------------------- /src/createOperationHandlerSpec.js: -------------------------------------------------------------------------------- 1 | 'use strict'; 2 | /* global jasmine */ 3 | var createOperationHandler = require('./createOperationHandler'); 4 | 5 | describe('create operation handler', function(){ 6 | var basicOperation, 7 | complexOperation, 8 | requestHandler, 9 | promise, 10 | authData, 11 | getAuthData; 12 | 13 | beforeEach(function(){ 14 | authData = {}; 15 | getAuthData = jasmine.createSpy('getAuthData').and.returnValue(authData); 16 | 17 | promise = { 18 | then: jasmine.createSpy('promise.then') 19 | }; 20 | 21 | requestHandler = jasmine.createSpy('requestHandler').and.returnValue(promise); 22 | basicOperation = { 23 | method: 'GET', 24 | apiObject: { 25 | apiDeclaration: { 26 | basePath: 'http://example.com/api' 27 | }, 28 | path: '/do/it' 29 | }, 30 | parameters: [ 31 | { 32 | paramType: 'query', 33 | type: 'number', 34 | name: 'queryParam' 35 | } 36 | ] 37 | }; 38 | 39 | complexOperation = { 40 | method: 'PUT', 41 | apiObject: { 42 | apiDeclaration: { 43 | basePath: 'http://example.com/api' 44 | }, 45 | path: '/do/{what}.{format}' 46 | }, 47 | consumes: [ 48 | 'application/json', 49 | 'multipart/form-data', 50 | 'application/x-www-form-urlencoded', 51 | 'text/plain' 52 | ], 53 | produces: ['application/json'], 54 | parameters: [ 55 | { 56 | paramType: 'path', 57 | type: 'string', 58 | name: 'pathParam' 59 | }, 60 | { 61 | paramType: 'form', 62 | type: 'string', 63 | name: 'formParam' 64 | }, 65 | { 66 | paramType: 'form', 67 | type: 'string', 68 | name: 'otherFormParam' 69 | }, 70 | { 71 | paramType: 'body', 72 | type: 'string', 73 | name: 'theBody' 74 | }, 75 | { 76 | paramType: 'query', 77 | type: 'string', 78 | name: 'queryParam' 79 | }, 80 | { 81 | paramType: 'query', 82 | type: 'number', 83 | name: 'queryNumberParam' 84 | }, 85 | { 86 | paramType: 'form', 87 | type: 'File', 88 | name: 'theFile' 89 | } 90 | ] 91 | }; 92 | }); 93 | 94 | it('returns the result of the request handler regardless of errors', function(){ 95 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 96 | var result = operationHandler(); 97 | expect(requestHandler).toHaveBeenCalled(); 98 | expect(result).toBe(promise); 99 | }); 100 | 101 | it('converts passed value to it\'s corresponding param for one-param operations', function(){ 102 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 103 | 104 | operationHandler(1); 105 | 106 | expect(requestHandler).toHaveBeenCalledWith(undefined, jasmine.objectContaining( 107 | {data: {queryParam: 1}} 108 | )); 109 | }); 110 | 111 | 112 | it('converts passed value to it\'s corresponding param for one-param operations', function(){ 113 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 114 | 115 | operationHandler(0); 116 | //console.log(requestHandler.calls.mostRecent()); 117 | expect(requestHandler).toHaveBeenCalledWith(undefined, jasmine.objectContaining( 118 | {data: {queryParam: 0}} 119 | )); 120 | }); 121 | 122 | it('doesn\'t convert passed values if they are an object and a key in the object' + 123 | 'corresponds to a param name', function(){ 124 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 125 | 126 | operationHandler({ queryParam: 1 }); 127 | 128 | expect(requestHandler).toHaveBeenCalledWith(undefined, jasmine.objectContaining( 129 | {data: {queryParam: 1}} 130 | )); 131 | }); 132 | 133 | it('prunes unknown params immediately', function(){ 134 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 135 | 136 | operationHandler({ 137 | queryParam: 1, 138 | unkownParamName: 'turn down for what' 139 | }); 140 | 141 | expect(requestHandler).not.toHaveBeenCalledWith(undefined, jasmine.objectContaining( 142 | {data: { unkownParamName: 'turn down for what' }} 143 | )); 144 | 145 | expect(requestHandler).toHaveBeenCalledWith(undefined, jasmine.objectContaining( 146 | {data: {queryParam: 1}} 147 | )); 148 | }); 149 | 150 | it('provides error types for the operation handler as a property', function(){ 151 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 152 | 153 | operationHandler({ queryParam: '1' }); 154 | expect(requestHandler).toHaveBeenCalledWith( 155 | jasmine.any(operationHandler.errorTypes.ValidationErrors), 156 | jasmine.any(operationHandler.Request) 157 | ); 158 | }); 159 | 160 | it('provides an operation data validator as a property', function(){ 161 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 162 | expect(operationHandler.validate).toBeDefined(); 163 | }); 164 | 165 | it('provides the operation as a property', function(){ 166 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 167 | expect(operationHandler.operation).toBe(basicOperation); 168 | }); 169 | 170 | it('provides the possible error types for the operation as a property', function(){ 171 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 172 | expect(operationHandler.errorTypes).toBeDefined(); 173 | }); 174 | 175 | it('provides the Request type for the operation as a property', function(){ 176 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 177 | expect(operationHandler.Request).toBeDefined(); 178 | 179 | var otherOperationHandler = createOperationHandler(complexOperation, getAuthData, requestHandler); 180 | expect(otherOperationHandler.Request).toBeDefined(); 181 | 182 | expect(otherOperationHandler.Request).not.toBe(operationHandler.Request); 183 | }); 184 | 185 | it('provides a method to just get the URL for an operation', function(){ 186 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 187 | expect(operationHandler.getUrl).toBeDefined(); 188 | var url = operationHandler.getUrl({queryParam: 1}); 189 | expect(url).toBe('http://example.com/api/do/it?queryParam=1'); 190 | 191 | // Invalid data causes an exception to be thrown 192 | expect(function(){ 193 | operationHandler.getUrl({queryParam: '1'}); 194 | }).toThrow(); 195 | }); 196 | 197 | it('calls getAuthData to get the authorization data settings', function(){ 198 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 199 | operationHandler({ queryParam: 1 }); 200 | expect(getAuthData).toHaveBeenCalled(); 201 | }); 202 | 203 | it('returns missing auth exceptions when auth params are missing', function(){ 204 | basicOperation.apiObject.apiDeclaration.authorizations = { 205 | apiKey: { 206 | type: 'basicAuth', 207 | } 208 | }; 209 | 210 | var getAuthData = function(){ return undefined; }; 211 | 212 | var operationHandler = createOperationHandler(basicOperation, getAuthData, requestHandler); 213 | operationHandler({ queryParam: 1 }); 214 | expect(requestHandler).toHaveBeenCalledWith( 215 | jasmine.any(operationHandler.errorTypes.MissingAuthorizationError), 216 | jasmine.any(operationHandler.Request) 217 | ); 218 | }); 219 | }); -------------------------------------------------------------------------------- /example/schema.json: -------------------------------------------------------------------------------- 1 | {"apiVersion":"1.0.0","swaggerVersion":"1.2","apis":[{"path":"/pet","description":"Operations about pets","apiDeclaration":{"apiVersion":"1.0.0","swaggerVersion":"1.2","basePath":"http://petstore.swagger.io/api","resourcePath":"/pet","produces":["application/json","application/xml","text/plain","text/html"],"apis":[{"path":"/pet/{petId}","operations":[{"method":"GET","summary":"Find pet by ID","notes":"Returns a pet based on ID","type":"Pet","nickname":"getPetById","parameters":[{"name":"petId","description":"ID of pet that needs to be fetched","required":true,"type":"integer","format":"int64","paramType":"path","allowMultiple":false,"minimum":"1.0","maximum":"100000.0"}],"responseMessages":[{"code":400,"message":"Invalid ID supplied"},{"code":404,"message":"Pet not found"}]},{"method":"DELETE","summary":"Deletes a pet","notes":"","type":"void","nickname":"deletePet","authorizations":{"oauth2":[{"scope":"write:pets","description":"modify pets in your account"}]},"parameters":[{"name":"petId","description":"Pet id to delete","required":true,"type":"string","paramType":"path","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid pet value"}]},{"method":"PATCH","summary":"partial updates to a pet","notes":"","type":"array","items":{"$ref":"Pet"},"nickname":"partialUpdate","produces":["application/json","application/xml"],"consumes":["application/json","application/xml"],"authorizations":{"oauth2":[{"scope":"write:pets","description":"modify pets in your account"}]},"parameters":[{"name":"petId","description":"ID of pet that needs to be fetched","required":true,"type":"string","paramType":"path","allowMultiple":false},{"name":"body","description":"Pet object that needs to be added to the store","required":true,"type":"Pet","paramType":"body","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid tag value"}]},{"method":"POST","summary":"Updates a pet in the store with form data","notes":"","type":"void","nickname":"updatePetWithForm","consumes":["application/x-www-form-urlencoded"],"authorizations":{"oauth2":[{"scope":"write:pets","description":"modify pets in your account"}]},"parameters":[{"name":"petId","description":"ID of pet that needs to be updated","required":true,"type":"string","paramType":"path","allowMultiple":false},{"name":"name","description":"Updated name of the pet","required":false,"type":"string","paramType":"form","allowMultiple":false},{"name":"status","description":"Updated status of the pet","required":false,"type":"string","paramType":"form","allowMultiple":false}],"responseMessages":[{"code":405,"message":"Invalid input"}]}]},{"path":"/pet/uploadImage","operations":[{"method":"POST","summary":"uploads an image","notes":"","type":"void","nickname":"uploadFile","consumes":["multipart/form-data"],"authorizations":{"oauth2":[{"scope":"write:pets","description":"modify pets in your account"},{"scope":"read:pets","description":"read your pets"}]},"parameters":[{"name":"additionalMetadata","description":"Additional data to pass to server","required":false,"type":"string","paramType":"form","allowMultiple":false},{"name":"file","description":"file to upload","required":false,"type":"File","paramType":"body","allowMultiple":false}]}]},{"path":"/pet","operations":[{"method":"POST","summary":"Add a new pet to the store","notes":"","type":"void","nickname":"addPet","consumes":["application/json","application/xml"],"authorizations":{"oauth2":[{"scope":"write:pets","description":"modify pets in your account"}]},"parameters":[{"name":"body","description":"Pet object that needs to be added to the store","required":true,"type":"Pet","paramType":"body","allowMultiple":false}],"responseMessages":[{"code":405,"message":"Invalid input"}]},{"method":"PUT","summary":"Update an existing pet","notes":"","type":"void","nickname":"updatePet","parameters":[{"name":"body","description":"Pet object that needs to be updated in the store","required":true,"type":"Pet","paramType":"body","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid ID supplied"},{"code":404,"message":"Pet not found"},{"code":405,"message":"Validation exception"}]}]},{"path":"/pet/findByStatus","operations":[{"method":"GET","summary":"Finds Pets by status","notes":"Multiple status values can be provided with comma seperated strings","type":"array","items":{"$ref":"Pet"},"nickname":"findPetsByStatus","parameters":[{"name":"status","description":"Status values that need to be considered for filter","defaultValue":"available","required":true,"type":"string","paramType":"query","allowMultiple":true,"enum":["available","pending","sold"]}],"responseMessages":[{"code":400,"message":"Invalid status value"}]}]},{"path":"/pet/findByTags","operations":[{"method":"GET","summary":"Finds Pets by tags","notes":"Muliple tags can be provided with comma seperated strings. Use tag1, tag2, tag3 for testing.","type":"array","items":{"$ref":"Pet"},"nickname":"findPetsByTags","parameters":[{"name":"tags","description":"Tags to filter by","required":true,"type":"string","paramType":"query","allowMultiple":true}],"responseMessages":[{"code":400,"message":"Invalid tag value"}],"deprecated":"true"}]}],"models":{"Tag":{"id":"Tag","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}}},"Pet":{"id":"Pet","required":["id","name"],"properties":{"id":{"type":"integer","format":"int64","description":"unique identifier for the pet","minimum":"0.0","maximum":"100.0"},"category":{"$ref":"Category"},"name":{"type":"string"},"photoUrls":{"type":"array","items":{"type":"string"}},"tags":{"type":"array","items":{"$ref":"Tag"}},"status":{"type":"string","description":"pet status in the store","enum":["available","pending","sold"]}}},"Category":{"id":"Category","properties":{"id":{"type":"integer","format":"int64"},"name":{"type":"string"}}}}}},{"path":"/user","description":"Operations about user","apiDeclaration":{"apiVersion":"1.0.0","swaggerVersion":"1.2","basePath":"http://petstore.swagger.io/api","resourcePath":"/user","produces":["application/json"],"apis":[{"path":"/user/createWithList","operations":[{"method":"POST","summary":"Creates list of users with given list input","notes":"","type":"void","nickname":"createUsersWithListInput","authorizations":{"oauth2":[{"scope":"test:anything","description":"anything"}]},"parameters":[{"name":"body","description":"List of user object","required":true,"type":"array","items":{"$ref":"User"},"paramType":"body","allowMultiple":false}]}]},{"path":"/user/{username}","operations":[{"method":"PUT","summary":"Updated user","notes":"This can only be done by the logged in user.","type":"void","nickname":"updateUser","authorizations":{"oauth2":[{"scope":"test:anything","description":"anything"}]},"parameters":[{"name":"username","description":"name that need to be deleted","required":true,"type":"string","paramType":"path","allowMultiple":false},{"name":"body","description":"Updated user object","required":true,"type":"User","paramType":"body","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid username supplied"},{"code":404,"message":"User not found"}]},{"method":"DELETE","summary":"Delete user","notes":"This can only be done by the logged in user.","type":"void","nickname":"deleteUser","authorizations":{"oauth2":[{"scope":"test:anything","description":"anything"}]},"parameters":[{"name":"username","description":"The name that needs to be deleted","required":true,"type":"string","paramType":"path","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid username supplied"},{"code":404,"message":"User not found"}]},{"method":"GET","summary":"Get user by user name","notes":"","type":"User","nickname":"getUserByName","parameters":[{"name":"username","description":"The name that needs to be fetched. Use user1 for testing.","required":true,"type":"string","paramType":"path","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid username supplied"},{"code":404,"message":"User not found"}]}]},{"path":"/user","operations":[{"method":"POST","summary":"Create user","notes":"This can only be done by the logged in user.","type":"void","nickname":"createUser","authorizations":{"oauth2":[{"scope":"test:anything","description":"anything"}]},"parameters":[{"name":"body","description":"Created user object","required":true,"type":"User","paramType":"body","allowMultiple":false}]}]},{"path":"/user/createWithArray","operations":[{"method":"POST","summary":"Creates list of users with given input array","notes":"","type":"void","nickname":"createUsersWithArrayInput","authorizations":{"oauth2":[{"scope":"test:anything","description":"anything"}]},"parameters":[{"name":"body","description":"List of user object","required":true,"type":"array","items":{"$ref":"User"},"paramType":"body","allowMultiple":false}]}]},{"path":"/user/login","operations":[{"method":"GET","summary":"Logs user into the system","notes":"","type":"string","nickname":"loginUser","parameters":[{"name":"username","description":"The user name for login","required":true,"type":"string","paramType":"query","allowMultiple":false},{"name":"password","description":"The password for login in clear text","required":true,"type":"string","paramType":"query","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid username and password combination"}]}]},{"path":"/user/logout","operations":[{"method":"GET","summary":"Logs out current logged in user session","notes":"","type":"void","nickname":"logoutUser","parameters":[]}]}],"models":{"User":{"id":"User","properties":{"id":{"type":"integer","format":"int64"},"firstName":{"type":"string"},"username":{"type":"string"},"lastName":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"phone":{"type":"string"},"userStatus":{"type":"integer","format":"int32","description":"User Status","enum":["1-registered","2-active","3-closed"]}}}}}},{"path":"/store","description":"Operations about store","apiDeclaration":{"apiVersion":"1.0.0","swaggerVersion":"1.2","basePath":"http://petstore.swagger.io/api","resourcePath":"/store","produces":["application/json"],"apis":[{"path":"/store/order/{orderId}","operations":[{"method":"GET","summary":"Find purchase order by ID","notes":"For valid response try integer IDs with value <= 5. Anything above 5 or nonintegers will generate API errors","type":"Order","nickname":"getOrderById","parameters":[{"name":"orderId","description":"ID of pet that needs to be fetched","required":true,"type":"string","paramType":"path","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid ID supplied"},{"code":404,"message":"Order not found"}]},{"method":"DELETE","summary":"Delete purchase order by ID","notes":"For valid response try integer IDs with value < 1000. Anything above 1000 or nonintegers will generate API errors","type":"void","nickname":"deleteOrder","authorizations":{"oauth2":[{"scope":"write:pets","description":"write to your pets"}]},"parameters":[{"name":"orderId","description":"ID of the order that needs to be deleted","required":true,"type":"string","paramType":"path","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid ID supplied"},{"code":404,"message":"Order not found"}]}]},{"path":"/store/order","operations":[{"method":"POST","summary":"Place an order for a pet","notes":"","type":"void","nickname":"placeOrder","authorizations":{"oauth2":[{"scope":"write:pets","description":"write to your pets"}]},"parameters":[{"name":"body","description":"order placed for purchasing the pet","required":true,"type":"Order","paramType":"body","allowMultiple":false}],"responseMessages":[{"code":400,"message":"Invalid order"}]}]}],"models":{"Order":{"id":"Order","properties":{"id":{"type":"integer","format":"int64"},"petId":{"type":"integer","format":"int64"},"quantity":{"type":"integer","format":"int32"},"status":{"type":"string","description":"Order Status","enum":["placed"," approved"," delivered"]},"shipDate":{"type":"string","format":"date-time"}}}}}}],"authorizations":{"oauth2":{"type":"oauth2","scopes":[{"scope":"write:pets","description":"Modify pets in your account"},{"scope":"read:pets","description":"Read your pets"}],"grantTypes":{"implicit":{"loginEndpoint":{"url":"http://petstore.swagger.io/api/oauth/dialog"},"tokenName":"access_token"}}}},"info":{"title":"Swagger Sample App","description":"This is a sample server Petstore server. You can find out more about Swagger \n at http://swagger.io or on irc.freenode.net, #swagger. For this sample,\n you can use the api key \"special-key\" to test the authorization filters","termsOfServiceUrl":"http://helloreverb.com/terms/","contact":"apiteam@wordnik.com","license":"Apache 2.0","licenseUrl":"http://www.apache.org/licenses/LICENSE-2.0.html"}} -------------------------------------------------------------------------------- /dist/swagger-client-generator.min.js: -------------------------------------------------------------------------------- 1 | !function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var r;"undefined"!=typeof window?r=window:"undefined"!=typeof global?r=global:"undefined"!=typeof self&&(r=self),r.swaggerClientGenerator=e()}}(function(){return function e(r,t,n){function o(i,s){if(!t[i]){if(!r[i]){var p="function"==typeof require&&require;if(!s&&p)return p(i,!0);if(a)return a(i,!0);throw new Error("Cannot find module '"+i+"'")}var u=t[i]={exports:{}};r[i][0].call(u.exports,function(e){var t=r[i][1][e];return o(t?t:e)},u,u.exports,e,r,t,n)}return t[i].exports}for(var a="function"==typeof require&&require,i=0;iparseInt(r.maximum,10)?new u.NumberTooLargeError(e,r.maximum):void 0}function a(e){return"boolean"==typeof e||e instanceof Boolean?void 0:new u.NotABooleanError(e,typeof e)}function i(e){return null!=e?new u.NotVoidError(e,typeof e):void 0}function s(){}function p(e,r){return"string"==typeof e||e instanceof String?"enum"in r&&-1===r.enum.indexOf(e)?new u.StringNotInEnumError(e,r.enum):void 0:new u.NotAStringError(e,typeof e)}var u=e("./errorTypes");t.validateInteger=n,t.validateNumber=o,t.validateBoolean=a,t.validateVoid=i,t.validateFile=s,t.validateString=p},{"./errorTypes":1}],8:[function(e,r){"use strict";function t(e,r,t,n){if(!t)throw new o(r,e);if("header"===e.passAs)n.headers[e.keyname]=t;else if("query"===e.passAs){var a=n.url,i=e.keyname+"="+encodeURIComponent(t);-1===a.indexOf("?")?a+="?"+i:a=a.replace("?","?"+i+"&"),n.url=a}}function n(e,r,t,n,a){if(!t||!n)throw new o(r,e);var i=a.url;-1===i.indexOf("@")&&(i=i.replace("://","://"+t+":"+n+"@")),a.url=i}var o=e("./errorTypes").MissingAuthorizationError;r.exports=function(e,r,a){var i=e.authorizations;if(i||(i=e.apiObject.apiDeclaration.authorizations),i){var s=Object.keys(i).filter(function(e){return"oauth2"!==i[e].type});if(0!==s.length)if(1===s.length){var p=s[0],u=i[p];if(!r)throw new o(p,u);r[p]&&(r=r[p]),"apiKey"===u.type?t(u,p,r,a):"basicAuth"===u.type&&n(u,p,r.username,r.password,a)}else{var c=s.some(function(e){var o=i[e],s=r[e];return s?("apiKey"===o.type?t(o,e,s,a):"basicAuth"===o.type&&n(o,e,s.username,s.password,a),!0):!1});if(!c)throw new o(s.join(", "),i)}}}},{"./errorTypes":11}],9:[function(e,r){"use strict";function t(e,r){var t,s={},p="auth";e=o(e);var u=e.apis.some(function(e){return e.apiDeclaration.apis.some(function(e){var r=a(e.apiDeclaration.resourcePath||e.path);return"auth"===r?!0:e.operations.some(function(e){return"auth"===e.nickname})})});return u&&(p="authorization"),s[p]=function(){return 0===arguments.length?t:void(t=n(arguments))},e.apis.forEach(function(e){var o,u,c;e.apiDeclaration.resourcePath&&(o=a(e.apiDeclaration.resourcePath),u=s[o]={},u[p]=function(){return 0===arguments.length?c:void(c=n(arguments))}),e.apiDeclaration.apis.forEach(function(e){var f,m=o,l=u;m||(m=a(e.path),l=s[m]={},l[p]=function(){return 0===arguments.length?f:void(f=n(arguments))}),e.operations.forEach(function(e){function o(){return a||f||c||t}var a,s,u=e.nickname;s=i(e,o,r),s[p]=function(){return 0===arguments.length?a:void(a=n(arguments))},l[u]=s})})}),s}function n(e){return"string"==typeof e[0]&&"string"==typeof e[1]?{username:e[0],password:e[1]}:e[0]}function o(e){return e.apis.forEach(function(r){r.resourceListing=e,r.apiDeclaration.apis.forEach(function(e){e.resourceObject=r,e.apiDeclaration=r.apiDeclaration,e.operations.forEach(function(r){r.apiObject=e,r.parameters.forEach(function(e){e.operation=r})})})}),e}function a(e){return e=e.replace(/\W/g,"/"),e=e.replace(/(\w)\/(\w)/g,function(e,r,t){return r+t.toUpperCase()}),e=e.replace(/\//g,"")}var i=e("./createOperationHandler");r.exports=t},{"./createOperationHandler":10}],10:[function(e,r){"use strict";function t(e,r,t){function n(r,t){this.method=e.method,this.operation=e,this.errorTypes=l,this.data=r,this.options=t}var f=function(f,l){var d,y;l=l||{},null==f&&(f={}),"function"==typeof l&&(l.callback=l);try{f=o(f),f=a(e,f),f=i(e,f),d=m.operation(f,e,e.apiObject.apiDeclaration.models),y=new n(f,l),d||(y.url=p(e,f),y.headers=s(e,f,l),y.body=u(e,f,y.headers),c(e,r(),y))}catch(h){d=h}return t(d,y)};return f.Request=n,f.errorTypes=l,f.operation=e,f.getUrl=function(r){r=o(r),r=a(e,r),r=i(e,r);var t=m.operation(r,e,e.apiObject.apiDeclaration.models);if(t)throw t;return p(e,r)},f.validate=function(r){return m.operation(r,e,e.apiObject.apiDeclaration.models)},f}function n(){}function o(e){return JSON.parse(JSON.stringify(e))}function a(e,r){var t=e.parameters.filter(function(e){return e.required});if(t.length>1)return r;if(1!==t.length&&1!==e.parameters.length)return r;var n=t[0]||e.parameters[0];if("object"==typeof r&&void 0!==r[n.name])return r;var o,a=e.apiObject.apiDeclaration.models;try{o=m.dataType(r,n,a)}catch(i){return r}if(o)return r;var s={};return s[n.name]=r,s}function i(e,r){if(!r||"object"!=typeof r)return r;var n={};e.parameters.forEach(function(e){n[e.name]=!0});var o=Object.keys(r).filter(function(e){return!(e in n)});return t.logger.warn("Unknown parameters removed from request:",o.join(", ")),o.forEach(function(e){delete r[e]}),r}var s=e("./getRequestHeaders"),p=e("./getRequestUrl"),u=e("./getRequestBody"),c=e("./applyAuthData"),f=e("./errorTypes"),m=e("swagger-validate"),l={};Object.keys(m.errors).forEach(function(e){l[e]=m.errors[e]}),Object.keys(f).forEach(function(e){l[e]=f[e]}),r.exports=t,t.logger={debug:n,info:n,warn:n,error:n}},{"./applyAuthData":8,"./errorTypes":11,"./getRequestBody":12,"./getRequestHeaders":13,"./getRequestUrl":14,"swagger-validate":2}],11:[function(e,r,t){"use strict";function n(e){this.name="InvalidRequestError",this.message=e||"Invalid request"}function o(e,r){this.name="MissingAuthorizationError",this.message="No data found for authorization: "+e,this.authorization=r}function a(e){this.name="MissingPathParamsError",this.message="Missing the following required path parameters: "+e.join("")}function i(e,r){var t=r.apiObject.apiDeclaration,n=r.consumes||t.consumes||[];this.name="ContentTypeNotSupportedError",this.message="Operation ["+r.nickname+"] does not accept "+e+". It supports: "+n.join(", ")}function s(e,r){var t=r.apiObject.apiDeclaration,n=r.produces||t.produces||[];this.name="AcceptsNotSupportedError",this.message="Operation ["+r.nickname+"] does not produce "+e+". It supports: "+n.join(", ")}function p(e,r){this.name="OperationValidationError",this.message=e.nickname+" failed validation: \n "+r.join("\n ")}function u(e,r){this.name="ParameterValidationError",this.message=e.name+" failed validation: \n "+r.join("\n ")}function c(e){this.name="DataTypeValidationError",this.message=e||"Invalid data type"}n.prototype=Object.create(Error.prototype),n.prototype.constructor=n,t.InvalidRequestError=n,o.prototype=Object.create(n.prototype),o.prototype.constructor=o,t.MissingAuthorizationError=o,a.prototype=Object.create(n.prototype),a.prototype.constructor=a,t.MissingPathParamsError=a,i.prototype=Object.create(n.prototype),i.prototype.constructor=i,t.ContentTypeNotSupportedError=i,s.prototype=Object.create(n.prototype),s.prototype.constructor=s,t.AcceptsNotSupportedError=s,p.prototype=Object.create(n.prototype),p.prototype.constructor=p,t.OperationValidationError=p,u.prototype=Object.create(n.prototype),u.prototype.constructor=u,t.ParameterValidationError=u,c.prototype=Object.create(Error.prototype),c.prototype.constructor=c,t.DataTypeValidationError=c},{}],12:[function(e,r){"use strict";r.exports=function(e,r,t){var n=e.parameters.filter(function(e){return"body"===e.paramType&&null!=r[e.name]}).map(function(e){return r[e.name]})[0];if(!t||!t["Content-Type"])return n;var o=t["Content-Type"],a=e.parameters.filter(function(e){return"form"===e.paramType&&null!=r[e.name]});if(-1!==o.indexOf("application/x-www-form-urlencoded"))n=a.map(function(e){var t=e.name,n=r[t];return encodeURIComponent(t)+"="+encodeURIComponent(n)}).join("&");else if(-1!==o.indexOf("multipart/form-data")){var i=Math.random().toString(16).substr(2),s="SwaggerBoundary"+i;n=a.map(function(e){var t=e.name,n=r[t],o="--"+s;return o+='\nContent-Disposition: form-data; name="'+t+'"',n.contentType&&(n.name&&(o+='; filename="'+n.name+'"'),o+="\nContent-Type: "+n.contentType),n.contentTransferEncoding&&(o+="\nContent-Transfer-Encoding: "+n.contentTransferEncoding),o+=n.body?"\n\n"+n.body:"\n\n"+n}).join("\n"),n+="\n--"+s+"--\n",t["Content-Type"]=o.replace("multipart/form-data","multipart/form-data; boundary="+s)}else-1!==o.indexOf("application/json")&&"string"!=typeof n&&(n=JSON.stringify(n));return n}},{}],13:[function(e,r,t){"use strict";function n(e,r){var t=e.parameters.some(function(e){return"body"===e.paramType&&void 0!==r[e.name]});if(t)return"application/json";var n=e.parameters.some(function(e){return"form"===e.paramType&&void 0!==r[e.name]}),o=n&&e.parameters.some(function(e){return"File"===e.type&&void 0!==r[e.name]});return o?"multipart/form-data":n?"application/x-www-form-urlencoded":void 0}function o(e,r){var t=e.apiObject.apiDeclaration,n=e.consumes||t.consumes;return n&&n.length?-1!==n.indexOf(r):!0}function a(e,r){var t=e.apiObject.apiDeclaration,n=e.produces||t.produces;return n&&n.length?-1!==n.indexOf(r):!0}var i=e("./errorTypes"),s=i.ContentTypeNotSupportedError,p=i.AcceptsNotSupportedError,u="application/json";r.exports=function(e,r,t){r=r||{},t=t||{};var i={};e.parameters.forEach(function(e){"header"===e.paramType&&null!=r[e.name]&&(i[e.name]=r[e.name])}),t.headers&&Object.keys(t.headers).forEach(function(e){i[e]=t.headers[e]});var c=t.contentType||n(e,r,t);if(c){if(!o(e,c))throw new s(c,e);i["Content-Type"]=c}var f=t.accept||u;if(f){if(!a(e,f))throw new p(f,e);i.Accept=f}return i},t.hasAccept=o,t.hasContentType=a},{"./errorTypes":11}],14:[function(e,r){"use strict";function t(e,r,t){var n=r.parameters.filter(function(e){return"path"===e.paramType}),o=n.filter(function(e){return void 0===t[e.name]});if(o.length)throw new a(o.map(function(e){return e.name}));return n.forEach(function(r){var n=r.name,o=new RegExp("{"+n+"[^}]*}","gi"),a=t[n].toString();delete t[n],a=a.split("/").map(encodeURIComponent).join("/"),e=e.replace(o,a)}),e}function n(e){var r=e.apiObject,t=r.apiDeclaration.basePath,n=r.path.replace("{format}","json");return t+n}var o=e("./errorTypes"),a=o.MissingPathParamsError;r.exports=function(e,r){var o=n(e);if(o=t(o,e,r),!r)return o;var a=e.parameters.filter(function(e){return"query"===e.paramType&&void 0!==r[e.name]}).map(function(e){var t=e.name,n=encodeURIComponent(t),o=r[t];return"array"===e.type&&Array.isArray(o)?o.map(function(e){return n+"="+encodeURIComponent(e)}).join("&"):n+"="+encodeURIComponent(o)}).join("&");return a&&(o+="?"+a),o}},{"./errorTypes":11}]},{},[9])(9)}); 2 | //# sourceMappingURL=swagger-client-generator.min.js.map -------------------------------------------------------------------------------- /dist/swagger-client-generator.min.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"swagger-client-generator.min.js","sources":["/Users/ozan/code/swagger-client-generator/node_modules/boilerplate-gulp/node_modules/browserify/node_modules/browser-pack/_prelude.js","/Users/ozan/code/swagger-client-generator/node_modules/swagger-validate/src/errorTypes.js","/Users/ozan/code/swagger-client-generator/node_modules/swagger-validate/src/index.js","/Users/ozan/code/swagger-client-generator/node_modules/swagger-validate/src/validateArray.js","/Users/ozan/code/swagger-client-generator/node_modules/swagger-validate/src/validateDataType.js","/Users/ozan/code/swagger-client-generator/node_modules/swagger-validate/src/validateModel.js","/Users/ozan/code/swagger-client-generator/node_modules/swagger-validate/src/validateOperation.js","/Users/ozan/code/swagger-client-generator/node_modules/swagger-validate/src/validatePrimitiveTypes.js","/Users/ozan/code/swagger-client-generator/src/applyAuthData.js","/Users/ozan/code/swagger-client-generator/src/createClient.js","/Users/ozan/code/swagger-client-generator/src/createOperationHandler.js","/Users/ozan/code/swagger-client-generator/src/errorTypes.js","/Users/ozan/code/swagger-client-generator/src/getRequestBody.js","/Users/ozan/code/swagger-client-generator/src/getRequestHeaders.js","/Users/ozan/code/swagger-client-generator/src/getRequestUrl.js"],"names":[],"mappings":"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","sourcesContent":["(function e(t,n,r){function s(o,u){if(!n[o]){if(!t[o]){var a=typeof require==\"function\"&&require;if(!u&&a)return a(o,!0);if(i)return i(o,!0);throw new Error(\"Cannot find module '\"+o+\"'\")}var f=n[o]={exports:{}};t[o][0].call(f.exports,function(e){var n=t[o][1][e];return s(n?n:e)},f,f.exports,e,t,n,r)}return n[o].exports}var i=typeof require==\"function\"&&require;for(var o=0;o parseInt(dataType.maximum, 10)){\n return new errorTypes.NumberTooLargeError(candidate, dataType.maximum);\n }\n}\nexports.validateNumber = validateNumber;\n\nfunction validateBoolean(candidate){\n if(!(typeof candidate === 'boolean' || candidate instanceof Boolean)){\n return new errorTypes.NotABooleanError(candidate, typeof candidate);\n }\n}\nexports.validateBoolean = validateBoolean;\n\n\nfunction validateVoid(candidate){\n if(candidate != null){\n return new errorTypes.NotVoidError(candidate, typeof candidate);\n }\n}\nexports.validateVoid = validateVoid;\n\nfunction validateFile(){\n // Not sure how to check this, since anything could qualify as 'File'.\n}\nexports.validateFile = validateFile;\n\nfunction validateString(candidate, dataType){\n if(typeof candidate !== 'string' && !(candidate instanceof String)){\n return new errorTypes.NotAStringError(candidate, typeof candidate);\n }\n\n if('enum' in dataType){\n if(dataType.enum.indexOf(candidate) === -1) {\n return new errorTypes.StringNotInEnumError(candidate, dataType.enum);\n }\n }\n}\nexports.validateString = validateString;","'use strict';\n\nvar MissingAuthorizationError = require('./errorTypes').MissingAuthorizationError;\n\nmodule.exports = function applyAuthData(operation, authData, request){\n var authMap = operation.authorizations;\n if(!authMap) authMap = operation.apiObject.apiDeclaration.authorizations;\n if(!authMap) return;\n\n var authNames = Object.keys(authMap).filter(function(authName){\n // Currently unable to handle oauth2\n return authMap[authName].type !== 'oauth2';\n });\n\n if(authNames.length === 0) return;\n\n if(authNames.length === 1){\n var authName = authNames[0];\n var auth = authMap[authName];\n\n if(!authData) throw new MissingAuthorizationError(authName, auth);\n\n // Unpack nested authData for single auth ops: { apiKey: '123' } -> '123'\n if(authData[authName]) authData = authData[authName];\n\n if(auth.type === 'apiKey'){\n applyApiKey(auth, authName, authData, request);\n } else if(auth.type === 'basicAuth') {\n applyBasicAuth(auth, authName, authData.username, authData.password, request);\n }\n } else {\n var hasAuth = authNames.some(function(authName){\n var auth = authMap[authName];\n var data = authData[authName];\n\n if(!data) return false;\n\n if(auth.type === 'apiKey'){\n applyApiKey(auth, authName, data, request);\n } else if(auth.type === 'basicAuth'){\n applyBasicAuth(auth, authName, data.username, data.password, request);\n }\n\n return true;\n });\n\n if(!hasAuth){\n throw new MissingAuthorizationError(authNames.join(', '), authMap);\n }\n }\n};\n\nfunction applyApiKey(auth, authName, apiKey, request){\n if(!apiKey) throw new MissingAuthorizationError(authName, auth);\n \n if(auth.passAs === 'header'){\n request.headers[auth.keyname] = apiKey;\n } else if(auth.passAs === 'query'){\n var url = request.url;\n var queryParam = auth.keyname + '=' + encodeURIComponent(apiKey);\n if(url.indexOf('?') === -1){\n url += '?' + queryParam;\n } else {\n url = url.replace('?', '?' + queryParam + '&');\n }\n\n request.url = url;\n }\n}\n\nfunction applyBasicAuth(auth, authName, username, password, request){\n if(!username || !password) throw new MissingAuthorizationError(authName, auth);\n \n var url = request.url;\n \n // Only add basic auth once\n if(url.indexOf('@') === -1){\n url = url.replace('://', '://' + username + ':' + password + '@');\n }\n\n request.url = url;\n}","'use strict';\n\nvar createOperationHandler = require('./createOperationHandler');\n\nfunction createClient(schema, requestHandler){\n var api = {},\n apiAuthData,\n authMethodName = 'auth';\n\n schema = processSchema(schema);\n \n // If the 'auth' key is used for any resource or operation, we'll use\n // 'authorization' instead for the auth methods\n var authIsInUse = schema.apis.some(function(resourceObject){\n return resourceObject.apiDeclaration.apis.some(function(apiObject){\n var resourceApiName = getApiName(apiObject.apiDeclaration.resourcePath || apiObject.path);\n if(resourceApiName === 'auth') return true;\n return apiObject.operations.some(function(operation){\n return operation.nickname === 'auth';\n });\n });\n });\n \n if(authIsInUse) authMethodName = 'authorization';\n\n api[authMethodName] = function(){\n if(arguments.length === 0) return apiAuthData;\n apiAuthData = processApiAuthArgs(arguments);\n };\n\n schema.apis.forEach(function(resourceObject){\n var resourceName,\n resourceApi,\n resourceAuthData;\n\n if(resourceObject.apiDeclaration.resourcePath){\n resourceName = getApiName(resourceObject.apiDeclaration.resourcePath);\n resourceApi = api[resourceName] = {};\n resourceApi[authMethodName] = function(){\n if(arguments.length === 0) return resourceAuthData;\n resourceAuthData = processApiAuthArgs(arguments);\n };\n }\n\n resourceObject.apiDeclaration.apis.forEach(function(apiObject){\n var apiObjectName = resourceName,\n apiObjectApi = resourceApi,\n apiObjectAuthData;\n\n if(!apiObjectName){\n apiObjectName = getApiName(apiObject.path);\n apiObjectApi = api[apiObjectName] = {};\n apiObjectApi[authMethodName] = function(){\n if(arguments.length === 0) return apiObjectAuthData;\n\n apiObjectAuthData = processApiAuthArgs(arguments);\n };\n }\n\n apiObject.operations.forEach(function(operation){\n var operationHandlerName = operation.nickname,\n operationAuthData,\n operationHandler; \n \n function getAuthData(){\n return operationAuthData || apiObjectAuthData || resourceAuthData || apiAuthData;\n }\n\n operationHandler = createOperationHandler(operation, getAuthData, requestHandler);\n\n operationHandler[authMethodName] = function(){\n if(arguments.length === 0) return operationAuthData;\n\n operationAuthData = processApiAuthArgs(arguments);\n };\n\n apiObjectApi[operationHandlerName] = operationHandler;\n });\n });\n });\n\n return api;\n}\nmodule.exports = createClient;\n\nfunction processApiAuthArgs(args){\n // for basic auth, allow calls with two args (username, password)\n if(typeof args[0] === 'string' && typeof args[1] === 'string') {\n return {\n username: args[0],\n password: args[1]\n };\n } else {\n return args[0];\n }\n}\n\n// Helpper method which assings back pointer to object parents and returns\n// the api objects within the given schema.\nfunction processSchema(schema){\n schema.apis.forEach(function(resourceObject){\n resourceObject.resourceListing = schema;\n\n resourceObject.apiDeclaration.apis.forEach(function(apiObject){\n apiObject.resourceObject = resourceObject;\n apiObject.apiDeclaration = resourceObject.apiDeclaration;\n\n apiObject.operations.forEach(function(operation){\n operation.apiObject = apiObject;\n\n operation.parameters.forEach(function(parameter){\n parameter.operation = operation;\n });\n });\n });\n });\n\n return schema;\n}\n\n// Takes a path and returns a JavaScript-friendly variable name\nfunction getApiName(name){\n // String non-word characters\n name = name.replace(/\\W/g, '/');\n\n // Turn paths which look/like/this to lookLikeThis\n name = name.replace(/(\\w)\\/(\\w)/g, function(match, p1, p2){\n return p1 + p2.toUpperCase();\n });\n\n name = name.replace(/\\//g, '');\n\n return name;\n}","'use strict';\n\nvar getRequestHeaders = require('./getRequestHeaders'),\n getRequestUrl = require('./getRequestUrl'),\n getRequestBody = require('./getRequestBody'),\n applyAuthData = require('./applyAuthData'),\n errorTypes = require('./errorTypes'),\n swaggerValidate = require('swagger-validate');\n\nvar allErrorTypes = {};\nObject.keys(swaggerValidate.errors).forEach(function(errorName){\n allErrorTypes[errorName] = swaggerValidate.errors[errorName];\n});\n\nObject.keys(errorTypes).forEach(function(errorName){\n allErrorTypes[errorName] = errorTypes[errorName];\n});\n\nfunction createOperationHandler(operation, getAuthData, requestHandler){\n function Request(data, options){\n this.method = operation.method;\n this.operation = operation;\n this.errorTypes = allErrorTypes;\n this.data = data;\n this.options = options;\n }\n\n var operationHandler = function(data, options){\n var error,\n request;\n \n options = options || {};\n \n if(data == null) data = {};\n\n // if a function is passed in as options, assume it's a callback function\n // for convenience\n if(typeof options === 'function'){\n options.callback = options;\n }\n\n try{\n data = prune(data);\n data = singleParamConvenienceProcessor(operation, data);\n data = removeUnknownParams(operation, data);\n\n error = swaggerValidate.operation(data, operation, operation.apiObject.apiDeclaration.models);\n \n request = new Request(data, options);\n \n // If we know there is an error, don't attempt to craft the request params.\n // The request param generators assume valid data to work properly.\n if(!error){\n request.url = getRequestUrl(operation, data);\n request.headers = getRequestHeaders(operation, data, options);\n request.body = getRequestBody(operation, data, request.headers);\n \n applyAuthData(operation, getAuthData(), request);\n }\n } catch(e){\n error = e;\n }\n \n return requestHandler(error, request);\n };\n\n // Useful for instanceof checks\n operationHandler.Request = Request;\n operationHandler.errorTypes = allErrorTypes;\n\n // Useful for reflection\n operationHandler.operation = operation;\n \n operationHandler.getUrl = function(data){\n data = prune(data);\n data = singleParamConvenienceProcessor(operation, data);\n data = removeUnknownParams(operation, data);\n\n var error = swaggerValidate.operation(data, operation, operation.apiObject.apiDeclaration.models);\n if(error) throw error;\n \n return getRequestUrl(operation, data);\n };\n\n\n // Can be used to preemptively validate without action\n operationHandler.validate = function(data){\n return swaggerValidate.operation(data, operation, operation.apiObject.apiDeclaration.models);\n };\n\n return operationHandler;\n}\nmodule.exports = createOperationHandler;\n\nfunction noop(){}\ncreateOperationHandler.logger = {\n debug: noop,\n info: noop,\n warn: noop,\n error: noop\n};\n\n// Stringify and parse the data to clean up undefined, and non-scalar properties\nfunction prune(data){\n return JSON.parse(JSON.stringify(data));\n}\n\n// Enables data to be passed directly for single param operations.\nfunction singleParamConvenienceProcessor(operation, data){\n // If there are more than one params, bail\n var requiredParams = operation.parameters.filter(function(param){\n return param.required;\n });\n\n // If there are more than one required params, or if there is no required param\n // and there are many optional params, bail\n if(requiredParams.length > 1) return data;\n\n if(requiredParams.length !== 1 && operation.parameters.length !== 1) return data;\n\n var param = requiredParams[0] || operation.parameters[0];\n \n // If the param is already defined explicitly, bail\n if(typeof data === 'object' && data[param.name] !== undefined) return data;\n\n var models = operation.apiObject.apiDeclaration.models;\n\n // If the data passed is is not valid for the param data type, bail\n var error;\n\n try {\n error = swaggerValidate.dataType(data, param, models); \n } catch(e){\n return data;\n }\n \n // If the data passed is a valid param data type, bail\n if(!error){\n var wrapper = {};\n wrapper[param.name] = data;\n return wrapper;\n } else {\n return data;\n }\n}\n \n\nfunction removeUnknownParams(operation, data){\n if(!data || typeof data !== 'object') return data;\n\n var paramNames = {};\n operation.parameters.forEach(function(param){\n paramNames[param.name] = true;\n });\n\n var unknownKeys = Object.keys(data).filter(function(key){\n return !(key in paramNames);\n });\n\n createOperationHandler.logger.warn('Unknown parameters removed from request:', \n unknownKeys.join(', '));\n\n unknownKeys.forEach(function(key){\n delete data[key];\n });\n\n return data;\n}","'use strict';\n\nfunction InvalidRequestError(message){\n this.name = 'InvalidRequestError';\n this.message = message || 'Invalid request';\n}\nInvalidRequestError.prototype = Object.create(Error.prototype);\nInvalidRequestError.prototype.constructor = InvalidRequestError;\n\nexports.InvalidRequestError = InvalidRequestError;\n\n\nfunction MissingAuthorizationError(authName, auth){\n this.name = 'MissingAuthorizationError';\n this.message = 'No data found for authorization: ' + authName;\n this.authorization = auth;\n}\nMissingAuthorizationError.prototype = Object.create(InvalidRequestError.prototype);\nMissingAuthorizationError.prototype.constructor = MissingAuthorizationError;\n\nexports.MissingAuthorizationError = MissingAuthorizationError;\n\n\nfunction MissingPathParamsError(pathParams){\n this.name = 'MissingPathParamsError';\n this.message = 'Missing the following required path parameters: ' + pathParams.join('');\n}\nMissingPathParamsError.prototype = Object.create(InvalidRequestError.prototype);\nMissingPathParamsError.prototype.constructor = MissingPathParamsError;\n\nexports.MissingPathParamsError = MissingPathParamsError;\n\n\nfunction ContentTypeNotSupportedError(contentType, operation){\n var apiDeclaration = operation.apiObject.apiDeclaration;\n var consumes = operation.consumes || apiDeclaration.consumes || [];\n\n this.name = 'ContentTypeNotSupportedError';\n this.message = 'Operation [' + operation.nickname + '] does not accept ' + contentType + '. It supports: ' + \n consumes.join(', ');\n}\nContentTypeNotSupportedError.prototype = Object.create(InvalidRequestError.prototype);\nContentTypeNotSupportedError.prototype.constructor = ContentTypeNotSupportedError;\n\nexports.ContentTypeNotSupportedError = ContentTypeNotSupportedError;\n\n\nfunction AcceptsNotSupportedError(accepts, operation){\n var apiDeclaration = operation.apiObject.apiDeclaration;\n var produces = operation.produces || apiDeclaration.produces || [];\n\n this.name = 'AcceptsNotSupportedError';\n this.message = 'Operation [' + operation.nickname + '] does not produce ' + accepts + '. It supports: ' + \n produces.join(', ');\n}\nAcceptsNotSupportedError.prototype = Object.create(InvalidRequestError.prototype);\nAcceptsNotSupportedError.prototype.constructor = AcceptsNotSupportedError;\n\nexports.AcceptsNotSupportedError = AcceptsNotSupportedError;\n\n\nfunction OperationValidationError(operation, errors){\n this.name = 'OperationValidationError';\n this.message = operation.nickname + ' failed validation: \\n\\t' + errors.join('\\n\\t');\n}\nOperationValidationError.prototype = Object.create(InvalidRequestError.prototype);\nOperationValidationError.prototype.constructor = OperationValidationError;\n\nexports.OperationValidationError = OperationValidationError;\n\n\nfunction ParameterValidationError(parameter, errors){\n this.name = 'ParameterValidationError';\n this.message = parameter.name + ' failed validation: \\n\\t' + errors.join('\\n\\t');\n}\nParameterValidationError.prototype = Object.create(InvalidRequestError.prototype);\nParameterValidationError.prototype.constructor = ParameterValidationError;\n\nexports.ParameterValidationError = ParameterValidationError;\n\n\nfunction DataTypeValidationError(message){\n this.name = 'DataTypeValidationError';\n this.message = message || 'Invalid data type';\n}\nDataTypeValidationError.prototype = Object.create(Error.prototype);\nDataTypeValidationError.prototype.constructor = DataTypeValidationError;\n\nexports.DataTypeValidationError = DataTypeValidationError;","'use strict';\n\nmodule.exports = function getRequestBody(operation, data, headers){\n var body = operation.parameters.filter(function(param){\n return param.paramType === 'body' && data[param.name] != null;\n }).map(function(param){\n return data[param.name];\n })[0];\n\n if(!(headers && headers['Content-Type'])) return body;\n\n var contentType = headers['Content-Type'];\n var presentFormParams = operation.parameters.filter(function(param){\n return param.paramType === 'form' && data[param.name] != null;\n });\n\n if(contentType.indexOf('application/x-www-form-urlencoded') !== -1){\n body = presentFormParams.map(function(param){\n var key = param.name,\n value = data[key];\n return encodeURIComponent(key) + '=' + encodeURIComponent(value);\n }).join('&');\n } else if(contentType.indexOf('multipart/form-data') !== -1){\n var randomness = Math.random().toString(16).substr(2);\n var boundary = 'SwaggerBoundary' + randomness;\n \n body = presentFormParams.map(function(param){\n var key = param.name,\n value = data[key],\n result = '--' + boundary;\n\n result += '\\nContent-Disposition: form-data; name=\"' + key + '\"';\n \n if(value.contentType){\n if(value.name){\n result += '; filename=\"' + value.name + '\"';\n }\n\n result += '\\nContent-Type: ' + value.contentType;\n }\n\n if(value.contentTransferEncoding){\n result += '\\nContent-Transfer-Encoding: ' + value.contentTransferEncoding;\n }\n\n if(value.body){\n result += '\\n\\n' + value.body;\n } else {\n result += '\\n\\n' + value;\n }\n\n return result;\n }).join('\\n');\n\n body += '\\n--' + boundary + '--\\n';\n \n headers['Content-Type'] = contentType.replace(\n 'multipart/form-data', \n 'multipart/form-data; boundary=' + boundary\n );\n } else if(contentType.indexOf('application/json') !== -1){\n if(typeof body !== 'string'){\n body = JSON.stringify(body);\n }\n }\n\n return body;\n};","'use strict';\n\nvar errorTypes = require('./errorTypes'),\n ContentTypeNotSupportedError = errorTypes.ContentTypeNotSupportedError,\n AcceptsNotSupportedError = errorTypes.AcceptsNotSupportedError;\n\nvar DEFAULT_ACCEPT = 'application/json';\nmodule.exports = function getRequestHeaders(operation, data, options){\n data = data || {};\n options = options || {};\n\n var headers = {};\n\n operation.parameters.forEach(function(param){\n if(param.paramType === 'header' && data[param.name] != null){\n headers[param.name] = data[param.name];\n }\n });\n\n // Passed headers\n if(options.headers){\n Object.keys(options.headers).forEach(function(key){\n headers[key] = options.headers[key];\n });\n }\n\n // Content-Type\n var contentType = options.contentType || getContentType(operation, data, options);\n if(contentType) {\n if(hasAccept(operation, contentType)){\n headers['Content-Type'] = contentType; \n } else {\n throw new ContentTypeNotSupportedError(contentType, operation);\n }\n }\n\n // Accept\n var accept = options.accept || DEFAULT_ACCEPT;\n if(accept){\n if(hasContentType(operation, accept)){\n headers.Accept = accept; \n } else {\n throw new AcceptsNotSupportedError(accept, operation);\n }\n }\n \n return headers;\n};\n\nfunction getContentType(operation, data){\n var hasBody = operation.parameters.some(function(param){\n return param.paramType === 'body' && data[param.name] !== undefined;\n });\n\n if (hasBody){\n return 'application/json';\n } else {\n var hasFormParams = operation.parameters.some(function(param){\n return param.paramType === 'form' && data[param.name] !== undefined;\n });\n\n var hasFileParam = hasFormParams && \n operation.parameters.some(function(param){\n return param.type === 'File' && data[param.name] !== undefined;\n });\n\n if(hasFileParam) return 'multipart/form-data';\n else if(hasFormParams) return 'application/x-www-form-urlencoded';\n }\n}\n\n// Accepts is an optional field in the spec, but must be enforced when present\nfunction hasAccept(operation, contentType){\n var apiDeclaration = operation.apiObject.apiDeclaration;\n var accepts = operation.consumes || apiDeclaration.consumes;\n\n if(accepts && accepts.length){\n return accepts.indexOf(contentType) !== -1;\n } else {\n return true;\n }\n}\nexports.hasAccept = hasAccept;\n\n// Content-Type (produces) is an optional field in the spec, but must be enforced when present\nfunction hasContentType(operation, contentType){\n var apiDeclaration = operation.apiObject.apiDeclaration,\n contentTypes = operation.produces || apiDeclaration.produces;\n\n if(contentTypes && contentTypes.length){\n return contentTypes.indexOf(contentType) !== -1;\n } else {\n return true;\n }\n}\nexports.hasContentType = hasContentType;","'use strict';\n\nvar errorTypes = require('./errorTypes'),\n MissingPathParamsError = errorTypes.MissingPathParamsError;\n\nmodule.exports = function getRequestUrl(operation, data){\n var url = getUrlTemplate(operation);\n\n url = applyPathParams(url, operation, data);\n\n if(!data) return url;\n\n var queryParams = operation.parameters.filter(function(param){\n return param.paramType === 'query' && data[param.name] !== undefined;\n }).map(function(param){\n var key = param.name;\n var encodedKey = encodeURIComponent(key);\n var value = data[key];\n \n // For arrays, create multiple of the same query params to accomodate \n // the spec ambiguity on the issue: http://docs.oracle.com/javaee/6/api/\n // javax/servlet/ServletRequest.html#getParameterValues(java.lang.String)\n if(param.type === 'array' && Array.isArray(value)){\n return value.map(function(item){\n return encodedKey + '=' + encodeURIComponent(item);\n }).join('&');\n } else {\n return encodedKey + '=' + encodeURIComponent(value); \n }\n }).join('&');\n\n if(queryParams) url += '?' + queryParams;\n\n return url;\n};\n\nfunction applyPathParams(url, operation, data){\n var pathParams = operation.parameters.filter(function(param){\n return param.paramType === 'path';\n });\n\n var missingParams = pathParams.filter(function(param){\n return data[param.name] === undefined;\n });\n\n if(missingParams.length){\n throw new MissingPathParamsError(missingParams.map(function(param){\n return param.name;\n }));\n }\n\n pathParams.forEach(function(param){\n var key = param.name;\n \n var exp = new RegExp('{' + key + '[^}]*}', 'gi');\n\n var value = data[key].toString();\n delete data[key];\n value = value.split('/').map(encodeURIComponent).join('/');\n\n url = url.replace(exp, value);\n });\n\n return url;\n}\n\nfunction getUrlTemplate(operation){\n var apiObject = operation.apiObject; \n\n var basePath = apiObject.apiDeclaration.basePath;\n var path = apiObject.path.replace('{format}', 'json');\n \n return basePath + path;\n}\n"],"sourceRoot":"/source/"} --------------------------------------------------------------------------------