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
Copy the content of generatedSwagger and paste it in Swagger Online Editor.
Simply by lifting your sails app sails lift
, after lifting or starting the app,there should be swagger.json
within ./swagger folder.
make sure ./swagger folder is already existing.
Check the ./swagger/swagger.json for 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). - Creation of default tags for paths based upon Sails Model and Controller
globalId
's.
Use with Swagger UI
See #28
Documentation detail and customisation of most aspects of the generated Swagger can be achieved by adding:
- Top-level configuration to
config/swaggergenerator.js
. This provides direct JSON used as the template for the output Swagger/OpenAPI. - Objects with the key
swagger
to custom route configuration, controller files, action functions, model definitions and model attribute definitions. Theswagger
element must be of type SwaggerActionAttribute for actions (based on OpenApi.Operation) or SwaggerModelSchemaAttribute for model schemas (based on OpenApi.UpdatedSchema). - JSDoc (swagger-jsdoc)
@swagger
comments to 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.
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: 'https://github.com/theo4u', email: '[email protected]'},
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: 'https://theoomoregbee.github.io/'}
},
defaults: {
responses: {
'200': { description: 'The requested resource' },
'404': { description: 'Resource not found' },
'500': { description: 'Internal server error' }
}
},
excludeDeprecatedPutBlueprintRoutes: true,
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'
and output file will not be written if empty/null/undefined (seepostProcess
below for alternate save mechanism).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.excludeDeprecatedPutBlueprintRoutes
should deprecatedPUT
blueprint routes be excluded from generated Swagger output; defaults totrue
.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 an alternate mechanism for saving and/or 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
(must be of type SwaggerActionAttribute for actions, based on OpenApi.Operation) to individual route configurations inconfig/routes.js
. - Adding an object with the key
swagger
(must be of type SwaggerControllerAttribute) to the exports of a controller file, standalone action file or actions2 file. - Adding an object with the key
swagger
(must be of type SwaggerModelAttribute) to the exports of a model file. - Adding JSDoc
@swagger
comments to Sails model files, controller files, standalone action files or actions2 files; specifically:- JSDoc
@swagger
documentation under the/{actionName}
path for the route (controllers/actions), or - JSDoc
@swagger
documentation under the/{blueprintAction}
path for the route (models), or - JSDoc
@swagger
documentation undertags
andcomponents
paths for adding to the top-level Swagger/OpenAPI definitions.
- JSDoc
If you want to add extra configuration to a route, it can be done via the config/routes.js
, since Sails uses different route targets, we can leverage the route object target to extend/override our swagger configuration by adding an object with a key swagger
.
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' }
}
}
}
}
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
swagger.actions.{actionName}
name. See UserController.js; - For standalone action or actions2 files, placing content in the
swagger.actions.{actionFileName|actions2FileName}
object. See actions2.js Note:actionFileName|actions2FileName
must correspond to the filename; - For all controller/action files, adding per-action documentation to be
applied to all actions using the key
swagger.actions.allActions
e.g. use this to apply common tags to all actions for a controller. - Adding documentation under
tags
andcomponents
elements for adding to the top-level Swagger/OpenAPI definitions. See example in either UserController.js or actions2.js.
- 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 controller file actions, - JSDoc
@swagger
documentation under the/{actionFileName|actions2FileName}
path for standalone action or actions2 files, - JSDoc
@swagger
documentation under the/allActions
path to be applied to all actions for the controller, or - JSDoc
@swagger
documentation undertags
andcomponents
paths for adding to the top-level Swagger/OpenAPI definitions.
- JSDoc
An exclude
property, set to true
, may be added to any swagger
element or @swagger
JSDoc
action documentation to exclude that action from the generated Swagger. See example in
NomodelController.js.
The Swagger definition for each action is merged in the order above to form the final
definition, with config/routes.js
taking highest precendence and earlier definitions
above taking precedence over later.
- 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. - Inputs may also add an object with the key
meta.swagger
to document the attributes Swagger/OpenAPI schema associated with the input value. See example in actions2.js. - Inputs may be excluded from the generated Swagger by setting
meta.swagger.exclude
totrue
. - Inputs may specify where the input should be included within the generated Swagger using the key
meta.swagger.in
. The valuesquery
/header
/path
/cookie
may be used to produce Swagger operation parameters and the valuebody
may be used to producerequestBody
schema properties (valid for PUT/POST/PATCH operations only).
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: {
actions: {
tokens: {
tags: [ 'Auth' ],
description: 'Route description...'
}
}
tags: [
{
name: 'Auth',
description: 'Module description ...',
}
],
components: {
...
}
}
};
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
:- Adding documentation to the model's Swagger schema using the key
swagger.modelSchema
e.g. use this to apply detailed documentation via thedescription
field; - In additon to the model's schema, the key
swagger.modelSchema
may be used to specify tag names (as astring[]
) to be assigned all blueprint actions for the model. This is a non-standard convenience function i.e. in Swagger/OpenAPI you need to explicitly add tags to each/every OpenAPI.Operation; - Adding per-action documentation by adding objects keyed on
swagger.actions.{blueprintAction}
name; - Adding action documentation to all actions using the key
swagger.actions.allActions
e.g. use this to apply commonexternalDocs
to all blueprint actions for the model; or - Adding documentation under
swagger.tags
andswagger.components
elements for adding to the top-level Swagger/OpenAPI definitions.
- Adding documentation to the model's Swagger schema using the key
- Adding documentation-specific fields to model attributes (supports
description
,moreInfoUrl
andexample
). Note that applicable Sails attributes, automigrations and validations are also parsed. - Adding an object with the key
meta.swagger
to individual model attributes to document the attributes Swagger/OpenAPI schema. See example in Pet.js. - Adding JSDoc
@swagger
comments to model files:- JSDoc
@swagger
documentation under the/{globalId}
to add documentation to the model's Swagger schema (or tags as noted above), - JSDoc
@swagger
documentation under the/{blueprintAction}
to add per-action documentation for the model blueprint actions, - JSDoc
@swagger
documentation under the/allActions
path to be applied to all blueprint actions for the model, or - JSDoc
@swagger
documentation undertags
andcomponents
paths for adding to the top-level Swagger/OpenAPI definitions.
- JSDoc
An exclude
property, set to true
, may be added to any swagger
element of @swagger
JSDoc
action documentation to exclude the model completely (exclude the schema) or a specific
blueprint action from the generated Swagger. See example in OldPet.js.
Individual model attributes may be excluded from the generated Swagger by setting
meta.swagger.exclude
to true
. See example in Pet.js.
OpenAPI 3 specifies the Any Type by the absence of the type
property in a schema;
this may be achieved by setting a model attribute's meta.swagger.type
value to null
.
See example in User.js.
The Swagger definition for each action is merged in the order above to form the final
definition, with config/routes.js
taking highest precendence and earlier definitions
above taking precedence over later.
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: {
actions: {
create: { ... },
},
modelSchema: { ... },
tags: [...]
components: {...}
}
};
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
(except the ones specified in `config.routes.js) e.g.
{
tags: [
{
name: 'Test Module',
description: 'Module description ...',
externalDocs: { url: 'https://docs.com/test' }
}
],
components: {
schemas: {
test: { ... }
}
}
}
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.
Note that a final clean-up phase is run after processing, which performs the following:
- Removal of unreferenced tags; and
- Creation of tags referenced but are not defined.
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.
For example, the element components.schemas.pet
will be added as part of a merge process,
but the contents of multiple definitions of pet
will not be 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 an alternate mechanism for saving and/or 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 SwaggerRouteInfo
object
(see defintion here):
export interface SwaggerRouteInfo {
middlewareType: MiddlewareType; //< one of action|blueprint
verb: HTTPMethodVerb; //< one of all|get|post|put|patch|delete
path: string; //< full Sails URL as per sails.getUrlFor() including prefix
variables: string[]; //< list of ALL variables extracted from path e.g. `/pet/:id` --> `id`
optionalVariables: string[]; //< list of optional variables from path e.g. `/pet/:id?`
action: string; //< either blueprint action (e.g. 'find') or action identity (e.g. 'subdir/reporting/run')
actionType: ActionType; //< one of blueprint|shortcutBlueprint|controller|standalone|actions2|function
actions2Machine?: Sails.Actions2Machine; //< for actionType === 'actions2', details of the action2 machine
model?: SwaggerSailsModel; //< reference to Sails Model (blueprints only)
associationAliases?: string[]; //< association attribute names (relevant blueprint routes only)
defaultTagName?: string; //< default tag name for route, if any, based on Sails Model or Controller
swagger?: SwaggerActionAttribute; //< per-route Swagger (OpenApi Operation)
}
Other interfaces for models, swagger
elements etc may be found in interfaces.ts.
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 = { ... };
return blueprintActionTemplates;
}
}
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
addModelBodyParamUpdate
addResultOfArrayOfModels
addAssociationPathParam
addAssociationFKPathParam
addAssociationResultOfArray
addResultOfModel
addResultNotFound
addResultValidationError
addFksBodyParam
addShortCutBlueprintRouteNote
- 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 situations where saving the generated swagger documentation JSON to a file is
not desired/appropriate, the postProcess
config option may be used to specify
an alternate save mechanism.
Note that if swaggerJsonPath
config option is empty/null/undefined the output file will not be written.
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
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
- While developing, you can run the below command to start nodemon watch too run linting and testing on
.ts
changes
npm run dev
See the different releases here
MIT License (MIT)