Skip to content
This repository has been archived by the owner on Jan 31, 2025. It is now read-only.

V2.x #72

Merged
merged 15 commits into from
Mar 16, 2017
Merged

V2.x #72

Show file tree
Hide file tree
Changes from 14 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
coverage
templates
test/fixtures
25 changes: 25 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"rules": {
"indent": [
2,
4
],
"quotes": [
2,
"single"
],
"linebreak-style": [
2,
"unix"
],
"semi": [
2,
"always"
]
},
"env": {
"es6": true,
"node": true
},
"extends": "eslint:recommended"
}
69 changes: 0 additions & 69 deletions .jshintrc

This file was deleted.

39 changes: 11 additions & 28 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,32 +1,15 @@
### Unreleased
## v2.0.0

- `options.security` to define the security authorize handlers directory.
### Bugfixes

### 1.0.8
- #67 - swagger schema extensions (^x-) in info section are not allowed
- #63 - Incorrect property path on validation errors
- #73 - Validation for multiple $ref parameters doesn't work

- Added support for allowEmptyValue.
### Features

### 1.0.7

### 1.0.6

### 1.0.5

### 1.0.4

### 1.0.3

- Add schema from NPM.

### 1.0.2

- Added custom 'file' type to validators.

### 1.0.1

- Fixed a bug where basePath was being changed to resourcePath.

### 1.0.0 (renamed to swaggerize-routes)

