This helps to create swagger documentation json which is based entirely on Swagger/OpenAPI specification (see here). The hook produces specification based upon OAS 3.0.
$ npm install sails-hook-swagger-generator --save
Just install the library and sails lift
, the moment you do, that's all check your ./swagger folder, it should be there and make sure this folder is already existing.
Check the ./swagger/swagger.json for sample generated swagger documentation json, then head to Swagger Editor.
By default, the Swagger Generator Sails Hook generates:
- Full automatic documentation for all Sails Blueprint routes;
- Documentation for all Sails
actions2
actions with routes configured in
config/routes.js
; and - Listing of all routes configured in
config/routes.js
(full details cannot be inferred for custom routes without additional information being provided - see below).
Documentation detail and customisation of most aspects of the generated Swagger can be achieved by:
- Top-level configuration to
config/swaggergenerator.js
. This provides direct JSON used as the template for the output Swagger/OpenAPI. - Adding objects with the key
swagger
to custom route configuration, controller files, action functions, model definitions and model attribute definitions. - Adding JSDoc (swagger-jsdoc)
@swagger
comments toconfig/routes.js
, controller/action files and model files.
Top-level Swagger/OpenAPI definitions for tags
and components
may be added in all swagger
objects
above and in all JSDoc @swagger
documentation comments. This enables the definition of top-level elements
in the location where they are logically defined/documented within the Sails application.
All content added to swagger
objects is merged into the generated output except those at the top-level with
keys that use an _
(underscore) prefix, which are processed specially
e.g. _tags
, _components
, _{actionName}
or _{blueprintAction}
.
See below for details.
It comes with some default settings which can be overridden by creating config/swaggergenerator.js
:
module.exports['swagger-generator'] = {
disabled: false,
swaggerJsonPath: './swagger/swagger.json',
swagger: {
openapi: '3.0.0',
info: {
title: 'Swagger Json',
description: 'This is a generated swagger json for your sails project',
termsOfService: 'http://example.com/terms',
contact: {name: 'Theophilus Omoregbee', url: 'http://github.com/theo4u', email: 'theo4u@ymail.com'},
license: {name: 'Apache 2.0', url: 'http://www.apache.org/licenses/LICENSE-2.0.html'},
version: '1.0.0'
},
servers: [
{ url: 'http://localhost:1337/' }
],
externalDocs: {url: 'http://theophilus.ziippii.com'}
},
defaults: {
responses: {
'200': { description: 'The requested resource' },
'404': { description: 'Resource not found' },
'500': { description: 'Internal server error' }
}
},
includeRoute: function(routeInfo) { return true; },
updateBlueprintActionTemplates: function(blueprintActionTemplates) { ... },
postProcess: function(specifications) { ... }
};
Notes on the use of configuration:
disabled
attribute is used to disable the module (e.g you may want to disable it on production).swaggerJsonPath
where to generate theswagger.json
file to; defaults tosails.config.appPath + '/swagger/swagger.json'
.swagger
object is template for the Swagger/OpenAPI output. It defaults to the minimal content above. Check Swagger/OpenAPI specification for more, in case you want to extend it. Generally, this hook provides sensible defaults for as much as possible but you may override them in this location or in any of the mechanisms explained below.defaults
object should contain theresponses
element; defaults to the above if not specified.includeRoute
function used to filter routes to be included in generated Swagger output; see advanced section below.updateBlueprintActionTemplates
allows customisation of the templates used to generate Swagger for blueprints; see advanced section below.postProcess
allows modification of the generated Swagger output before it is written to the output file; see advanced section below.
Documentation detail and customisation of most aspects of the generated Swagger for custom routes may be achieved by:
- Adding an object with the key
swagger
to individual route configurations inconfig/routes.js
. - Adding an object with the key
swagger
added to a controller file action function. - Adding an object with the key
swagger
to the exports of a controller file, standalone action file or actions2 file. - Adding JSDoc
@swagger
comments to route configurations inconfig/routes.js
; specifically:
- JSDoc
@swagger
documentation under the/{routePath}
and{httpMethod}
Swagger path for routes, or - JSDoc
@swagger
documentation undertags
andcomponents
paths for adding to the top-level Swagger/OpenAPI definitions.
- Adding JSDoc
@swagger
comments to Sails controller files, standalone action files or actions2 files; specifically:
- JSDoc
@swagger
documentation under the/{actionName}
path for the route, or - JSDoc
@swagger
documentation undertags
andcomponents
paths for adding to the top-level Swagger/OpenAPI definitions.
If you want to add extra configuration to a route, it can be done via the config/routes.js
, since Sails uses any of these two route patterns
(see full documentation here):
'http_method route': 'controller.action'
or'http_method route': { controller:'controller', action:'action' }
We can extend and add extra information on the second above for sails hook swagger generator to override
default properties (which is created from the first). Adding an object with a key swagger
or adding
JSDoc @swagger
commonents will do the trick.
For example, in config/routes.js
:
{
'post /user/login': {
controller: 'UserController',
action: 'login',
swagger: {
summary: 'Authentication',
description: 'This is for authentication of any user',
tags: [ 'Tag Name' ],
requestBody: {
content: {
'application/json': {
schema: {
properties: {
email: { type: 'string' },
password: { type: 'string', format: 'password' }
},
required: [ 'email', 'password' ],
}
}
}
},
parameters: [{
in: 'query',
name: 'firstName',
required: true,
schema: { type: 'string' },
description: 'This is a custom required parameter'
}],
responses: {
'200': {
description: 'The requested resource',
content: {
'application/json': {
schema: {
type: 'array',
items: { '$ref': '#/components/schemas/someDataType' },
},
},
},
},
'404': { description: 'Resource not found' },
'500': { description: 'Internal server error' }
}
}
}
}
Alternately, JSDoc @swagger
comments may be added for route configuration in config/routes.js
as follows:
- JSDoc
@swagger
documentation under the/{routePath}
and{httpMethod}
Swagger path for routes, or - JSDoc
@swagger
documentation undertags
andcomponents
paths for adding to the top-level Swagger/OpenAPI definitions.
For example, in config/routes.js
:
{
/** @swagger
* /clients/:client_id/user/:id:
* delete:
* summary: Client Op
* description: This is not really useful - example only :)
*/
'delete /clients/:client_id/user/:id': 'UserController.destroy',
}
Documentation detail and customisation of most aspects of the generated Swagger may be added to controller files, standalone action files or actions2 files as follows:
- Adding an object with the key
swagger
added to a controller file action function. - Adding an object with the key
swagger
to the exports of a controller file, standalone action file or actions2 file:- For controller files, actions are referenced by adding objects keyed on
_{actionName}
name. - For standalone action or actions2 files, placing content in the
swagger
object itself. - Adding documentation under
_tags
and_components
elements for adding to the top-level Swagger/OpenAPI definitions.
- For controller files, actions are referenced by adding objects keyed on
- Adding JSDoc
@swagger
comments to controller file, standalone action file or actions2 file:- JSDoc
@swagger
documentation under the/{actionName}
path for the route, or - JSDoc
@swagger
documentation undertags
andcomponents
paths for adding to the top-level Swagger/OpenAPI definitions.
- JSDoc
Note the use of the _
(underscore) prefix to avoid clashes with / distinguish them from general Swagger/OpenAPI elements.
For actions2 files:
- Inputs are parsed to generate parameter documentation.
- Exits are parsed to generate response documentation.
- Both may be customised/overridden by specifying parameters and/or responses in the
swagger
object in actions2 file.
For example, for a route configured as:
module.exports.routes = {
'/api/v1/auth/tokens': 'AuthController.tokens',
};
The tokens
action might be documented in a Controller api/controllers/AuthController.js
as follows:
function tokens(req, res) {
...
}
module.exports = {
tokens: tokens,
swagger: {
_tokens: {
tags: [ 'Auth' ],
description: 'Route description...'
},
_tags: [
{
name: 'Auth',
description: 'Module description ...',
}
],
}
};
Or, alternately:
function tokens(req, res) {
...
}
tokens.swagger = {
tags: [ 'Auth ],
description: 'Route description...'
}
module.exports = {
tokens: tokens,
swagger: {
_tags: [ ... ]
}
};
Or, alternately using JSDoc:
/**
* @swagger
*
* /tokens:
* description: Route description...
* tags:
* - Auth
* tags:
* - name: Auth
* description: Module description...
*/
function tokens(req, res) {
...
}
module.exports = {
tokens: tokens
};
Documentation detail and customisation of most aspects of the generated Swagger for blueprint routes may be achieved by:
- Adding an object with the key
swagger
to individual models e.g.api/models/modelName.js
:- Documenting the Swagger schema associated with the Sails mode by adding general content.
- Adding per-action documentation by adding objects keyed on
_{blueprintAction}
name. - Adding documentation under
_tags
and_components
elements for adding to the top-level Swagger/OpenAPI definitions.
- Adding specific fields to model attributes (supports
description
,externalDocs
andexample
). Note that applicable Sails attributes, automigrations and validations are also parsed. - JSDoc
@swagger
documentation under the/{globalId}
path for models. - JSDoc
@swagger
per-action documentation under the/{blueprintAction}
path for the model blueprint actions. - JSDoc
@swagger
documentation undertags
andcomponents
paths for adding to the top-level Swagger definitions.
Note the use of the _
(underscore) prefix to avoid clashes with / distinguish them from general Swagger/OpenAPI elements.
For example, in a model api/models/User.js
:
/**
* @swagger
*
* /User:
* tags:
* - Tag Name
* /findone:
* externalDocs:
* url: https://docs.com/here
*/
module.exports = {
attributes: {
uid: {
type: 'string',
example: '012345',
description: 'A unique identifier',
}
},
swagger: {
_create: { ... },
_tags: { ... }
}
};
Note that following parameters are added to the components/parameters
if they are not
provided in config/swaggergenerator.js
(expressed as OpenAPI references):
[
{ $ref: '#/components/parameters/WhereQueryParam' },
{ $ref: '#/components/parameters/LimitQueryParam' },
{ $ref: '#/components/parameters/SkipQueryParam' },
{ $ref: '#/components/parameters/SortQueryParam' },
{ $ref: '#/components/parameters/SelectQueryParam' },
{ $ref: '#/components/parameters/PopulateQueryParam' },
]
Note that when generating Swagger/OpenAPI documentation for blueprint routes, the hook also generates:
- Schemas for models, which may be referenced using the form
{ $ref: '#/components/schemas/modelName' }
. - Parameters for model primary keys, which may be referenced using the form
{ $ref: '#/components/parameters/ModelPKParam-modelName' }
.
These may be re-used (referenced) if/as applicable within custom route documentation.
You are able to add to the top-level Swagger/OpenAPI definitions for tags
and components
in all swagger
objects
detailed above and in all JSDoc @swagger
documention comments.
All swagger
objects may contain the elements _tags
and _components
e.g.
{
_tags: [
{
name: 'Test Module',
description: 'Module description ...',
externalDocs: { url: 'https://docs.com/test' }
}
],
_components: {
schemas: {
test: { ... }
}
}
}
Note the use of the _
(underscore) prefix to avoid clashes with / distinguish them from general Swagger/OpenAPI elements.
Similarly, JSDoc @swagger
tags may define tags
and components
:
/**
* @swagger
*
* tags:
* - name: Test Module
* description: |
* Module description
* (continued).
*
* Another paragraph.
*
* externalDocs:
* url: https://docs.com/test
* description: Refer to these docs
*
* components:
* schemas:
* test:
* ...
*/
Tags are added to the top-level Swagger/OpenAPI definitions as follows:
- If a tags with the specified name does not exist, it is added.
- Where a tag with the specified name does exist, elements of that tag that do not exist are added
e.g.
description
andexternalDocs
elements.
Elements of components are added to the top-level Swagger/OpenAPI definitions as follows:
- Elements of the component definition reference (schemas, parameters, etc) are added where they do not exist.
- Existing elements are not overwritten or merged.
The following elements (from the OpenAPI 3 specification) are handled:
let componentDefinitionReference = {
// Reusable schemas (data models)
schemas: {},
// Reusable path, query, header and cookie parameters
parameters: {},
// Security scheme definitions (see Authentication)
securitySchemes: {},
// Reusable request bodies
requestBodies: {},
// Reusable responses, such as 401 Unauthorized or 400 Bad Request
responses: {},
// Reusable response headers
headers: {},
// Reusable examples
examples: {},
// Reusable links
links: {},
// Reusable callbacks
callbacks: {},
};
Three mechanisms are provided to enable advancing filtering of the Swagger generation process:
- An
includeRoute()
function used to filter routes to be included in generated Swagger output. - An
updateBlueprintActionTemplates()
function allows customisation of the templates used to generate Swagger for blueprints. - A
postProcess()
function allows modification of the generated Swagger output before it is written to the output file.
Each is configured in config/swaggergenerator.js
.
This hook parses all routes, custom and blueprint, before commencing the generation of the Swagger output.
Each route is described by a RouteInfo
object
(see defintions here and here):
let routeInfo = {
middlewareType: string, // one of action|blueprint|view
blueprintAction: string, // optional; one of findone|find|create|update|destroy|populate|add|remove|replace or custom
httpMethod: string, // one of all|get|post|put|patch|delete
path: string, // full Sails URL as per sails.getUrlFor() including prefix
swaggerPath: string, // full Sails URL with ':token' replaced with '{token}' for use with Swagger
identity: string, // optional (present for blueprints only); Sails Model identity
modelInfo: object, // optional (present for blueprints only); Sails Model metadata
controller: string, // Sails Controller relative path including 'Controller' suffix
tags: string[], // default name of Swagger group based on Sails controller/action or Sails Model globalId
actionPath: string, // Sails Controller action path consistent with 'router:bind' events (controller relative path without suffix + action name)
actionName: string, // Sails Controller action name e.g. findone or buildModels
actionType: string, // one of controller|standalone|actions2
actionFound: string, // one of notfound|loaded+notfound|loaded+found|implicit
actions2: object, // optional; present for custom routes with actions2 actions
patternVariables: string[], // route dynamic parameters
associations: object[], // optional (present for blueprints only); Sails Model associations
aliases: string[], // optional (applicable for populate blueprints only); Sails Model attribute names for populate action(s)
associationsPrimaryKeyAttributeInfo: object[], // optional (present for blueprints only); array of attributeInfo's for association PKs
swagger: object, // per-route swagger (see below)
};
Sails models are described by a ModelInfo
object
(see defintion here):
let modelInfo = {
tags: string[], // default name of Swagger group based on model globalId
globalId: string, // model globalId
identity: string, // model identity
primaryKey: string, // name of primary key attribute
attributes: object, // attribute definition as per Sails model definition
swagger: object, // model-specific swagger (see below)
};
The includeRoute(routeInfo): boolean
function may be used to select which routes are included in the generated Swagger output.
For example:
module.exports['swagger-generator'] = {
includeRoute: (routeInfo) => {
let c = routeInfo.controller;
if(!c) return true;
if(c.toLowerCase().startsWith('user')) return true;
return false;
}
}
The templates used for generating Swagger for each Sails blueprint action route may be
customised / modified / added to using the updateBlueprintActionTemplates
config option
e.g. to support custom blueprint actions/routes.
For example:
module.exports['swagger-generator'] = {
updateBlueprintActionTemplates: function(blueprintActionTemplates) {
blueprintActionTemplates.search = { ... };
}
}
The blueprintActionTemplates
object contains keys of the blueprint action names
and values as per the following example (refer to the
source code for the default templates):
let blueprintActionTemplates = {
findone: {
summary: 'Get {globalId} (find one)',
description: 'Look up the **{globalId}** record with the specified ID.',
externalDocs: {
url: 'https://sailsjs.com/documentation/reference/blueprint-api/find-one',
description: 'See https://sailsjs.com/documentation/reference/blueprint-api/find-one'
},
parameters: [
'primaryKeyPathParameter', // special case; filtered and substituted during generation phase
{ $ref: '#/components/parameters/LimitQueryParam' },
],
resultDescription: 'Responds with a single **{globalId}** record as a JSON dictionary',
notFoundDescription: 'Response denoting **{globalId}** record with specified ID **NOT** found',
// if functions, each called with (blueprintActionTemplate, routeInfo, pathEntry)
modifiers: ['addSelectQueryParam', exampleModifierFunctionRef],
},
...
};
Note that:
- For summary and description strings the value
{globalId}
is replaced with the applicable Sails model value. - Parameters values are Swagger definitions, with the exception of the special string value
primaryKeyPathParameter
, which may be used to include a reference to a model's primary key. - Modifiers are used to apply custom changes to the generated Swagger, noting that:
- String values are predefined in
generatePaths()
(refer to the source code); valid modifiers are:addPopulateQueryParam
addSelectQueryParam
addOmitQueryParam
addModelBodyParam
addResultOfArrayOfModels
addAssociationPathParam
addAssociationFKPathParam
addAssociationResultOfArray
addResultOfModel
addResultNotFound
addResultValidationError
addFksBodyParam
- Functions are called as
func(blueprintActionTemplate, routeInfo, pathEntry, tags, components)
whereblueprintActionTemplate
the blueprint action template (see above) to which the modifier relatesrouteInfo
the route information object (see above) for which the Swagger is being generatedpathEntry
the generated Swagger path entry to be modifiedtags
the generated Swagger tag definitions to be modified/extendedcomponents
the generated Swagger component definitions to be modified/extended
- String values are predefined in
The final generated Swagger output may be post-processed before it is written to
the output file using a post-processing function specified as the postProcess
config option.
For example:
module.exports['swagger-generator'] = {
postProcess: function(specifications) {
let sch = specifications.components.schemas;
Object.keys(sch).map(k => {
sch[k].description = sck[k].description.toUpperCase();
});
}
}
-
Clone this repository
-
Install all development dependencies
$ npm install
- Then run test
$ npm test
Since the release of v1.0.0 of sailsjs
- test compatibility with v1.0.0
- read info about a controller using machine-as-action #4
Fork this repo and push in your ideas. Do not forget to add a bit of test(s) of what value you adding.
- stick to conventional commit message here or read more angular commit pattern
See the different releases here
MIT License (MIT)