gulp-swagger-codegen

2.0.3 • Public • Published

gulp-swagger-codegen

Fast, flexible code generation for Node.js + Swagger projects

Travis-CI Build Prod Dependencies Dev Dependencies Code Coverage npm version

Stats Downloads

This gulp module generates code from Swagger API definitions and is intended to be used to simplify implementation of API servers/clients by providing generated code for the request/response objects, and also some templates to simplify use of the swagger-tools package.

This package uses ES6, and thus is only available for Node.js 4.x+ or suitable Io.js versions. If in doubt, please review our .travis.yml file to determine if we are currently testing for your particular version of Node.

Installation

To install this plugin:

npm install --save-dev gulp-swagger-codegen

Debugging and Troubleshooting

If the plugin fails, a console error message should indicate the nature of the problem. Typically most errors are templating syntax related. If you are having trouble and not sure where to look, you can validate the logical behaviour of the plugin by setting:

(On windows)
SET DEBUG=gulp-swagger-codegen

(On OS/X and Linux)
export DEBUG=gulp-swagger-codegen

This will cause the debug output to be produced for the plugin when it runs, giving you an insight into why it's doing certain things or not producing any output.

Example Usage

The following code snippet shows the module being used in a standard Gulp workflow, where we assume that the various templates below exist in your project(s):

gulp.task('default', () => {
  return gulp.src(['./path/to/your/swagger.yaml'])
    .pipe(task({
      helpers: {
        /* Assign handlebars helpers with this syntax.
        someName: yourHelperFunction
        someName will become the #someName helper in the files.
        */
      }
      perDefinition: {
        './templates/es6/definition.hbs': {
          target:     './definitions',
          extension:  '.js',
        },
      },
      perPath: {
        './templates/es6/swagger-tools-controller.hbs': {
          target: './controllers',
          groupBy: 'x-swagger-router-controller',
          extension: '.js',
          operations: ['get', 'put', 'post', 'delete'],
        },
      }
    }))
    .pipe(gulp.dest('./server/api/'))
});

You can dramatically simplify this by using a template-helper module, such as swagger-template-es6-server:

const templateSet = require('swagger-template-es6-server');
const codegen = require('gulp-swagger-codegen');

gulp.task('generate-code', () =>
  gulp.src(['./examples/waffle-maker/service-contract.yaml'])
    .pipe(codegen(templateSet({
      implementationPath: '../implementation',
    })))
    .pipe(gulp.dest('./examples/waffle-maker')));

For more information on this template set, see the official NPM package site.

Included Templates

Within the NPM package for this module, several templates are included to help serve as examples and potentially jump-start your development:

  • {package-root}/templates/es6/definition.hbs
    • Generates a complete ES6 class for parsing the general structure of an entity, with basic null field checking and array/nesting support.
  • {package-root}/templates/es6/swagger-tools-controller.hbs
    • Generates a simple controller wrapper for a 'swagger-tools' router that calls methods on another class 'director' that actually performs the work. The controllers are intended to remove all the boilerplate parameter pack and unpack work. This template requires:
      • x-swagger-router-controller to be set per-path or per-operation
      • x-gulp-swagger-codegen-outcome to be set on all response schemas. See below for more detail on the swagger-tools-controller example.

Other templates may be included from time to time.

Task Configuration Options

perDefinition

This defines templates that are executed per request/response definition in your Swaggerfile. The keys of this object represent the path to a handlebars template.

Configuration Schema

The supported schema of perDefinition is:

{
  'template-file-path': {
    target:     'relative-output-root',
    _extension:  '.ext',
    /* Any user-set options for templates */
  }
}

Worked Example Usage

perDefinition: {
  './templates/es6/definition.hbs': {
    target:     './definitions',
    extension:  '.js',
  },
},

This will, for each definition object in a Swaggerfile:

  • Generate from the /templates/es6/definition.hbs template
  • Write the file to /definitions (relative to gulp.dest)
  • Apply the .js extension

Definition file names are by default lowercase. You can execute as many different templates or types of template per definition as you require, the only constraint is that you cannot use the same template multiple times per definition.

perPath

This defines templates that are executed per groups of operations, aggregated by some attribute of the path (or operation within the path). An operation is a method such as get, put, delete, post defined within a path. An example attribute to group on would by x-swagger-router-controller from the swagger-tools swaggerRouter middleware, which lets you map operationId's to controllers.

Configuration Schema

The supported schema of perPath is:

{
  'template-file-path.ext': {
    target: 'output-folder',
    groupBy: 'some-group-attribute',
    operations: ['some-http-verb', 'other-http-verb'],
    extension: '.ext',
    /* Any user-set options for templates */
  },
}

Worked Example

Consider the following example:

  perPath: {
        './templates/es6/swagger-tools-controller.hbs': {
          target: './controllers',
          groupBy: 'x-swagger-router-controller',
          operations: ['get', 'put', 'post', 'delete'],
          extension: '.js',
        },
      }

