Skip to content

Commit

Permalink
Merge pull request #29 from tlivings/v3.0.0
Browse files Browse the repository at this point in the history
Move to Swagger 2.0 Specification
  • Loading branch information
tlivings committed Sep 19, 2014
2 parents 4764475 + a0877fb commit 91a9777
Show file tree
Hide file tree
Showing 30 changed files with 515 additions and 670 deletions.
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,6 @@ html-report
xunit.xml
node_modules
npm-debug.log
test/fixtures/package.json

.project
.idea
Expand Down
48 changes: 5 additions & 43 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,45 +1,7 @@
### 1.0.0-rc.4
### 3.0.0

* Fixed issue related to [#18](https://github.com/krakenjs/swaggerize-express/issues/18) and [expressjs/body-parser#44](https://github.com/expressjs/body-parser/issues/44).
- Swagger 2.0 compatible.
- `docspath` defaults to `/api-docs`.
- `setUrl` is now `setHost` and is used to set `host` and `port` in the `api`.

### 1.0.0-rc.3

* Fixed issue where generator failed with deeper `path`.

### 1.0.0-rc.2

* Improved tests generation.

### 1.0.0-rc.1

* __BREAKING CHANGE__: `options.docs` is `options.docspath` and defaults to `/`.

### 0.1.0-alpha.6

* __BREAKING CHANGE__: Reverted to standard express handlers.
* Removed output validation.
* Middleware capability.

### 0.1.0-alpha.5

* Better tests generator.
* Bug fixes.

### 0.1.0-alpha.4

* Only one of `--handlers` or `--models` is necessary in generator, not both.
* Generator now has a `--tests` switch for generating tests.
* Output validation is opt-in via `outputValidation: true`.

### 0.1.0-alpha.3

* `resourcePath` in swagger document is base `mountpath` for routes.
* Added support for path variables in `handlers` directory names.
* Path parameters are always required if defined.
* Fixed trailing commas in handler definitions created by generator.

### 0.1.0-alpha.2

* Added generator for models and handlers stubs.
* Fixed a bug that was not allowing multiple operations under an API definition.
* Fixed bug caused by `res.send` deprecation.
See `v1.0.0`, `v2.0.0` branches for addition changelogs.
44 changes: 5 additions & 39 deletions QUICKSTART.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,47 +10,14 @@ $ mkdir helloworld && cd $_

### 2.

```bash
$ vim api.json
```

Paste the following:

```json
{
"swaggerVersion": "1.2",
"apiVersion": "1.0.0",
"basePath": "http://localhost:8000/",
"resourcePath": "/v1/helloworld",
"apis": [
{
"path": "/hello/{name}",
"operations": [
{
"method": "GET",
"type": "string",
"nickname": "getHello",
"parameters": [
{
"name": "name",
"required": true,
"type": "string",
"paramType": "path"
}
]
}
]
}
]
}
```
Grab the (Swagger Pet Store example)[https://github.com/wordnik/swagger-spec/blob/master/examples/v2.0/json/petstore.json].

### 3.

```bash
$ npm init
$ npm install --save express swaggerize-express
$ node_modules/.bin/swaggerize --api api.json --handlers handlers --models models --tests tests
$ node_modules/.bin/swaggerize --api petstore.json --handlers handlers --models models --tests tests
```

### 4.
Expand All @@ -71,15 +38,14 @@ app = express();
var server = http.createServer(app);

var swagger = swaggerize({
api: require('./api.json'),
handlers: './handlers',
outputvalidation: app.settings.env === 'development'
api: require('./petstore.json'),
handlers: './handlers'
});

app.use(swagger);

server.listen(8000, 'localhost', function () {
swagger.setUrl('http://' + server.address().address + ':' + server.address().port);
swagger.setHost(server.address().address + ':' + server.address().port);
});
```

Expand Down
16 changes: 8 additions & 8 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
- **Stability:** `stable`
- **Changelog:** [https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md](https://github.com/krakenjs/swaggerize-express/blob/master/CHANGELOG.md)

`swaggerize-express` is a "spec first" approach to building RESTful services with a [Swagger spec](https://github.com/wordnik/swagger-spec/blob/master/versions/1.2.md)
`swaggerize-express` is a "spec first" approach to building RESTful services with a [Swagger spec](https://github.com/wordnik/swagger-spec/blob/master/versions/2.0.md)
and Express.

`swaggerize-express` provides the following features:
Expand All @@ -16,13 +16,13 @@ and Express.
- Input model validation.
- Models and handlers stubs generator command (`swaggerize`).

### Why "Spec First"
### Why "Design First"

There are already a number of modules that help build REST services with express and swagger. However,
these modules tend to focus on building the documentation or specification as a side effect of writing
the application business logic.

`swaggerize-express` begins with the service specification first. This facilitates writing services that
`swaggerize-express` begins with the service definition first. This facilitates writing services that
are easier to design, review, and test.

### Usage
Expand All @@ -39,11 +39,11 @@ app.use(swaggerize({

Options:

- `api` - a valid Swagger 1.2 document.
- `api` - a valid Swagger 2.0 document.
- `docspath` - the path to expose api docs for swagger-ui, etc. Defaults to `/`.
- `handlers` - either a directory structure for route handlers or a premade object (see *Handlers Object* below).

The base url for the api can also be updated via the `setUrl` function on the middleware.
The base url for the api can also be updated via the `setHost` function on the middleware.

Example:

Expand All @@ -65,15 +65,15 @@ var swagger = swaggerize({
app.use(swagger);

server.listen(port, 'localhost', function () {
swagger.setUrl('http://' + server.address().address + ':' + server.address().port);
swagger.setHost(server.address().address + ':' + server.address().port);
});
```

Also checkout the [Quick Start Guide](https://github.com/krakenjs/swaggerize-express/blob/master/QUICKSTART.md).

### Mount Path

Api `path` values will be prefixed with the swagger document's `resourcePath` value.
Api `path` values will be prefixed with the swagger document's `basePath` value.

### Handlers Directory

Expand Down Expand Up @@ -178,4 +178,4 @@ Example:
swaggerize --api config/api.json --models resources/models --handlers resources/handlers --tests tests/
```

`--api` is required, but only one of `--models` or `--handlers` or `--tests` is required.
`--api` is required, but only one of `--models` or `--handlers` or `--tests` is required.
65 changes: 48 additions & 17 deletions bin/lib/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@
var fs = require('fs'),
path = require('path'),
lodash = require('lodash'),
mkdirp = require('mkdirp');
mkdirp = require('mkdirp'),
utils = require('swaggerize-builder/lib/utils');

var modelTemplate, handlerTemplate, testTemplate;

Expand All @@ -22,6 +23,9 @@ function createModels(models, modelsPath) {
if (!fs.existsSync(fileName)) {
model = models[modelName];
mkdirp.sync(path.dirname(fileName));
if (!model.id) {
model.id = modelName;
}
fs.writeFileSync(fileName, lodash.template(template, model));
}
else {
Expand All @@ -30,36 +34,44 @@ function createModels(models, modelsPath) {
});
}

function createHandlers(apis, handlersPath) {
function createHandlers(paths, handlersPath) {
var routes, template;

routes = {};
template = fs.readFileSync(handlerTemplate);

apis.forEach(function (api) {
Object.keys(paths).forEach(function (path) {
var pathnames, route;

route = {
path: api.path,
path: path,
pathname: undefined,
methods: []
};

pathnames = [];

api.path.split('/').forEach(function (element) {
path.split('/').forEach(function (element) {
if (element) {
pathnames.push(element);
}
});

route.pathname = pathnames.join('/');

api.operations.forEach(function (operation) {
utils.verbs.forEach(function (verb) {
var operation = paths[path][verb];

if (!operation) {
return;
}

route.methods.push({
method: operation.method.toLowerCase(),
name: operation.nickname,
output: operation.type
method: verb,
name: operation.operationId,
description: operation.description,
parameters: operation.parameters,
produces: operation.produces
});
});

Expand Down Expand Up @@ -111,13 +123,13 @@ function createTests(api, testsPath, apiPath, handlersPath, modelsPath) {
apiPath = path.relative(testsPath, apiPath);
handlersPath = path.relative(testsPath, handlersPath);

if (api.models && modelsPath) {
if (api.definitions && modelsPath) {

Object.keys(api.models).forEach(function (key) {
Object.keys(api.definitions).forEach(function (key) {
var modelSchema, ModelCtor, options;

options = {};
modelSchema = api.models[key];
modelSchema = api.definitions[key];
ModelCtor = require(path.join(modelsPath, key.toLowerCase() + '.js'));

Object.keys(modelSchema.properties).forEach(function (prop) {
Expand Down Expand Up @@ -149,19 +161,38 @@ function createTests(api, testsPath, apiPath, handlersPath, modelsPath) {

}

resourcePath = api.resourcePath;
resourcePath = api.basePath;

Object.keys(api.paths).forEach(function (opath) {
var fileName, operations;

operations = [];

utils.verbs.forEach(function (verb) {
var operation = {};

api.apis.forEach(function (api) {
var fileName;
if (!api.paths[opath][verb]) {
return;
}

Object.keys(api.paths[opath][verb]).forEach(function (key) {
operation[key] = api.paths[opath][verb][key];
});

operation.path = opath;
operation.method = verb;

operations.push(operation);
});

fileName = path.join(testsPath, 'test' + api.path.replace(/\//g, '_') + '.js');
fileName = path.join(testsPath, 'test' + opath.replace(/\//g, '_') + '.js');

if (!fs.existsSync(fileName)) {
fs.writeFileSync(fileName, lodash.template(template, {
apiPath: apiPath,
handlers: handlersPath,
resourcePath: resourcePath,
api: api,
operations: operations,
models: models
}));

Expand Down
39 changes: 25 additions & 14 deletions bin/lib/swaggerize.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@
var fs = require('fs'),
path = require('path'),
mkdirp = require('mkdirp'),
schema = require('swaggerize-builder/lib/schema'),
tv4 = require('tv4'),
create = require('./create');

module.exports = function (options) {
var apiPath, modelsPath, handlersPath, testsPath, validation, api;
var apiPath, modelsPath, handlersPath, testsPath, api;

function usage() {
console.error('swaggerize --api <swagger document> [[--models <models dir>] | [--handlers <handlers dir>] | [--tests <tests dir>]]');
Expand All @@ -30,15 +30,7 @@ module.exports = function (options) {

api = require(apiPath);

validation = schema.validate(api);

if (!validation.valid) {
console.error('%s (at %s)', validation.error, validation.error.dataPath);
if (validation.error.subErrors) {
validation.error.subErrors.forEach(function (subError) {
console.error('%s (at %s)', subError, subError.dataPath);
});
}
if (validate(api)) {
return 1;
}

Expand All @@ -47,7 +39,7 @@ module.exports = function (options) {
console.error('tests can not be generated without handlers path.');
return usage();
}
if ((api.models && !modelsPath)) {
if ((api.definitions && !modelsPath)) {
console.error('api contains models, so tests can not be generated without handlers and models paths.');
return usage();
}
Expand All @@ -63,10 +55,29 @@ module.exports = function (options) {
}
});

modelsPath && create.models(api.models, modelsPath);
handlersPath && create.handlers(api.apis, handlersPath);
modelsPath && create.models(api.definitions, modelsPath);
handlersPath && create.handlers(api.paths, handlersPath);

testsPath && create.tests(api, testsPath, apiPath, handlersPath, modelsPath);

return 0;
};

function validate(api) {
var schema, validator, validation;

schema = require('swaggerize-builder/lib/schema/swagger-spec/schemas/v2.0/schema.json');
validator = tv4.freshApi();

validation = validator.validateResult(api, schema);

if (!validation.valid) {
console.error('%s (at %s)', validation.error.message, validation.error.dataPath || '/');
if (validation.error.subErrors) {
validation.error.subErrors.forEach(function (subError) {
console.error('%s (at %s)', subError, subError.dataPath);
});
}
return 1;
}
}
Loading

0 comments on commit 91a9777

Please sign in to comment.