baucis-swagger2
Advanced tools
Comparing version 0.1.2 to 0.1.3
38
Api.js
// __Dependencies__ | ||
var url = require('url'); | ||
var deco = require('deco'); | ||
var utils = require('./utils'); | ||
@@ -9,12 +8,2 @@ // __Private Module Members__ | ||
// A method for capitalizing the first letter of a string | ||
function capitalize (s) { | ||
if (!s) { | ||
return s; | ||
} | ||
if (s.length === 1) { | ||
return s.toUpperCase(); | ||
} | ||
return s[0].toUpperCase() + s.substring(1); | ||
} | ||
@@ -51,3 +40,3 @@ // Figure out the basePath for Swagger API definition | ||
name: controller.model().singular(), | ||
description: capitalize(controller.model().singular()) + ' resource.', | ||
description: utils.capitalize(controller.model().singular()) + ' resource.', | ||
'x-resource': true //custom extension to state this tag represent a resource | ||
@@ -58,2 +47,3 @@ }); | ||
} | ||
/* TODO. Move general params to a unique point of definition accordingly to Swagger 2.0 | ||
function getReusableParameters() { | ||
@@ -65,11 +55,12 @@ return []; | ||
} | ||
*/ | ||
function buildPaths(controllers) { | ||
var paths = {}; | ||
controllers.forEach(function (controller) { | ||
var resourcePath = '/' + controller.model().plural(); | ||
controller.generateSwagger2(); | ||
for(var path in controller.swagger2.paths) { | ||
paths[path] = controller.swagger2.paths[path]; | ||
var collection = controller.swagger2.paths; | ||
for (var path in collection) { | ||
if (collection.hasOwnProperty(path)) { | ||
paths[path] = collection[path]; | ||
} | ||
} | ||
@@ -82,7 +73,8 @@ }); | ||
controllers.forEach(function (controller) { | ||
var resourcePath = '/' + controller.model().plural(); | ||
controller.generateSwagger2(); | ||
for(var def in controller.swagger2.definitions) { | ||
definitions[def] = controller.swagger2.definitions[def]; | ||
var collection = controller.swagger2.definitions; | ||
for (var def in collection) { | ||
if (collection.hasOwnProperty(def)) { | ||
definitions[def] = collection[def]; | ||
} | ||
} | ||
@@ -147,3 +139,3 @@ definitions.ErrorModel = generateErrorModelDefinition(); | ||
// __Module Definition__ | ||
var decorator = module.exports = function (options, protect) { | ||
module.exports = function (options, protect) { | ||
var api = this; | ||
@@ -150,0 +142,0 @@ |
@@ -5,18 +5,8 @@ // This is a Controller mixin to add methods for generating Swagger data. | ||
var mongoose = require('mongoose'); | ||
var utils = require('./utils'); | ||
// __Private Members__ | ||
// A method for capitalizing the first letter of a string | ||
function capitalize (s) { | ||
if (!s) { | ||
return s; | ||
} | ||
if (s.length === 1) { | ||
return s.toUpperCase(); | ||
} | ||
return s[0].toUpperCase() + s.substring(1); | ||
} | ||
// __Module Definition__ | ||
var decorator = module.exports = function () { | ||
module.exports = function () { | ||
var controller = this; | ||
@@ -26,9 +16,4 @@ | ||
// Generate parameter list for operations | ||
function generateParameters(isInstance, verb) { | ||
var parameters = []; | ||
// Parameters available for singular routes | ||
if (isInstance) { | ||
parameters.push({ | ||
function getParamId() { | ||
return { | ||
name: 'id', | ||
@@ -39,5 +24,6 @@ in: 'path', | ||
required: true | ||
}); | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamXBaucisUpdateOperator() { | ||
return { | ||
name: 'X-Baucis-Update-Operator', | ||
@@ -48,8 +34,6 @@ in: 'header', | ||
required: false | ||
}); | ||
} | ||
// Parameters available for plural routes | ||
if (!isInstance) { | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamSkip() { | ||
return { | ||
name: 'skip', | ||
@@ -61,5 +45,6 @@ in: 'query', | ||
required: false | ||
}); | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamLimit() { | ||
return { | ||
name: 'limit', | ||
@@ -71,5 +56,6 @@ in: 'query', | ||
required: false | ||
}); | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamCount() { | ||
return { | ||
name: 'count', | ||
@@ -80,5 +66,6 @@ in: 'query', | ||
required: false | ||
}); | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamConditions() { | ||
return { | ||
name: 'conditions', | ||
@@ -89,5 +76,6 @@ in: 'query', | ||
required: false | ||
}); | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamSort() { | ||
return { | ||
name: 'sort', | ||
@@ -98,7 +86,6 @@ in: 'query', | ||
required: false | ||
}); | ||
} | ||
// Parameters available for singular and plural routes | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamSelect() { | ||
return { | ||
name: 'select', | ||
@@ -109,5 +96,6 @@ in: 'query', | ||
required: false | ||
}); | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamPopulate() { | ||
return { | ||
name: 'populate', | ||
@@ -118,57 +106,48 @@ in: 'query', | ||
required: false | ||
}); | ||
if (verb === 'post') { | ||
// TODO post body can be single or array | ||
parameters.push({ | ||
}; | ||
} | ||
function getParamDocument(isPost) { | ||
// TODO post body can be single or array | ||
return { | ||
name: 'document', | ||
in: 'body', | ||
description: 'Create a document by sending the paths to be updated in the request body.', | ||
description: (isPost) ? | ||
'Create a document by sending the paths to be updated in the request body.' : | ||
'Update a document by sending the paths to be updated in the request body.', | ||
schema: { | ||
$ref: '#/definitions/' + capitalize(controller.model().singular()), | ||
$ref: '#/definitions/' + utils.capitalize(controller.model().singular()), | ||
}, | ||
required: true | ||
}); | ||
}; | ||
} | ||
// Generate parameter list for operations | ||
function generateParameters(isInstance, verb) { | ||
var parameters = []; | ||
// Parameters available for singular and plural routes | ||
parameters.push(getParamSelect(), | ||
getParamPopulate()); | ||
if (isInstance) { | ||
// Parameters available for singular routes | ||
parameters.push(getParamId(), | ||
getParamXBaucisUpdateOperator()); | ||
} | ||
else { | ||
// Parameters available for plural routes | ||
parameters.push(getParamSkip(), | ||
getParamLimit(), | ||
getParamCount(), | ||
getParamConditions(), | ||
getParamSort()); | ||
} | ||
if (verb === 'post') { | ||
parameters.push(getParamDocument(true)); | ||
} | ||
if (verb === 'put') { | ||
parameters.push({ | ||
name: 'document', | ||
in: 'body', | ||
description: 'Update a document by sending the paths to be updated in the request body.', | ||
schema: { | ||
$ref: '#/definitions/' + capitalize(controller.model().singular()), | ||
}, | ||
required: true | ||
}); | ||
parameters.push(getParamDocument(false)); | ||
} | ||
return parameters; | ||
} | ||
function buildSemanticLabel(verb) { | ||
if ("get"===verb || "head"===verb) { | ||
return "query"; | ||
} | ||
else if ("put"===verb || "patch"===verb) { | ||
return "modify"; | ||
} | ||
else if ("post"===verb) { | ||
return "create"; | ||
} | ||
else if ("delete"===verb) { | ||
return "delete"; | ||
} | ||
else if ("options"===verb) { | ||
return "info"; | ||
} | ||
else if ("trace"===verb) { | ||
return "trace"; | ||
} | ||
return null; | ||
} | ||
function buildTags(resourceName) { | ||
var res = [ | ||
resourceName ]; | ||
return res; | ||
return [ resourceName ]; | ||
} | ||
@@ -188,3 +167,3 @@ | ||
schema: { | ||
'$ref': '#/definitions/' + capitalize(resourceName) | ||
'$ref': '#/definitions/' + utils.capitalize(resourceName) | ||
} | ||
@@ -213,3 +192,3 @@ }; | ||
function buildSecurityFor(isInstance, verb, resourceName) { | ||
function buildSecurityFor() { | ||
var security = []; | ||
@@ -219,3 +198,8 @@ // TODO | ||
} | ||
function buildOperationInfo(res, operationId, summary, description) { | ||
res.operationId = operationId; | ||
res.summary = summary; | ||
res.description = description; | ||
return res; | ||
} | ||
function buildBaseOperation(mode, verb, resourceName, pluralName) { | ||
@@ -228,22 +212,22 @@ var isInstance = (mode === 'instance'); | ||
responses: buildResponsesFor(isInstance, verb, resourceName, pluralName), | ||
security: buildSecurityFor(isInstance, verb, resourceName), | ||
security: buildSecurityFor(), | ||
}; | ||
if (isInstance) { | ||
if ('get' === verb) { | ||
res.operationId = 'getById'; | ||
res.summary = 'Get a ' + resourceName + ' by its unique ID'; | ||
res.description = 'Retrieve a ' + resourceName + ' by its ID' + '.'; | ||
return res; | ||
return buildOperationInfo(res, | ||
'getById', | ||
'Get a ' + resourceName + ' by its unique ID', | ||
'Retrieve a ' + resourceName + ' by its ID' + '.'); | ||
} | ||
else if ('put' === verb) { | ||
res.operationId = 'update'; | ||
res.summary = 'Modify a ' + resourceName + ' by its unique ID'; | ||
res.description = 'Update an existing ' + resourceName + ' by its ID' + '.'; | ||
return res; | ||
return buildOperationInfo(res, | ||
'update', | ||
'Modify a ' + resourceName + ' by its unique ID', | ||
'Update an existing ' + resourceName + ' by its ID' + '.'); | ||
} | ||
else if ('delete' === verb) { | ||
res.operationId = 'deleteById'; | ||
res.summary = 'Delete a ' + resourceName + ' by its unique ID'; | ||
res.description = 'Deletes an existing ' + resourceName + ' by its ID' + '.'; | ||
return res; | ||
return buildOperationInfo(res, | ||
'deleteById', | ||
'Delete a ' + resourceName + ' by its unique ID', | ||
'Deletes an existing ' + resourceName + ' by its ID' + '.'); | ||
} | ||
@@ -253,18 +237,18 @@ } else { | ||
if ('get' === verb) { | ||
res.operationId = 'query'; | ||
res.summary = 'Query some ' + pluralName; | ||
res.description = 'Query over ' + pluralName + '.'; | ||
return res; | ||
return buildOperationInfo(res, | ||
'query', | ||
'Query some ' + pluralName, | ||
'Query over ' + pluralName + '.'); | ||
} | ||
else if ('post' === verb) { | ||
res.operationId = 'create'; | ||
res.summary = 'Create some ' + pluralName; | ||
res.description = 'Create one or more ' + pluralName + '.'; | ||
return res; | ||
return buildOperationInfo(res, | ||
'create', | ||
'Create some ' + pluralName, | ||
'Create one or more ' + pluralName + '.'); | ||
} | ||
else if ('delete' === verb) { | ||
res.operationId = 'deleteByQuery'; | ||
res.summary = 'Delete some ' + pluralName + ' by query'; | ||
res.description = 'Delete all ' + pluralName + ' matching the specified query.'; | ||
return res; | ||
return buildOperationInfo(res, | ||
'deleteByQuery', | ||
'Delete some ' + pluralName + ' by query', | ||
'Delete all ' + pluralName + ' matching the specified query.'); | ||
} | ||
@@ -275,5 +259,9 @@ } | ||
function buildOperation(mode, verb, resourceName, pluralName) { | ||
function buildOperation(containerPath, mode, verb) { | ||
var resourceName = controller.model().singular(); | ||
var pluralName = controller.model().plural(); | ||
var operation = buildBaseOperation(mode, verb, resourceName, pluralName); | ||
operation.tags = buildTags(resourceName); | ||
containerPath[verb] = operation; | ||
return operation; | ||
@@ -315,17 +303,11 @@ } | ||
} | ||
// A method used to generated a Swagger property for a model | ||
function generatePropertyDefinition (name, path, definitionName) { | ||
var property = {}; | ||
var schema = controller.model().schema; | ||
function skipProperty(name, path, controller) { | ||
var select = controller.select(); | ||
var type = path.options.type ? swagger20TypeFor(path.options.type) : 'string'; // virtuals don't have type | ||
var mode = (select && select.match(/(?:^|\s)[-]/g)) ? 'exclusive' : 'inclusive'; | ||
var exclusiveNamePattern = new RegExp('\\B-' + name + '\\b', 'gi'); | ||
var inclusiveNamePattern = new RegExp('(?:\\B[+]|\\b)' + name + '\\b', 'gi'); | ||
// Keep deselected paths private | ||
if (path.selected === false) { | ||
return; | ||
return true; | ||
} | ||
// TODO is _id always included unless explicitly excluded? | ||
@@ -335,9 +317,18 @@ | ||
if (select && mode === 'exclusive' && select.match(exclusiveNamePattern)) { | ||
return; | ||
return true; | ||
} | ||
// If the mode is inclusive but the name is not present, skip this one. | ||
if (select && mode === 'inclusive' && name !== '_id' && !select.match(inclusiveNamePattern)) { | ||
return true; | ||
} | ||
return false; | ||
} | ||
// A method used to generated a Swagger property for a model | ||
function generatePropertyDefinition(name, path, definitionName) { | ||
var property = {}; | ||
var type = path.options.type ? swagger20TypeFor(path.options.type) : 'string'; // virtuals don't have type | ||
if (skipProperty(name, path, controller)) { | ||
return; | ||
} | ||
// Configure the property | ||
@@ -349,3 +340,3 @@ if (path.options.type === mongoose.Schema.Types.ObjectId) { | ||
else if (path.options.ref) { | ||
property.$ref = '#/definitions/'+capitalize(path.options.ref); | ||
property.$ref = '#/definitions/' + utils.capitalize(path.options.ref); | ||
} | ||
@@ -358,3 +349,3 @@ } | ||
//2. reference | ||
$ref: '#/definitions/'+ definitionName + capitalize(name) | ||
$ref: '#/definitions/'+ definitionName + utils.capitalize(name) | ||
}; | ||
@@ -373,2 +364,3 @@ } | ||
/* | ||
// Set enum values if applicable | ||
@@ -378,3 +370,2 @@ if (path.enumValues && path.enumValues.length > 0) { | ||
} | ||
// Set allowable values range if min or max is present | ||
@@ -384,30 +375,24 @@ if (!isNaN(path.options.min) || !isNaN(path.options.max)) { | ||
} | ||
if (!isNaN(path.options.min)) { | ||
// TODO: property.allowableValues.min = path.options.min; | ||
} | ||
if (!isNaN(path.options.max)) { | ||
// TODO: property.allowableValues.max = path.options.max; | ||
} | ||
*/ | ||
if (!property.type && !property.$ref) { | ||
console.log('Warning: That field type is not yet supported in baucis Swagger definitions, using "string."'); | ||
console.log('Path name: %s.%s', capitalize(controller.model().singular()), name); | ||
console.log('Mongoose type: %s', path.options.type); | ||
warnInvalidType(name, path); | ||
property.type = 'string'; | ||
} | ||
return property; | ||
} | ||
// A method used to generate a Swagger model definition for a controller | ||
function generateModelDefinition (schema, definitionName) { | ||
var definition = {}; | ||
function warnInvalidType(name, path) { | ||
console.log('Warning: That field type is not yet supported in baucis Swagger definitions, using "string."'); | ||
console.log('Path name: %s.%s', utils.capitalize(controller.model().singular()), name); | ||
console.log('Mongoose type: %s', path.options.type); | ||
} | ||
definition.required = []; | ||
definition.properties = {}; | ||
Object.keys(schema.paths).forEach(function (name) { | ||
var path = schema.paths[name]; | ||
function mergePaths(definition, pathsCollection, definitionName) { | ||
Object.keys(pathsCollection).forEach(function (name) { | ||
var path = pathsCollection[name]; | ||
var property = generatePropertyDefinition(name, path, definitionName); | ||
@@ -419,22 +404,20 @@ definition.properties[name] = property; | ||
}); | ||
Object.keys(schema.virtuals).forEach(function (name) { | ||
var path = schema.virtuals[name]; | ||
var property = generatePropertyDefinition(name, path, definitionName); | ||
definition.properties[name] = property; | ||
if (path.options.required) { | ||
definition.required.push(name); | ||
} | ||
}); | ||
} | ||
// A method used to generate a Swagger model definition for a controller | ||
function generateModelDefinition (schema, definitionName) { | ||
var definition = { | ||
required: [], | ||
properties: {} | ||
}; | ||
mergePaths(definition, schema.paths, definitionName); | ||
mergePaths(definition, schema.virtuals, definitionName); | ||
return definition; | ||
} | ||
function addInnerModelDefinitions(defs, definitionName) { | ||
var schema = controller.model().schema; | ||
Object.keys(schema.paths).forEach(function (name) { | ||
var path = schema.paths[name]; | ||
function mergePathsForInnerDef(defs, collectionPaths, definitionName) { | ||
Object.keys(collectionPaths).forEach(function (name) { | ||
var path = collectionPaths[name]; | ||
if (path.schema) { | ||
var newdefinitionName = definitionName + capitalize(name); //<-- synthetic name (no info for this in input model) | ||
var newdefinitionName = definitionName + utils.capitalize(name); //<-- synthetic name (no info for this in input model) | ||
var def = generateModelDefinition(path.schema, newdefinitionName); | ||
@@ -444,11 +427,8 @@ defs[newdefinitionName] = def; | ||
}); | ||
} | ||
Object.keys(schema.virtuals).forEach(function (name) { | ||
var path = schema.virtuals[name]; | ||
if (path.schema) { | ||
var newdefinitionName = definitionName + capitalize(name); //<-- synthetic name (no info for this in input model) | ||
var def = generateModelDefinition(path.schema, newdefinitionName); | ||
defs[newdefinitionName] = def; | ||
} | ||
}); | ||
function addInnerModelDefinitions(defs, definitionName) { | ||
var schema = controller.model().schema; | ||
mergePathsForInnerDef(defs, schema.paths, definitionName); | ||
mergePathsForInnerDef(defs, schema.virtuals, definitionName); | ||
} | ||
@@ -462,3 +442,3 @@ | ||
var modelName = capitalize(controller.model().singular()); | ||
var modelName = utils.capitalize(controller.model().singular()); | ||
@@ -471,4 +451,3 @@ controller.swagger2 = { paths: {}, definitions: {} }; | ||
// Instance path | ||
var resourceName = controller.model().singular(); | ||
// Paths | ||
var pluralName = controller.model().plural(); | ||
@@ -480,12 +459,10 @@ | ||
var paths = {}; | ||
paths[instancePath] = { | ||
'get': buildOperation('instance', 'get', resourceName, pluralName), | ||
'put': buildOperation('instance', 'put', resourceName, pluralName), | ||
'delete': buildOperation('instance', 'delete', resourceName, pluralName) | ||
}; | ||
paths[collectionPath] = { | ||
'get': buildOperation('collection', 'get', resourceName, pluralName), | ||
'post': buildOperation('collection', 'post', resourceName, pluralName), | ||
'delete': buildOperation('collection', 'delete', resourceName, pluralName), | ||
}; | ||
paths[instancePath] = {}; | ||
paths[collectionPath] = {}; | ||
buildOperation(paths[instancePath], 'instance', 'get'); | ||
buildOperation(paths[instancePath], 'instance', 'put'); | ||
buildOperation(paths[instancePath], 'instance', 'delete'); | ||
buildOperation(paths[collectionPath], 'collection', 'get'); | ||
buildOperation(paths[collectionPath], 'collection', 'post'); | ||
buildOperation(paths[collectionPath], 'collection', 'delete'); | ||
controller.swagger2.paths = paths; | ||
@@ -492,0 +469,0 @@ |
{ | ||
"name": "baucis-swagger2", | ||
"version": "0.1.2", | ||
"version": "0.1.3", | ||
"description": "Generate customizable swagger version 2.0 definitions for your Baucis REST API.", | ||
@@ -16,3 +16,3 @@ "homepage": "https://github.com/icinetic/baucis-swagger2", | ||
"pretest": "npm install", | ||
"test": "mocha --bail --timeout 5000 -R spec" | ||
"test": "istanbul cover node_modules/mocha/bin/_mocha -- --bail --timeout 5000 -R spec" | ||
}, | ||
@@ -47,4 +47,6 @@ "author": { | ||
"baucis": "^1.1.1", | ||
"codeclimate-test-reporter": "0.0.4", | ||
"expect.js": "~0.3.1", | ||
"express": "~4.12.3", | ||
"istanbul": "^0.3.13", | ||
"mocha": "~2.2.4", | ||
@@ -51,0 +53,0 @@ "mongoose": "~3.8.23", |
@@ -9,2 +9,5 @@ baucis-swagger2 | ||
[![NPM](https://nodei.co/npm/baucis-swagger2.png?downloads=true&downloadRank=true&stars=true)](https://nodei.co/npm/baucis-swagger2/) | ||
This module generates customizable swagger 2.0 definitions for your Baucis API. | ||
@@ -11,0 +14,0 @@ Use this module in conjunction with [Baucis](https://github.com/wprl/baucis). |
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
63080
14
1370
113
9