hapi-swagger
Advanced tools
Comparing version 3.0.0-rc1 to 3.0.0-rc2
@@ -8,5 +8,5 @@ 'use strict'; | ||
listModel, | ||
standardHTTPErrors, | ||
extendedHTTPErrors, | ||
fileHTTPErrors; | ||
standardHTTP, | ||
extendedHTTP, | ||
fileHTTP; | ||
@@ -36,3 +36,3 @@ | ||
modified: Joi.string().isoDate().description('ISO date string'), | ||
}).meta({ | ||
}).description('json body for sum').meta({ | ||
className: 'Sum' | ||
@@ -52,11 +52,6 @@ }); | ||
standardHTTPErrors = { | ||
standardHTTP = { | ||
'200': { | ||
'description': 'Success', | ||
"schema": { | ||
"type": "array", | ||
"items": { | ||
"$ref": "#/definitions/Sum" | ||
} | ||
} | ||
"schema": sumModel | ||
}, | ||
@@ -72,3 +67,3 @@ '400': { | ||
extendedHTTPErrors = { | ||
extendedHTTP = { | ||
'400': { | ||
@@ -86,3 +81,3 @@ 'description': 'Bad Request' | ||
fileHTTPErrors = { | ||
fileHTTP = { | ||
'400': { | ||
@@ -105,176 +100,115 @@ 'description': 'Bad Request' | ||
module.exports = [{ | ||
method: 'PUT', | ||
path: '/sum/add/{a}/{b}', | ||
method: 'POST', | ||
path: '/tools/microformats/1', | ||
config: { | ||
tags: ['api'], | ||
plugins: { | ||
'hapi-swagger': { | ||
nickname: 'microformatsapi1', | ||
validate: { | ||
payload: { | ||
a: Joi.number() | ||
.required() | ||
.description('the first number'), | ||
b: Joi.number() | ||
.required() | ||
.description('the first number') | ||
}, | ||
query: { | ||
testquery: Joi.string() | ||
}, | ||
params: { | ||
testparam: Joi.string() | ||
}, | ||
headers: { | ||
testheaders: Joi.string() | ||
} | ||
} | ||
}, | ||
}, | ||
handler: { | ||
proxy: { | ||
host: 'glennjones.net', | ||
protocol: 'http', | ||
onResponse: defaultHandler | ||
} | ||
} | ||
} | ||
},{ | ||
method: 'POST', | ||
path: '/tools/microformats/2', | ||
config: { | ||
tags: ['api'], | ||
plugins: { | ||
'hapi-swagger': { | ||
nickname: 'microformatsapi2', | ||
validate: { | ||
payload: Joi.object({ | ||
a: Joi.number() | ||
.required() | ||
.description('the first number'), | ||
b: Joi.number() | ||
.required() | ||
.description('the first number') | ||
}).meta({className: 'SumX'}), | ||
query: { | ||
testquery: Joi.string() | ||
}, | ||
params: { | ||
testparam: Joi.string() | ||
}, | ||
headers: { | ||
testheaders: Joi.string() | ||
} | ||
} | ||
}, | ||
}, | ||
handler: { | ||
proxy: { | ||
host: 'glennjones.net', | ||
protocol: 'http', | ||
onResponse: defaultHandler | ||
} | ||
} | ||
} | ||
},{ | ||
method: 'POST', | ||
path: '/store/payload/1', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Add', | ||
tags: ['api','reduced'], | ||
notes: ['Adds together two numbers and return the result. As an option you can have the result return as a binary number.'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: standardHTTPErrors | ||
} | ||
}, | ||
handler: defaultHandler, | ||
description: 'Add sum, with JSON object', | ||
notes: ['Adds a sum to the data store, using JSON object in payload'], | ||
tags: ['api','reduced','three'], | ||
validate: { | ||
params: { | ||
payload: Joi.object({ | ||
a: Joi.number() | ||
.required() | ||
.description('the first number') | ||
.example(8), | ||
.description('the first number'), | ||
b: Joi.number() | ||
.required() | ||
.description('the second number') | ||
.example(4) | ||
}, | ||
headers: Joi.object({ | ||
'x-format': Joi.string() | ||
.valid('decimal', 'binary') | ||
.default('decimal') | ||
.description('return result as decimal or binary') | ||
}).unknown() | ||
}, | ||
response: {schema : resultModel} | ||
} | ||
},{ | ||
method: 'PUT', | ||
path: '/sum/subtract/{a}/{b}', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Subtract', | ||
notes: ['Subtracts the second number from the first and return the result'], | ||
tags: ['api'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: standardHTTPErrors | ||
} | ||
}, | ||
validate: { | ||
params: { | ||
a: Joi.number() | ||
.required() | ||
.description('the first number'), | ||
.description('the second number'), | ||
b: Joi.number() | ||
operator: Joi.string() | ||
.required() | ||
.description('the second number') | ||
} | ||
}, | ||
response: {schema : resultModel} | ||
} | ||
},{ | ||
method: 'PUT', | ||
path: '/sum/divide/{a}/{b}', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Divide', | ||
notes: ['Divides the first number by the second and return the result'], | ||
tags: ['api'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: standardHTTPErrors | ||
} | ||
}, | ||
validate: { | ||
params: { | ||
a: Joi.number() | ||
.required() | ||
.description('the first number - can NOT be 0'), | ||
.default('+') | ||
.description('the opertator i.e. + - / or *'), | ||
b: Joi.number() | ||
equals: Joi.number() | ||
.required() | ||
.description('the second number - can NOT be 0') | ||
} | ||
.description('the result of the sum') | ||
}).meta({className: 'Sum'}) | ||
}, | ||
response: {schema : resultModel} | ||
} | ||
},{ | ||
method: 'PUT', | ||
path: '/sum/multiple/{a}/{b}', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Multiple', | ||
notes: ['Multiples the two numbers together and return the result'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: standardHTTPErrors | ||
} | ||
}, | ||
tags: ['api','reduced'], | ||
validate: { | ||
params: { | ||
a: Joi.number() | ||
.required() | ||
.description('the first number'), | ||
b: Joi.number() | ||
.required() | ||
.description('the second number') | ||
} | ||
}, | ||
response: {schema : resultModel} | ||
} | ||
},{ | ||
method: 'GET', | ||
path: '/store/', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'List sums', | ||
notes: ['List the sums in the data store'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: standardHTTPErrors | ||
} | ||
}, | ||
tags: ['api','reduced','one'], | ||
validate: { | ||
query: { | ||
page: Joi.number() | ||
.description('the page number'), | ||
pagesize: Joi.number() | ||
.description('the number of items to a page') | ||
} | ||
}, | ||
response: {schema : listModel} | ||
} | ||
}, { | ||
method: 'GET', | ||
path: '/store/{id}', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Get sum', | ||
notes: ['Get a sum from the store'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: extendedHTTPErrors | ||
} | ||
}, | ||
tags: ['api','reduced','two'], | ||
validate: { | ||
params: { | ||
id: Joi.string() | ||
.required() | ||
.description('the id of the sum in the store') | ||
} | ||
}, | ||
response: {schema : sumModel} | ||
} | ||
}, { | ||
method: 'POST', | ||
path: '/store/', | ||
path: '/store/payload/2', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Add sum', | ||
notes: ['Adds a sum to the data store'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: standardHTTPErrors, | ||
payloadType: 'form', | ||
nickname: 'storeit' | ||
} | ||
}, | ||
handler: defaultHandler, | ||
description: 'Add sum, with JSON object', | ||
notes: ['Adds a sum to the data store, using JSON object in payload'], | ||
tags: ['api','reduced','three'], | ||
validate: { | ||
payload: { | ||
payload: Joi.object({ | ||
a: Joi.number() | ||
@@ -296,26 +230,15 @@ .required() | ||
.description('the result of the sum') | ||
} | ||
}).meta({className: 'Sum'}) | ||
}, | ||
response: {schema : sumModel} | ||
} | ||
}, { | ||
method: 'PUT', | ||
path: '/store/{id}', | ||
},{ | ||
method: 'POST', | ||
path: '/store/payload/3', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Update sum', | ||
notes: ['Update a sum in our data store'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: extendedHTTPErrors, | ||
payloadType: 'form' | ||
} | ||
}, | ||
tags: ['api'], | ||
validate: { | ||
params: { | ||
id: Joi.string() | ||
.required() | ||
.description('the id of the sum in the store') | ||
}, | ||
handler: defaultHandler, | ||
description: 'Add sum, with JSON object', | ||
notes: ['Adds a sum to the data store, using JSON object in payload'], | ||
tags: ['api','reduced','three'], | ||
validate: { | ||
payload: { | ||
@@ -340,35 +263,15 @@ a: Joi.number() | ||
}, | ||
response: {schema : sumModel} | ||
} | ||
}, { | ||
method: 'DELETE', | ||
path: '/store/{id}', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Delete sums', | ||
notes: ['Delete a sums from the data store'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: extendedHTTPErrors | ||
} | ||
}, | ||
tags: ['api'], | ||
validate: { | ||
params: { | ||
id: Joi.string() | ||
.required() | ||
.description('the id of the sum in the store') | ||
} | ||
} | ||
} | ||
}, { | ||
},{ | ||
method: 'POST', | ||
path: '/store/payload/', | ||
path: '/store/', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Add sum, with JSON object', | ||
notes: ['Adds a sum to the data store, using JSON object in payload'], | ||
description: 'Add sum', | ||
notes: ['Adds a sum to the data store'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: standardHTTPErrors | ||
payloadType: 'form', | ||
nickname: 'storeit' | ||
} | ||
@@ -396,47 +299,7 @@ }, | ||
} | ||
}, | ||
response: {schema : sumModel} | ||
} | ||
}, { | ||
method: 'POST', | ||
path: '/store/file/', | ||
config: { | ||
handler: defaultHandler, | ||
description: 'Add sum, with JSON file', | ||
notes: ['Adds a sum to the data store, using JSON object in a uploaded file'], | ||
plugins: { | ||
'hapi-swagger': { | ||
responses: fileHTTPErrors, | ||
payloadType: 'form' | ||
} | ||
}, | ||
tags: ['api','reduced','three'], | ||
validate: { | ||
payload: { | ||
file: Joi.any() | ||
.meta({ swaggerType: 'file' }) | ||
.required() | ||
.description('json file with object containing: a, b, operator and equals') | ||
} | ||
}, | ||
payload: { | ||
maxBytes: 1048576, | ||
parse: true, | ||
output: 'stream' | ||
}, | ||
response: {schema : sumModel} | ||
} | ||
},{ | ||
method: 'GET', | ||
path: '/{path*}', | ||
handler: { | ||
directory: { | ||
path: './public', | ||
listing: false, | ||
index: true | ||
} | ||
} | ||
}]; | ||
}, ]; | ||
@@ -5,2 +5,3 @@ var Hapi = require('hapi'), | ||
Blipp = require('blipp'), | ||
H2o2 = require('h2o2'), | ||
HapiSwagger = require('../'), | ||
@@ -37,2 +38,9 @@ Pack = require('../package'), | ||
} | ||
},{ | ||
"name": "sum", | ||
"description": "API of sums", | ||
"externalDocs": { | ||
"description": "Find out more", | ||
"url": "http://example.org" | ||
} | ||
}] | ||
@@ -45,2 +53,3 @@ }; | ||
Blipp, | ||
H2o2, | ||
{ | ||
@@ -47,0 +56,0 @@ register: HapiSwagger, |
@@ -72,3 +72,3 @@ /* | ||
*/ | ||
function _getSchema( request ){ | ||
function getSchema( request ){ | ||
return request.headers['x-forwarded-proto'] || request.server.info.protocol; | ||
@@ -79,21 +79,2 @@ } | ||
/** | ||
* finds the schema | ||
* | ||
* @param {Object} request | ||
* @return {String} | ||
*/ | ||
function getBasePath( settings, options, request ){ | ||
// treat protocol and host as defaults, and override with settings | ||
var baseUrl = Hoek.applyToDefaults( | ||
{ | ||
'protocol': settings.schema, | ||
'host': settings.host | ||
}, | ||
Url.parse(options.basePath) | ||
); | ||
return Url.format( baseUrl ); | ||
} | ||
/** | ||
* removes none schema properties from options | ||
@@ -135,4 +116,3 @@ * | ||
builder.default.host = getHost( request ); | ||
builder.default.schemes = [_getSchema( request )]; | ||
//builder.default.basePath = getBasePath( builder.default, settings, request ); | ||
builder.default.schemes = [getSchema( request )]; | ||
@@ -139,0 +119,0 @@ |
@@ -9,6 +9,70 @@ /* | ||
Boom = require('boom'), | ||
Joi = require('joi'); | ||
Joi = require('joi'), | ||
Properties = require('../lib/properties'); | ||
var definitions = module.exports = {}; | ||
var definitions = module.exports = {}, | ||
internals = {}; | ||
/** | ||
* creates a new definition object | ||
* | ||
* @param {Object} joiObj | ||
* @param {Object} definitions | ||
* @return {Object} | ||
*/ | ||
definitions.createDefinition = function (joiObj, definitions) { | ||
// TODO changes this to new method | ||
var properties = Properties.parseProperties(joiObj, definitions); | ||
var propertyArray = Properties.propertiesObjToArray(properties, null); | ||
return this.build( propertyArray ); | ||
} | ||
definitions.appendDefinition = function (name, joiObj, collection, altName) { | ||
// create definition object | ||
var definition = this.createDefinition(joiObj, collection); | ||
// find existing definition by this name | ||
var foundDefinition = collection[name]; | ||
if (foundDefinition) { | ||
// deep compare objects | ||
if(Hoek.deepEqual(foundDefinition, definition)){ | ||
// return existing name if existing object is exactly the same | ||
return name; | ||
}else{ | ||
// create new definition with altName | ||
// to stop reuse of definition with same name but different structures | ||
collection[altName] = definition; | ||
return altName; | ||
} | ||
}else{ | ||
// create new definition | ||
collection[name || altName] = definition; | ||
return [name || altName]; | ||
} | ||
}; | ||
/** | ||
* Given a JOI schema get its className | ||
* | ||
* @param {Object} joiObj | ||
* @return {String || undefined} | ||
*/ | ||
internals.getJOIClassName = function(joiObj) { | ||
if(joiObj && joiObj._meta && Array.isArray(joiObj._meta)){ | ||
var i = joiObj._meta.length; | ||
while (i--) { | ||
if(joiObj._meta[i].className){ | ||
return joiObj._meta[i].className | ||
} | ||
} | ||
} | ||
return undefined; | ||
} | ||
@@ -23,34 +87,71 @@ | ||
definitions.build = function( parameters ){ | ||
var out = definitions.createObject(), | ||
props = definitions.createProperties( parameters ); | ||
// merge in properties and required structures | ||
out = Hoek.merge(props, out); | ||
return out; | ||
} | ||
/** | ||
* builds basic definition object | ||
* | ||
* @return {Object} | ||
*/ | ||
definitions.createObject = function(){ | ||
return { | ||
'type': 'object', | ||
'properties': {} | ||
}; | ||
} | ||
/** | ||
* builds definition object properties structure | ||
* | ||
* @param {Object} parameters | ||
* @return {Object} | ||
*/ | ||
definitions.createProperties = function( parameters ){ | ||
var out = { | ||
'type': 'object', | ||
'properties': {} | ||
}; | ||
properties: {} | ||
}; | ||
//console.log(JSON.stringify(parameters )) | ||
for (var key in parameters) { | ||
// restructure path parameters to to JSON schema structure used in definitions | ||
for (var key in parameters) { | ||
if (parameters.hasOwnProperty(key)) { | ||
var obj = parameters[key]; | ||
// move required to top level | ||
if( obj.required ){ | ||
if(out.required === undefined){ | ||
out.required = []; | ||
} | ||
out.required.push(obj.name) | ||
var obj = parameters[key]; | ||
// move required to top level | ||
if( obj.required ){ | ||
if(out.required === undefined){ | ||
out.required = []; | ||
} | ||
// remove properties with undefined values | ||
for (var objkey in obj) { | ||
if(obj[objkey] === undefined){ | ||
delete obj[objkey]; | ||
} | ||
out.required.push(obj.name) | ||
} | ||
// remove properties with undefined values | ||
for (var objkey in obj) { | ||
if(obj[objkey] === undefined){ | ||
delete obj[objkey]; | ||
} | ||
out.properties[obj.name] = obj; | ||
// remove unneeded properties | ||
delete obj.name | ||
delete obj.required; | ||
} | ||
// recurse into child objects | ||
if(obj.type === 'object' && obj.properties){ | ||
out.properties[obj.name] = definitions.createProperties( obj.properties ); | ||
}else{ | ||
// if child has no name use its key | ||
out.properties[obj.name || key] = obj; | ||
} | ||
// remove unneeded properties | ||
delete obj.name | ||
delete obj.required; | ||
} | ||
return out; | ||
@@ -57,0 +158,0 @@ } |
@@ -34,40 +34,35 @@ /* | ||
//console.log(tags) | ||
if (tags) { | ||
return routes.filter(function(route) { | ||
for (var i = 0; i < tags.length; i++) { | ||
switch(tags[i].substring(0,1)) { | ||
case '-': // exclude tags that match this case | ||
tag = tags[i].substring(1,tags[i].length); | ||
if (Hoek.intersect(route.settings.tags, [tag]).length > 0) { | ||
exit = true; | ||
} | ||
break; | ||
case ' ': // (+) filter out tagged paths that do not have this tag! | ||
tag = tags[i].substring(1,tags[i].length); | ||
if (Hoek.intersect(route.settings.tags, [tag]).length == 0) { | ||
exit = true; | ||
} | ||
break; | ||
} | ||
return routes.filter(function(route) { | ||
for (var i = 0; i < tags.length; i++) { | ||
switch(tags[i].substring(0,1)) { | ||
case '-': // exclude tags that match this case | ||
tag = tags[i].substring(1,tags[i].length); | ||
if (Hoek.intersect(route.settings.tags, [tag]).length > 0) { | ||
exit = true; | ||
} | ||
break; | ||
case '+': // (+) filter out tagged paths that do not have this tag! | ||
tag = tags[i].substring(1,tags[i].length); | ||
if (Hoek.intersect(route.settings.tags, [tag]).length == 0) { | ||
exit = true; | ||
} | ||
break; | ||
} | ||
} | ||
// if we have reason to exit, then do so! | ||
if (exit == true) { | ||
return false; | ||
} | ||
// default behavior for tags is additive | ||
if (Hoek.intersect(route.settings.tags, tags).length > 0) { | ||
return true; | ||
} | ||
// fallback or no tag defined | ||
return false; | ||
}); | ||
// if we have reason to exit, then do so! | ||
if (exit == true) { | ||
return false; | ||
} | ||
// default behavior for tags is additive | ||
if (Hoek.intersect(route.settings.tags, tags).length > 0) { | ||
return true; | ||
} | ||
// fallback or no tag defined | ||
return false; | ||
}); | ||
}else{ | ||
return routes; | ||
} | ||
} |
@@ -38,2 +38,3 @@ /* | ||
} | ||
} | ||
@@ -40,0 +41,0 @@ x++; |
@@ -44,5 +44,9 @@ | ||
var settings = Hoek.applyToDefaults(defaults, options || {}), | ||
var settings = Hoek.applyToDefaults(defaults, options), | ||
swaggerDirPath = __dirname + Path.sep + '..' + Path.sep + 'public' + Path.sep + 'swaggerui'; | ||
Hoek.assert(plugin.registrations.vision, 'Missing vision plug-in registation'); | ||
Hoek.assert(plugin.registrations.inert, 'Missing inert plug-in registation'); | ||
// add routing for swaggerui static assets /swaggerui/ | ||
@@ -120,5 +124,2 @@ plugin.views({ | ||
if (response.variety === 'view') { | ||
if(!response.source.context){ | ||
response.source.context = {}; | ||
} | ||
// append tags from document request to JSON request | ||
@@ -164,10 +165,4 @@ if(request.query.tags){ | ||
var urlObj = Url.parse( url ); | ||
if( urlObj.query !== null){ | ||
var qsObj = Querystring.parse( urlObj.query ); | ||
qsObj[qsName] = qsValue; | ||
urlObj.query = Querystring.stringify( qsObj ); | ||
}else{ | ||
urlObj.query = Querystring.parse(qsName + '=' + qsValue); | ||
} | ||
urlObj.query = Querystring.parse(qsName + '=' + qsValue); | ||
return urlObj.format( urlObj ); | ||
} |
@@ -46,5 +46,5 @@ /* | ||
info.build = function( options ){ | ||
var out = (options && options.info)? Hoek.applyToDefaults(info.defaults, options.info) : info.defaults; | ||
var out = (options.info)? Hoek.applyToDefaults(info.defaults, options.info) : info.defaults; | ||
Joi.assert(out, info.schema); | ||
return out; | ||
} |
470
lib/paths.js
@@ -13,2 +13,3 @@ /* | ||
Definitions = require('../lib/definitions'), | ||
Properties = require('../lib/properties'), | ||
Utilities = require('../lib/utilities'); | ||
@@ -19,3 +20,10 @@ | ||
/** | ||
* build the swagger path section | ||
* | ||
* @param {Object} setting | ||
* @param {Object} routes | ||
* @return {Object} | ||
*/ | ||
paths.build = function( settings, routes ){ | ||
@@ -25,11 +33,12 @@ | ||
// loop each route | ||
routes.forEach(function (route) { | ||
// only include routes tagged with "api" | ||
if (!route.settings.tags || route.settings.tags.indexOf('api') < 0) return; | ||
// console.log(route.path,route.method) | ||
if (!route.settings.tags || route.settings.tags.indexOf('api') < 0){ | ||
return; | ||
} | ||
var routeOptions = route.settings.plugins ? route.settings.plugins['hapi-swagger'] : {}; | ||
var routeData = { | ||
group: route.group, | ||
path: route.path, | ||
@@ -40,13 +49,35 @@ method: route.method.toUpperCase(), | ||
authorizations: {}, | ||
tags: route.settings.tags, | ||
queryParams: route.settings.validate && route.settings.validate.query, | ||
pathParams: route.settings.validate && route.settings.validate.params, | ||
payloadParams: route.settings.validate && route.settings.validate.payload, | ||
responseSchema: route.settings.response && route.settings.response.schema, | ||
headerParams: route.settings.validate && route.settings.validate.headers, | ||
responses: routeOptions && routeOptions.responses || [], | ||
tags: Hoek.reach(route,'settings.tags'), | ||
queryParams: Hoek.reach(route,'settings.validate.query'), | ||
pathParams: Hoek.reach(route,'settings.validate.params'), | ||
payloadParams: Hoek.reach(route,'settings.validate.payload'), | ||
responseSchema: Hoek.reach(route,'settings.response.schema'), | ||
headerParams: Hoek.reach(route,'settings.validate.headers'), | ||
responses: routeOptions && routeOptions.responses || {}, | ||
nickname: routeOptions && routeOptions.nickname || null, | ||
payloadType: routeOptions && routeOptions.payloadType || null | ||
payloadType: routeOptions && routeOptions.payloadType || null, | ||
groups: route.group | ||
}; | ||
// user configured interface through route plugin options | ||
if( Hoek.reach(routeOptions, 'validate.query') ){ | ||
routeData.queryParams = Properties.objectToArray( Hoek.reach(routeOptions, 'validate.query') ); | ||
} | ||
if( Hoek.reach(routeOptions,'validate.params') ){ | ||
routeData.pathParams = Properties.objectToArray( Hoek.reach(routeOptions,'validate.params') ); | ||
} | ||
if( Hoek.reach(routeOptions, 'validate.headers') ){ | ||
routeData.headerParams = Properties.objectToArray( Hoek.reach(routeOptions, 'validate.headers') ); | ||
} | ||
if( Hoek.reach(routeOptions, 'validate.payload') ){ | ||
// has different structure, just pass straight through | ||
routeData.payloadParams = Hoek.reach(routeOptions, 'validate.payload' ); | ||
// if its a native javascript object convert it to JOI | ||
if (!routeData.payloadParams.isJoi){ | ||
routeData.payloadParams = Joi.object(routeData.payloadParams); | ||
} | ||
} | ||
if(route.settings.auth && settings.authorizations) { | ||
@@ -85,7 +116,13 @@ route.settings.auth.strategies.forEach(function(strategie) { | ||
return paths.properties( settings, routesData); | ||
} | ||
/** | ||
* build the swagger path section | ||
* | ||
* @param {Object} setting | ||
* @param {Object} routes | ||
* @return {Object} | ||
*/ | ||
paths.properties = function( settings, routes ){ | ||
@@ -103,3 +140,2 @@ | ||
out = { | ||
"tags": route.group || [], | ||
"summary": route.description, | ||
@@ -111,17 +147,15 @@ "security": Utilities.hasProperties(route.authorizations)? [route.authorizations] : [], | ||
}; | ||
// tags in swagger are used for grouping | ||
if(route.groups){ | ||
out.tags = route.groups; | ||
} | ||
out.description = Array.isArray(route.notes) ? route.notes.join('<br/><br/>') : route.notes; | ||
out.responses = (route.responses)? route.responses: {"200": {"description": "Successful"}}; | ||
out.responses = Utilities.hasProperties(route.responses)? route.responses : {"200": {"description": "Successful"}}; | ||
var pathParam = internals.getParams(route, 'pathParams') | ||
var queryParam = internals.getParams(route, 'queryParams') | ||
var headerParam = internals.getParams(route, 'headerParams') | ||
var payloadParameters; | ||
// build up swagger properties for route validation | ||
var pathProperties = internals.validatorsToProperties(pathParam, swagger.definitions); | ||
var queryProperties = internals.validatorsToProperties(queryParam, swagger.definitions); | ||
var headerProperties = internals.validatorsToProperties(headerParam, swagger.definitions); | ||
var payloadApiParams; | ||
// set globally or locally to route | ||
// set from plugin options or from route options | ||
var payloadType = settings.payloadType | ||
@@ -132,54 +166,46 @@ if(route.payloadType){ | ||
// build payload either with JSON or form input | ||
if (payloadType && payloadType.toLowerCase() === 'json') { | ||
// set as json | ||
var payloadProperty = internals.validatorToProperty(out.nickname, route.payloadParams, swagger.definitions); | ||
if (payloadProperty && payloadProperty.type !== 'void') { | ||
payloadProperty.required = true; | ||
payloadParameters = Properties.parseProperty(null, route.payloadParams, swagger.definitions); | ||
var definitionName = internals.getJOIClassName(route.payloadParams); | ||
//console.log(JSON.stringify(payloadParameters)) | ||
// override inline structure and use defination object | ||
if(payloadParameters && payloadParameters.type !== 'void'){ | ||
payloadParameters.in = 'body' | ||
payloadParameters.name = 'body' | ||
payloadParameters.schema = {'$ref': '#/definitions/' + Definitions.appendDefinition( | ||
definitionName, | ||
internals.getJOIObj(route, 'payloadParams'), | ||
swagger.definitions, | ||
out.operationId + '_payload' | ||
)} | ||
delete payloadParameters.properties | ||
// set to JSON | ||
out.consumes = settings.consumes || ['application/json']; | ||
} | ||
// build single item with in: body | ||
var payloadParam = internals.getParams(route, 'payloadParams') | ||
var payloadProperties = internals.validatorsToProperties(payloadParam, swagger.definitions); | ||
var x = internals.propertiesToAPIParams(payloadProperties, null); | ||
//console.log( JSON.stringify( payloadParam ) ) | ||
if(payloadParam !== null){ | ||
payloadApiParams = { | ||
"in": "body", | ||
"name": "body", | ||
"description": "order placed for purchasing the pet", | ||
"required": true, | ||
"schema": Definitions.build( x ) | ||
} | ||
} | ||
// payloadApiParams = internals.propertiesToAPIParams({ | ||
// body: payloadProperty | ||
// }, 'body'); | ||
} else { | ||
// set as form | ||
var payloadParam = internals.getParams(route, 'payloadParams') | ||
var payloadProperties = internals.validatorsToProperties(payloadParam, swagger.definitions); | ||
payloadApiParams = internals.propertiesToAPIParams(payloadProperties, 'formData'); | ||
payloadParameters = Properties.joiToSwaggerParameters( internals.getJOIObj(route, 'payloadParams'), 'formData', swagger.definitions ); | ||
} | ||
// add the path, query and body parameters | ||
out.parameters = out.parameters.concat( | ||
internals.propertiesToAPIParams(headerProperties, 'header'), | ||
internals.propertiesToAPIParams(pathProperties, 'path'), | ||
internals.propertiesToAPIParams(queryProperties, 'query') | ||
); | ||
if(payloadApiParams){ | ||
out.parameters = out.parameters.concat( payloadApiParams ) | ||
out.parameters = out.parameters.concat( | ||
Properties.joiToSwaggerParameters( internals.getJOIObj(route, 'headerParams'), 'header', swagger.definitions ), | ||
Properties.joiToSwaggerParameters( internals.getJOIObj(route, 'pathParams'), 'path', swagger.definitions ), | ||
Properties.joiToSwaggerParameters( internals.getJOIObj(route, 'queryParams'), 'query', swagger.definitions ) | ||
); | ||
if(payloadParameters){ | ||
out.parameters = out.parameters.concat( payloadParameters ) | ||
} | ||
// set response type and definition | ||
@@ -189,10 +215,15 @@ // If the responseSchema is a joi object, response className can be set as an option: | ||
var responseClassName = internals._getClassName(route.responseSchema), | ||
altClassName = out.nickname + '_' + route.method + '_response'; | ||
var responseClassName = internals.getJOIClassName(route.responseSchema); | ||
if(responseClassName){ | ||
if(!out.responses['200']){ | ||
out.responses['200'] = {"description": "Successful"} | ||
} | ||
out.responses['200'].schema = { | ||
"$ref": "#/definitions/" + Definitions.appendDefinition( responseClassName, route.responseSchema, swagger.definitions) | ||
} | ||
} | ||
responseClassName = responseClassName || altClassName; | ||
var responseProperty = internals.validatorToProperty( | ||
var responseProperty = Properties.parseProperty( | ||
responseClassName, | ||
internals.getParams(route, 'responseSchema'), | ||
internals.getJOIObj(route, 'responseSchema'), | ||
swagger.definitions, | ||
@@ -212,2 +243,3 @@ null | ||
} | ||
@@ -229,232 +261,31 @@ if(!pathObj[path]){ | ||
// gets the pramas from route object | ||
internals.getParams = function (route, name) { | ||
var prama = route[name]; | ||
if (route[name] && route[name].isJoi) { | ||
if (route[name]._inner.children) { | ||
prama = route[name]._inner.children; | ||
} else { | ||
// fix for responseObject array types | ||
if (route[name]._type = 'array') { | ||
prama = route[name]; | ||
} | ||
} | ||
} | ||
return prama; | ||
} | ||
// convert an object of properties to an api parameter array | ||
internals.propertiesToAPIParams = function (properties, type) { | ||
if (properties === null || | ||
properties === undefined || | ||
(typeof properties !== 'object')) { | ||
return []; | ||
} | ||
var params = []; | ||
var keys = Object.keys(properties); | ||
for (var i = 0, il = keys.length; i < il; ++i) { | ||
var key = keys[i]; | ||
var param = properties[key]; | ||
if (!param) { | ||
continue; | ||
} | ||
param.name = key; | ||
if(type){ | ||
param.in = type; | ||
if (param.type === "array") { | ||
param.allowMultiple = true; | ||
} | ||
} | ||
params.push(param); | ||
} | ||
return params; | ||
}; | ||
// convert an object of Joi validators into an object of swagger schema properties | ||
internals.validatorsToProperties = function (params, definitions, requiredArray) { | ||
var i, | ||
x, | ||
key, | ||
param, | ||
properties = {}; | ||
if (params === null || | ||
params === undefined || | ||
(typeof params !== 'object')) { | ||
return []; | ||
} | ||
if (params.isJoi && params._inner.children) { | ||
params = params._inner.children | ||
} | ||
// gets the JOI object from route object | ||
internals.getJOIObj = function (route, name) { | ||
var prama = route[name]; | ||
if (Array.isArray(params)) { | ||
i = params.length, | ||
x = 0; | ||
while (x < i) { | ||
key = params[x].key; | ||
param = params[x].schema; | ||
properties[key] = internals.validatorToProperty(key, param, definitions, requiredArray); | ||
x++; | ||
if (route[name] && route[name].isJoi) { | ||
if (route[name]._inner.children) { | ||
prama = route[name]._inner.children; | ||
} else { | ||
// for array types | ||
if (route[name]._type = 'array') { | ||
prama = route[name]; | ||
} | ||
} | ||
} | ||
return properties; | ||
}; | ||
internals._getClassName = function(schema) { | ||
if(schema && schema._meta && Array.isArray(schema._meta)){ | ||
var i = schema._meta.length; | ||
while (i--) { | ||
if(schema._meta[i].className){ | ||
return schema._meta[i].className | ||
} | ||
} | ||
} | ||
return undefined; | ||
return prama; | ||
} | ||
// decode a Joi validator into swagger schema property | ||
internals.validatorToProperty = function (name, param, definitions, requiredArray) { | ||
if (param === null || | ||
param === undefined) { | ||
return undefined; | ||
} | ||
// removes forbidden properties | ||
if (param._flags | ||
&& param._flags.presence | ||
&& param._flags.presence === 'forbidden'){ | ||
return undefined; | ||
} | ||
var property = { | ||
type: 'void' | ||
}; | ||
// create a definition and return that | ||
if (typeof param.validate !== 'function') { | ||
property.type = internals.validatorsToDefinitionName(name, param, definitions); | ||
return property; | ||
} | ||
if (param.describe) { | ||
var describe = param.describe(); | ||
property.type = param._type.toLowerCase(); | ||
property.description = typeof param._description === 'string' ? param._description : undefined; | ||
property.notes = typeof param._notes !== 'function' && param._notes.length ? param._notes : undefined; | ||
property.tags = typeof param._tags !== 'function' && param._tags.length ? param._tags : undefined; | ||
//property.defaultValue = (describe.flags) ? describe.flags.default : null; | ||
if (param._flags && param._flags.presence) { | ||
property.required = (param._flags.presence === 'required') ? true : false; | ||
} | ||
// add enum values if not only undefined or null | ||
if (Array.isArray(describe.valids) && describe.valids.length) { | ||
var enums = describe.valids.filter(function (v) { | ||
return v !== undefined && v !== ''; | ||
}); | ||
if (enums.length) { | ||
property["enum"] = enums; | ||
} | ||
} | ||
if (property.type === 'number') { | ||
property.minimum = internals.getArgByName(describe.rules, 'min'); | ||
property.maximum = internals.getArgByName(describe.rules, 'max'); | ||
if (internals.existsByName(describe.rules, 'integer')) { | ||
property.type = 'integer'; | ||
} | ||
} | ||
// if (property.type === 'object' && param._inner) { | ||
// var className = internals._getClassName(param); | ||
// var param = (param._inner.children) ? param._inner.children : param._inner | ||
// property.type = internals.validatorsToDefinitionName( | ||
// className || name || property.description, | ||
// param, | ||
// definitions); | ||
// } | ||
if (property.type === 'array') { | ||
property.minItems = internals.getArgByName(describe.rules, 'min'); | ||
property.maxItems = internals.getArgByName(describe.rules, 'max'); | ||
var arrayTypes = param._inner ? param._inner.inclusions : internals.getArgByName(describe.rules, 'includes'); | ||
// swagger appears to only support one array type at a time, so lets grab the first one | ||
var firstInclusionType = internals.first(arrayTypes); | ||
if (firstInclusionType) { | ||
// get className of embeded array | ||
if(name === 'items' | ||
&& Hoek.reach(param, '_inner.inclusions.0._meta') | ||
&& Array.isArray(param._inner.inclusions[0]._meta)){ | ||
var meta = param._inner.inclusions[0]._meta, | ||
i = meta.length; | ||
while (i--) { | ||
if(meta[i].className){ | ||
name = meta[i].className | ||
} | ||
} | ||
} | ||
var arrayProperty = internals.validatorToProperty(name, firstInclusionType, definitions); | ||
if (arrayProperty['enum']) { | ||
property.items = { | ||
'type': arrayProperty.type, | ||
'enum': arrayProperty['enum'] | ||
}; | ||
} else { | ||
if(arrayProperty.type === 'string'){ | ||
property.items = { | ||
'type': arrayProperty.type | ||
}; | ||
}else{ | ||
property.items = { | ||
'$ref': '#/definitions/' + name | ||
}; | ||
} | ||
} | ||
} | ||
} | ||
if (property.type === 'any') { | ||
var i = param._meta.length; | ||
while (i--) { | ||
if(param._meta[i].swaggerType | ||
&& param._meta[i].swaggerType === 'file'){ | ||
property.type = "file"; | ||
property.in = "body"; | ||
} | ||
} | ||
} | ||
} | ||
// if a required array is present use that for required fields instead of a flag | ||
if (requiredArray) { | ||
if (property.required) { | ||
requiredArray.push(name); | ||
} | ||
delete property.required; | ||
} | ||
return property; | ||
}; | ||
// get arg value of an item in arrays of structure | ||
@@ -494,53 +325,20 @@ // [ { name: 'name', arg: 'arg' } ] | ||
// create a definition from an object of Joi validators. Return the definition name | ||
internals.validatorsToDefinitionName = function (name, params, definitions) { | ||
// if no name create a signature | ||
if (!name) { | ||
name = 'definition_' + ShortId.generate(); | ||
} | ||
// need to either create new object or comparision | ||
var definition = internals.createDefinition(name, params, definitions); | ||
// find existing definition by this name | ||
var foundDefinition = definitions[name]; | ||
if (foundDefinition) { | ||
// deep compare object | ||
if(Hoek.deepEqual(foundDefinition, definition)){ | ||
// return existing id | ||
return foundDefinition.id; | ||
}else{ | ||
// create new definition with alt name, to stop reuse of definition | ||
definition.id = 'definition_' + ShortId.generate(); | ||
definitions[definition.id] = definition; | ||
/** | ||
* Given a JOI schema get its className | ||
* | ||
* @param {Object} joiObj | ||
* @return {String || undefined} | ||
*/ | ||
internals.getJOIClassName = function(joiObj) { | ||
if(joiObj && joiObj._meta && Array.isArray(joiObj._meta)){ | ||
var i = joiObj._meta.length; | ||
while (i--) { | ||
if(joiObj._meta[i].className){ | ||
return joiObj._meta[i].className | ||
} | ||
} | ||
}else{ | ||
// create new definition | ||
definitions[name] = definition; | ||
} | ||
return definition.id; | ||
}; | ||
// creates a new definition | ||
internals.createDefinition = function (name, params, definitions) { | ||
//console.log(JSON.stringify(internals.validatorsToProperties(params, definitions))) | ||
var payloadProperties = internals.validatorsToProperties(params, definitions); | ||
var x = internals.propertiesToAPIParams(payloadProperties, null); | ||
// return { | ||
// "type": "object", | ||
// "properties": internals.validatorsToProperties(params, definitions) | ||
// }; | ||
return Definitions.build( x ); | ||
return undefined; | ||
} | ||
internals.first = function first(array) { | ||
return array ? array[0] : undefined; | ||
}; |
@@ -15,3 +15,3 @@ /* | ||
* @return {Boolean} | ||
*/ | ||
*/ | ||
utilities.hasProperties = function( obj ) { | ||
@@ -25,2 +25,20 @@ var key; | ||
return false; | ||
} | ||
utilities.deleteEmptyProperties = function( obj ) { | ||
var key; | ||
for(key in obj) { | ||
if( obj.hasOwnProperty( key )) { | ||
// delete properties undefined values | ||
if(obj[key] === undefined || obj[key] === null){ | ||
delete obj[key]; | ||
} | ||
// delete array with no values | ||
if(Array.isArray(obj[key]) && obj[key].length === 0){ | ||
delete obj[key]; | ||
} | ||
} | ||
} | ||
return obj; | ||
} |
{ | ||
"name": "hapi-swagger", | ||
"description": "A swagger documentation UI generator plugin for hapi", | ||
"version": "3.0.0-rc1", | ||
"version": "3.0.0-rc2", | ||
"author": "Glenn Jones", | ||
@@ -33,8 +33,10 @@ "repository": { | ||
"code": "1.5.0", | ||
"mocha": "^1.17.1", | ||
"chai": "^1.9.2", | ||
"hapi": "^10.0.0" | ||
"hapi": "^10.0.0", | ||
"wreck": "6.3.0", | ||
"h2o2": "4.0.1" | ||
}, | ||
"scripts": { | ||
"test": "lab" | ||
"start": "node ./bin/test-server", | ||
"test": "lab -a code", | ||
"test-cov-html": "lab -a code -r html -o coverage.html" | ||
}, | ||
@@ -41,0 +43,0 @@ "peerDependencies": { |
@@ -48,29 +48,30 @@ var Lab = require('lab'), | ||
var schema = { | ||
'type': 'object', | ||
'properties': { | ||
'a': { | ||
'description': 'the first number', | ||
'type': 'number' | ||
}, | ||
'b': { | ||
'description': 'the second number', | ||
'type': 'number' | ||
}, | ||
'operator': { | ||
'description': 'the opertator i.e. + - / or *', | ||
'type': 'string' | ||
}, | ||
'equals': { | ||
'description': 'the result of the sum', | ||
'type': 'number' | ||
} | ||
var defination = { | ||
"properties": { | ||
"a": { | ||
"description": "the first number", | ||
"type": "number" | ||
}, | ||
'required': [ | ||
'a', | ||
'b', | ||
'operator', | ||
'equals' | ||
] | ||
} | ||
"b": { | ||
"description": "the second number", | ||
"type": "number" | ||
}, | ||
"operator": { | ||
"description": "the opertator i.e. + - / or *", | ||
"default": "+", | ||
"type": "string" | ||
}, | ||
"equals": { | ||
"description": "the result of the sum", | ||
"type": "number" | ||
} | ||
}, | ||
"required": [ | ||
"a", | ||
"b", | ||
"operator", | ||
"equals" | ||
], | ||
"type": "object" | ||
} | ||
@@ -80,3 +81,6 @@ server.inject({method: 'GET', url: '/swagger.json'}, function(response) { | ||
expect(response.statusCode).to.equal(200); | ||
expect(response.result.paths['/test/'].post.parameters[0].schema).to.deep.equal(schema); | ||
expect(response.result.paths['/test/'].post.parameters[0].schema).to.deep.equal({ | ||
"$ref": "#/definitions/test_payload" | ||
}); | ||
expect(response.result.definitions.test_payload).to.deep.equal(defination); | ||
done(); | ||
@@ -83,0 +87,0 @@ }); |
@@ -39,2 +39,9 @@ var Lab = require('lab'), | ||
} | ||
},{ | ||
method: 'GET', | ||
path: '/movies/movie/actors', | ||
handler: Helper.defaultHandler, | ||
config: { | ||
tags: ['api','d'] | ||
} | ||
}]; | ||
@@ -59,2 +66,18 @@ | ||
lab.test('filter by tags=a', function (done) { | ||
Helper.createServer( {}, routes, function(err, server){ | ||
expect(err).to.equal(null); | ||
server.inject({method: 'GET', url: '/swagger.json?tags=a,b,c,d'}, function(response) { | ||
//console.log(JSON.stringify(response.result.paths)); | ||
expect(response.statusCode).to.equal(200); | ||
expect(countProperties(response.result.paths)).to.equal(4); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
lab.test('filter by tags=a,c', function (done) { | ||
@@ -101,3 +124,3 @@ | ||
expect(response.statusCode).to.equal(200); | ||
expect(countProperties(response.result.paths)).to.equal(2); | ||
expect(countProperties(response.result.paths)).to.equal(0); | ||
done(); | ||
@@ -109,5 +132,4 @@ }); | ||
lab.test('filter by tags=api,+a-b', function (done) { | ||
lab.test('filter by tags=x', function (done) { | ||
@@ -118,6 +140,6 @@ Helper.createServer( {}, routes, function(err, server){ | ||
// note %2B is a '+' plus char url encoded | ||
server.inject({method: 'GET', url: '/swagger.json?tags=api,%2Ba,-b'}, function(response) { | ||
server.inject({method: 'GET', url: '/swagger.json?tags=x'}, function(response) { | ||
//console.log(JSON.stringify(response.result.paths)); | ||
expect(response.statusCode).to.equal(200); | ||
expect(countProperties(response.result.paths)).to.equal(2); | ||
expect(countProperties(response.result.paths)).to.equal(0); | ||
done(); | ||
@@ -129,4 +151,3 @@ }); | ||
@@ -133,0 +154,0 @@ }); |
@@ -49,3 +49,3 @@ var Lab = require('lab'), | ||
server.inject({method: 'GET', url: '/swagger.json'}, function(response) { | ||
//console.log(JSON.stringify(response.result.paths)); | ||
expect(response.statusCode).to.equal(200); | ||
@@ -52,0 +52,0 @@ expect(response.result.paths['/actors'].get.tags[0]).to.equal('actors'); |
var Hapi = require('hapi'), | ||
Inert = require('inert'), | ||
Vision = require('vision'), | ||
H2o2 = require('h2o2'), | ||
HapiSwagger = require('../lib/index.js'); | ||
@@ -23,3 +24,4 @@ | ||
Inert, | ||
Vision, | ||
Vision, | ||
H2o2, | ||
{ | ||
@@ -35,5 +37,3 @@ register: HapiSwagger, | ||
}); | ||
}); | ||
server.route(routes); | ||
@@ -40,0 +40,0 @@ callback(err, server); |
@@ -1,2 +0,6 @@ | ||
var Lab = require('lab'), | ||
var Hapi = require('hapi'), | ||
Inert = require('inert'), | ||
Vision = require('vision'), | ||
HapiSwagger = require('../lib/index.js'), | ||
Lab = require('lab'), | ||
Code = require('code'), | ||
@@ -29,2 +33,66 @@ Joi = require('joi'), | ||
}]; | ||
lab.test('plug-in register no vision dependency', function (done) { | ||
try{ | ||
var server = new Hapi.Server(); | ||
server.connection(); | ||
server.register([ | ||
Inert, | ||
HapiSwagger | ||
], function(err){ | ||
server.start(function(err){ | ||
}); | ||
}); | ||
server.route(routes); | ||
}catch(err){ | ||
expect(err.message).to.equal('Missing vision plug-in registation'); | ||
done(); | ||
} | ||
}); | ||
lab.test('plug-in register no inert dependency', function (done) { | ||
try{ | ||
var server = new Hapi.Server(); | ||
server.connection(); | ||
server.register([ | ||
Vision, | ||
HapiSwagger | ||
], function(err){ | ||
server.start(function(err){ | ||
}); | ||
}); | ||
server.route(routes); | ||
}catch(err){ | ||
expect(err.message).to.equal('Missing inert plug-in registation'); | ||
done(); | ||
} | ||
}); | ||
lab.test('plug-in register no options', function (done) { | ||
var server = new Hapi.Server(); | ||
server.connection(); | ||
server.register([ | ||
Inert, | ||
Vision, | ||
HapiSwagger | ||
], function(err){ | ||
server.start(function(err){ | ||
expect(err).to.equal(undefined); | ||
done(); | ||
}); | ||
}); | ||
server.route(routes); | ||
}); | ||
@@ -193,5 +261,3 @@ | ||
lab.test('expanded none', function (done) { | ||
// TODO find a way to test impact of property change | ||
Helper.createServer( {'expanded': 'none'}, routes, function(err, server){ | ||
@@ -207,4 +273,3 @@ expect(err).to.equal(null); | ||
lab.test('expanded list', function (done) { | ||
lab.test('expanded list', function (done) { | ||
Helper.createServer( {'expanded': 'list'}, routes, function(err, server){ | ||
@@ -220,4 +285,3 @@ expect(err).to.equal(null); | ||
lab.test('expanded full', function (done) { | ||
lab.test('expanded full', function (done) { | ||
Helper.createServer( {'expanded': 'full'}, routes, function(err, server){ | ||
@@ -229,6 +293,21 @@ expect(err).to.equal(null); | ||
}); | ||
}); | ||
}); | ||
lab.test('pass through of tags querystring', function (done) { | ||
Helper.createServer( {}, routes, function(err, server){ | ||
expect(err).to.equal(null); | ||
server.inject({method: 'GET', url: '/documentation?tags=reduced'}, function(response) { | ||
expect(response.statusCode).to.equal(200); | ||
expect(response.result.indexOf('swagger.json?tags=reduced') > -1).to.equal(true); | ||
done(); | ||
}); | ||
}); | ||
}); | ||
@@ -235,0 +314,0 @@ }); |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
Major refactor
Supply chain riskPackage has recently undergone a major refactor. It may be unstable or indicate significant internal changes. Use caution when updating to versions that include significant changes.
Found 1 instance in 1 package
4563269
82642
76
41945
11