- Alternately specify `x-handler` properties to reference handlers at either the operation or path level.
- Added security definitions to route.
- Use [swagger-parser](https://github.com/BigstickCarpet/swagger-parser) to validate the Swagger spec.
- The api is dereferenced (both remote and local $ref are dereferenced) using [swagger-parser](https://github.com/BigstickCarpet/swagger-parser) #40
- Use [JSON schema validator](https://github.com/mafintosh/is-my-json-valid) as the default validator. #30.
- Option to set `joischema` to `true` to use [Joi](https://github.com/hapijs/joi) schema validator. Uses [enjoi](https://github.com/tlivings/enjoi) - The json to joi schema converter - to build the validator functions.
- By default, validators are provided for `response` schema also (in addition to input `parameters`).
66 changes: 53 additions & 13 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,28 +13,64 @@ Lead Maintainer: [Trevor Livingston](https://github.com/tlivings/)
- Schema validation.
- Building route definitions from a Swagger 2.0 document.
- Validation helpers for input parameters.
- Validation helpers for response.

### Usage

```javascript
var builder = require('swaggerize-routes');
const builder = require('swaggerize-routes');

var routes = builder({
const routeBuilder = builder({
api: require('./api.json'),
handlers: './handlers',
security: './security' //Optional - security authorize handlers as per `securityDefinitions`
}));

//Promise Style
routeBuilder.then(routeObj => {
let { api, routes } = routeObj;
// `api` is the resolved swagger api Object ($ref, both remote and local references are resolved)
// `routes` - an array of routes corresponding to the swagger api `paths`.

}).catch(error => Assert.ifError(error));

//OR

// Callback style
builder({
api: 'http://petstore.swagger.io/v2/swagger.json',
handlers: './handlers',
security: './security', //Optional - security authorize handlers as per `securityDefinitions`
joischema: true //Set to true if `joischema` need to be used for validators.
}), (error, routes) => {
Assert.ifError(error);
let { api, routes } = routeObj;
// `api` is the resolved swagger api Object ($ref and remote and local ref are resolved)
// `routes` - an array of routes corresponding to the swagger api `paths`.
});

```

Options:
### API

- `api` - a valid Swagger 2.0 object.
- `handlers` - either a directory structure for route handlers or a premade object (see *Handlers Object* below).
- `basedir` - base directory to search for `handlers` path (defaults to `dirname` of caller).
- `schemas` - an array of `{name: string, schema: string|object}` representing additional schemas to add to validation.
- `security` - directory to scan for authorize handlers corresponding to `securityDefinitions`.
`builder(options, [cb])`

* `options` - (*Object*) - (required) - Options to build the routes based on swagger api.

- `api` - (*Object*) or (*String*) or (*Promise*) - (required) - api can be one of the following.
- A relative or absolute path to the Swagger api document.
- A URL of the Swagger api document.
- The swagger api Object
- A promise (or a `thenable`) that resolves to the swagger api Object.

- `handlers` - (*Object*) or (*String*) - (required) - either a directory structure for route handlers or a pre-created object (see *Handlers Object* below). If `handlers` option is not provided, route builder will try to use the default `handlers` directory (only if it exists). If there is no `handlers` directory available, then the route builder will try to use the `x-handler` swagger schema extension.
- `basedir` - (*String*) - (optional) - base directory to search for `handlers` path (defaults to `dirname` of caller).
- `security` - (*String*) - (optional) - directory to scan for authorize handlers corresponding to `securityDefinitions`.
- `validated` - (*Boolean*) - (optional) - Set this property to `true` if the api is already validated against swagger schema and already dereferenced all the `$ref`. This is really useful to generate validators for parsed api specs. Default value for this is `false` and the api will be validated using [swagger-parser validate](https://github.com/BigstickCarpet/swagger-parser/blob/master/docs/swagger-parser.md#validateapi-options-callback).
- `joischema` - (*Boolean*) - (optional) - Set to `true` if you want to use [Joi](https://github.com/hapijs/joi) schema based Validators. Swagvali uses [enjoi](https://github.com/tlivings/enjoi) - The json to joi schema converter - to build the validator functions, if `joischema` option is set to `true`.

* `callback` - (*Function*) - (optional) - `function (error, mock)`. If a callback is not provided a `Promise` will be returned.

**Returns:** An array of the processed routes.

### Handlers Directory

Expand Down Expand Up @@ -162,7 +198,11 @@ Example:

Handler keys in files do *not* have to be namespaced in this way.

### Route Object
### Route Object response

The response route object has two properties - `api` and `routes`.

`api` is the resolved swagger api object. This has all the resolved $ref values - both local and remote references.

The `routes` array returned from the call to the builder will contain `route` objects. Each `route` has the following properties:

Expand All @@ -176,13 +216,13 @@ The `routes` array returned from the call to the builder will contain `route` ob
- `consumes` - same as `consumes` in `api` definition.
- `produces` - same as `produces` in `api` definition.

### Validator Object
#### Validator Object

The validator object in the `validators` array will have the following properties:

- `parameter` - same as the `parameter` from the operation on `path`.
- `validate(value, callback)` - a function for validating the input data against the `parameter` definition.
- `schema` - the `joi` schema being validated against.
- `spec` - The schema of the parameter.
- `joischema` - The `joi` schema being validated against. This will be available only for the validators with option `joischema` set as `true`. By default the validator uses `is-my-json-valid` JSON schema validator and `joischema` property in validator object will be `undefined`.

### Security directory

Expand Down
101 changes: 101 additions & 0 deletions lib/builders/routes.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,101 @@
//const Fs = require('fs');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lingering comment

const { merge } = require('merge-object-files');
const Thing = require('core-util-is');
const Utils = require('../utils');
const buildSecurity = require('./security');
const buildValidator = require('swagvali');

const Buildroutes = (apiResolver, options) => {
let { handlers, basedir } = options;
let fileResolver = Thing.isObject(handlers)
// Use the handlers object
? Promise.resolve(handlers)
// Construct the handler object using `merge-object-files` for a valid dir path.
: (Thing.isString(handlers) ? merge(handlers, [ 'js' ]) : Promise.resolve(null));
let validatorResolver = buildValidator(apiResolver, { validated: true});

return Promise.all([ apiResolver, fileResolver, validatorResolver ]).then(resolved => {
let routes = [];
let [ api, files, validators ] = resolved;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Guessing order is maintained from Promise.all ?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes

Object.keys(api.paths).forEach(path => {
let pathObj = api.paths[path];
Object.keys(pathObj).forEach(operation => {
let operationObj;
let handler;
//If the key is not a valid operation, skip the iteration.
if (!Utils.verbs.includes(operation)) {
return;
}
operationObj = pathObj[operation];
//Find the handler function from handler directory.
handler = pathfinder(path, operation, files);
if(!handler) {
//Resolve the x-handler if exists
handler = operationObj['x-handler'] || pathObj['x-handler'];
if (handler) {
handler = Utils.resolve(basedir, handler, operation);
}
}
//Push the handler to the route definition.
if (handler){
routes.push({
path,
name: operationObj.operationId,
description: operationObj.description,
method: operation,
security: buildSecurity(operationObj.security || pathObj.security || api.security, api.securityDefinitions, options),
validators: validators[path][operation]['parameters'],
handler,
consumes: operationObj.consumes || api.consumes,
produces: operationObj.produces || api.produces
});
}
});
});

return {
api,
routes
};
});
};

const opsfinder = (source, operation) => {
let $find = source[`$${operation}`];
// Check if there is an operation key prefixed with `$` sign. (e.g:- $get, $post)
// Handlers option passed as an object should have `$` prefixed operation key
// to avoid namespace collisions.
if ($find) {
return $find;
}
return source[operation];
};

const pathfinder = (path, operation, files) => {
let pathnames = path.split('/').filter(el => el);
if (!files) {
return;
}
return (pathnames[0] ? matchpath(operation, pathnames, files[pathnames[0]]) : opsfinder(files, operation));
};

/**
* Match a route handler to a given path name set.
* @param method
* @param pathnames
* @param handlers
* @returns {*}
*/
const matchpath = (method, pathnames, handlers) => {
if (!handlers) {
return null;
}
if (pathnames.length > 1) {
pathnames.shift();
return matchpath(method, pathnames, handlers[pathnames[0]]);
}

return handlers[pathnames[0]] ? handlers[pathnames[0]] : opsfinder(handlers, method);
};

module.exports = Buildroutes;
Loading