swagger-scrape

1.1.8 • Public • Published

Swagger Scraper

The purpose of this module is to generate Swagger documentation from the JSDoc tags you have annotated your endpoint handlers with. It is able to interpret complex types as well.

Feedback is welcome.

How to initialise

You'll need an ExpressJS application. Find the point at which you have initialised the endpoints.

Required packages:

  • express-list-endpoints: ^4.0.0
  • swagger-ui-express: ^4.0.2

The following code will initialise the swagger documentation endpoints:

let appInfo = {
    version: "1.0.0",
    title: "Project Title",
    description: "Project Description",
    common: [
    	/* files that contains jsdoc common across the endpoints */
    ]
};


// assumed: app = the express js application instance

// include this dependency
const swaggerScrape = require('swagger-scrape');

// grab all endpoint information of endpoints registered with express js
let swaggerEndpoints = swaggerScrape.scrapeEndpoints("./", app);

// conver the endpoint information to a swagger document
let swaggerDoc = swaggerScrape.toSwaggerJson(appInfo, "hostname", "/application-context", swaggerEndpoints);

// expose the swagger ui based on the swagger doc at '/api-docs'.
app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(swaggerDoc));

Reading multiple endpoint maps

In certain situations you will require multiple endpoint maps to be parsed and converted into Swagger documentation. One example being when using a solution that allows both publicEndpoints and protected endpoints. Use the code below to allow for that:

const swaggerHost = process.env.SWAGGER_HOST || ('localhost:' + DEFAULT_PORT);
const swaggerEndpoints = SwaggerScrape.scrapeEndpoints("./", LambdaConfig.endpoints, SwaggerScrape.lambdaRoutes);
const publicSwaggerEndpoints = SwaggerScrape.scrapeEndpoints("./", LambdaConfig.publicEndpoints, SwaggerScrape.lambdaRoutes);
const swaggerDoc = SwaggerScrape.toSwaggerJson(appInfo, `${swaggerHost}`, "/", [...publicSwaggerEndpoints, ...swaggerEndpoints]);

The code is scraped twice, and the resulting arrays in swaggerEndpoints and publicSwaggerEndpoints are combined when invoking the toSwaggerJson function.

Annotating your code

What follows is a completely annotated function that produces a proper swagger document:

/**
 * @swagger
 *
 * Just put the complete description here. It is the description that is shown when you click into a 
 * swagger endpoint in the interface.
 *
 * @summary This is the short text shown right next to the endpoint name
 * @tag TestEndpoint
 *
 * @param req       the request object
 * @param resp      the reponse object
 *
 * @param pathParameter {number} (path) part of the url
 * @param queryParam	{string} (query) part of the requestquery (?queryParam=blaat)
 * @param headerParam	{string} (header) expected header value from request headers
 * @param bodyModel 	{ExpectedModel} (body) This is a complex type parameter that has a proper type hint.
 *
 * @response 403    You cannot go here.
 * @response 200    {ExpectedModel} all is well, information is returned in complex type.
 */
module.exports = function(req, resp) {

	// your code here.

};

Basics

To indicate that this jsdoc pertains to swagger documentation, add the @swagger tag.

You can organise your swagger endpoints into different categories by specifying one or more @tag.

Specify a short summary by adding the @summary tag.

Parameters

Parameters have the following notation:

@param <name> (query|path|header|body) {type} description

You can add an asterisk in front of the parameter type to indicate that it is required, like:

@param myParam *(query) {string} a required parameter

If you do not specify a type, string is assumed.

The following primitive types are supported:

  • number
  • boolean
  • integer
  • string
  • object
  • file

Anything else is considered to be a complex type and has additional logic to retrieve information for.

Response

Specifying a swagger response can be done by using the following notation:

@response <statusCode> {type} description

If no type is specified string is assumed. You can specify a complex type here as well.

Complex Types

When you specify a type name that is not a primitive, the swagger scraper uses the same file to try and find a constant of the name specified as the type name that was annotated with a @model jsdoc tag.

Fields inside the constant definition can be annotated with:

  • @type {name}; the type of this parameter
  • @required; if the parameter is required.

Find a nested model definition below:

/**
 * @model
 */
const AddressModel = {

    /**
     * @type {string}
     */
    street : '',

    /**
     * @type {number}
     */
    zipcode: 1010

}

/**
 * @model
 *
 * This is the empty response model
 */
const ExpectedModel = {

    /**
     * @type {number}
     * @required
     */
    age : 0,

    /**
     * @type {string}
     * @required
     */
    name: null,

    /**
     * @type {string[]}
     */
    lastNames : null,

    /**
     * @type {AddressModel}
     */
    address: null


};

Notice how you can specify arrays by appending the typename with [].

Also note how the AddressModel is another complex type. The scraper recursively resolves them in the same document.

You can also use the @typedef notation described here:

Hinting at where the documentation lives

Oftentimes when initialising endpoints into ExpressJS, you simply refer to a variable that is a closure or function with the signature:

function(req, resp) {}

Given the dynamic nature of Javascript, we cannot easily discover what file the function handler lives in (something we need to know if we would like JSdoc to parse a file). So, for that reason the @fileHint mechanism was introduced.

Inside your function, hint at where the handler actually lives:

app.get('/profile', (req, resp) => {
	/* @fileHint: src/controllers/MyProfile.js::showProfile; */
	return MyProfile.actionFunction(req, resp);
});

or:

app.get('/profile', (req, resp) => {
	/* @fileHint: src/controllers/MyProfile.js; */
	return MyProfile.actionFunction(req, resp);
});

The notation for filehint is as follows:

/* @fileHint: <filename>(::<id-name>); */

The filename parameter is relative to the basepath passed into the scrapeEndpoints function (./ in our example). There is an optional parameter called <id-name>. If you have a file that has more than one handler inside it, you can specify the @id <id-name> it will go looking for. Otherwise it will find the first @swagger annotated jsdoc block.

An example of this would be:

module.exports = {

	/**
	 * @id showProfile
	 * @swagger
	 * This bit of documentation is specific to the actionFunction method
	 */
	function actionFunction(req, resp) {
		
	}

	/**
	 * @id anotherAction
	 * @swagger
	 * This bit of documentation is specific to the other method
	 */
	function actionFunction2(req, resp) {
		
	}


}

Readme

Keywords

Package Sidebar

Install

npm i swagger-scrape

Weekly Downloads

5

Version

1.1.8

License

Apache-2.0

Unpacked Size

30.6 kB

Total Files

3

Last publish

Collaborators

  • marnix