This example will:

  • Iterate over each group of operations, grouped by x-swagger-controller-router attributes in the YAML.
  • Only operations for get, put, post and delete will be included. Others (head etc) will be ignored.
  • Create a file per group under ./controllers/ relative to the gulp output
  • Save the result with a .js extension

Multiple template sets, grouping by different attributes of paths or operations are possible.

Templating Details

The structure of data that is passed to the templates is described in the following section(s):

Common Annotations / Extensions

When being passed to templates, the following object-schema extensions are applied:

  • Definitions
    • definitionName - The swaggerfile 'name' of this definition (i.e. Pet, Purchase etc)
    • referencePath - The $ref equivelent path of the definition (i.e. #/definitions/Pet)

perDefinition Templates

Each handlebars template in perDefinition is run with the following initial context:

{
  model,          (overall parsed YAML/JSON swaggerfile)
  definition,     (the current definition being generated)
  definitionMap,  (name to definition map of all definitions)
  options         (Options for this template path, allows passing of extra fields)
}

perPath Templates

Each handlebars template in perPath is run with the following initial context:

{
  fileName,       (filename of template, i.e. PetsController becomes petscontroller)
  groupKey,       (value of the groupBy attribute)
  members,        (Operations grouped into this group)
  definitions,    (Definition map for all definitions)
  model,          (overall parsed YAML/JSON swaggerfile)
  options,        (Options for this template path, allows passing of extra fields)
}

Template Additional Information - swagger-tools-controller

The swagger-tools-controller.hbs template is intended to provide a simplification of code when using swagger-tools to generate most of your microservice. Each operationId must be extended with:

  • x-swagger-router-controller - Defines which 'controller.js' class contains the operation. This can be applied either at the path level or per-operation, overriding the path.
  • operationId - Defines a method name on the controller we generate, with a (req, res) input set.

Here's an example config section:

  perPath: {
    './templates/es6/swagger-tools-controller.hbs': {
      groupBy: 'x-swagger-router-controller',
      target: './controllers',
      extension: '.js',
      operations: ['get', 'put', 'post', 'delete'],
      implementationPath: '../implementation',
      // Extension properties you can use inside templates
      // for example, the bellow is only for the swagger-tools example
      defsRelativeToController: '../definitions' // relative path of /definitions vs controller output for requires.
    },
  },

The configuration for the template must pass an additional implementationPath: this is used because we generate a call to `require('implementationPath/controller-name.js'). However this module must have an export per operationId.

The templated controller will parse out each input in swaggerfile declaration order and then call a method on your implementation type, suich as:

// Create responder: This will set the content type, status code and also
// terminate the request. Note that you must set x-gulp-swagger-codegen-outcome
// on operations in order to have a mapping here. Enforce typing of the
// responses with swaggerValidator from swagger-tools.
const responder = {
  req,
  res,
  // Handle status 200 [success]
  success: function endSuccess(result) {
    res.json(result || {}, 200);
  },
  // Handle status 400 [invalidId]
  invalidId: function endInvalidId(result) {
    res.json(result || {}, 400);
  },
  // Handle status 404 [notFound]
  notFound: function endNotFound(result) {
    res.json(result || {}, 404);
  },
}

// Validate implementation presence
const impl = resolveImplementation(usersImplementation);
if (!impl) {
  throw new Error('Cannot resolve implementation of users');
} else if (!impl.getUserByName) {
  throw new Error('Implementation is missing operation getUserByName for users');
} else if (!(typeof impl.getUserByName === 'function')) {
  throw new Error('Implementation is not a function: getUserByName for users');
}

// Execute
impl.getUserByName(
  username,
  responder
);

Note that in this example, we are calling getUserByName on the implementation type and passing both the parameters (username) and a responder object. The responder object is a function-map for terminating the request, so that when your implementation is done, you can call the correct result-name and the generated code will set the result code and end the request.

You can annotate the names of the responder actions with the annotation x-gulp-swagger-codegen-outcome such as:

  responses:
    "404":
      x-gulp-swagger-codegen-outcome: notFound
      description: User not found
    "200":
      x-gulp-swagger-codegen-outcome: success
      description: successful operation
      schema:
        $ref: "#/definitions/User"
    "400":
      x-gulp-swagger-codegen-outcome: invalidId
      description: Invalid username supplied

Any operations without an annotation will not be processed by the template. You can manually use the res property from the responder for this scenario if you wish.

Contributions and Licensing

This code is MIT licensed and free for re-use in your projects and for forking. Whilst I'll do my best to maintain it for the forseeable future, you're more than welcome to submit feature requests or pull requests.

Dependencies (8)

Dev Dependencies (14)

Package Sidebar

Install

npm i gulp-swagger-codegen

Weekly Downloads

39

Version

2.0.3

License

MIT

Last publish

Collaborators

  • steve-gray