Huge News!Announcing our $40M Series B led by Abstract Ventures.Learn More
Socket
Sign inDemoInstall
Socket

hapi-swagger

Package Overview
Dependencies
Maintainers
1
Versions
163
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

hapi-swagger - npm Package Compare versions

Comparing version 3.0.0-rc1 to 3.0.0-rc2

.eslintignore

377

bin/routes.js

@@ -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;
}

@@ -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

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc