Skip to content
This repository was archived by the owner on Dec 10, 2021. It is now read-only.

Commit 822f2c8

Browse files
Brendan Abbottnfour
Brendan Abbott
authored andcommitted
Various improvements, see CHANGELOG
1 parent 87d9edb commit 822f2c8

11 files changed

+778
-644
lines changed

Diff for: CHANGELOG.md

+23
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
# Changelog
2+
3+
All notable changes to this project will be documented in this file.
4+
5+
The format is based on [Keep a Changelog](http://keepachangelog.com/en/1.0.0/)
6+
and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html).
7+
8+
## [Unreleased]
9+
10+
## Changed
11+
12+
- Plugin now generates OpenAPI documentation with a version of `3.0.0` instead of `3.0.0-RC2`.
13+
- Operation now supports `deprecated` and `tags` properties.
14+
- Parameters now support the `content` property.
15+
- Updated various build dependencies.
16+
17+
## Fixed
18+
19+
- Handle when `models` is not iterable.
20+
21+
## [v0.2.1] - 2017-07-07
22+
23+
Last release prior to CHANGELOG being added.

Diff for: README.md

+9-7
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@
44
[![Travis CI](https://img.shields.io/travis/temando/serverless-openapi-documentation.svg)](https://travis-ci.org/temando/serverless-openapi-documentation)
55
[![JavaScript Style Guide](https://img.shields.io/badge/code_style-standard-brightgreen.svg)](https://standardjs.com)
66

7-
Generates [**OpenAPI 3.0 RC2**](https://github.com/OAI/OpenAPI-Specification/tree/OpenAPI.next) documentation from serverless configuration files. OpenAPI is formerly known as Swagger. The configuration is inspired by the format used in [serverless-aws-documentation](https://www.npmjs.com/package/serverless-aws-documentation).
7+
Generates [**OpenAPI 3.0.0**](https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md) documentation from serverless configuration files. OpenAPI is formerly known as Swagger. The configuration is inspired by the format used in [serverless-aws-documentation](https://www.npmjs.com/package/serverless-aws-documentation).
88

9-
Works well with [Lincoln Open Api Renderer](https://github.com/temando/open-api-renderer).
9+
Works well with [Lincoln OpenAPI Renderer](https://github.com/temando/open-api-renderer).
1010

1111
---
1212

@@ -42,7 +42,7 @@ Plugin: ServerlessOpenAPIDocumentation
4242
openapi generate ...................... Generate OpenAPI v3 Documentation
4343
--output / -o ...................... Output file location [default: openapi.yml|json]
4444
--format / -f ...................... OpenAPI file format (yml|json) [default: yml]
45-
--indent / -i ...................... File indentation in spaces[default: 2]
45+
--indent / -i ...................... File indentation in spaces [default: 2]
4646
--help / -h ...................... Help
4747
```
4848

@@ -58,7 +58,7 @@ The `custom` section of your `serverless.yml` can be configured as below:
5858
custom:
5959
documentation:
6060
version: '1'
61-
summary: 'My API'
61+
title: 'My API'
6262
description: 'This is my API'
6363
models: {}
6464
```
@@ -73,9 +73,9 @@ functions:
7373
myFunc:
7474
events:
7575
- http:
76-
path: getStuff
77-
method: get
78-
documentation: ${file(serverless.doc.yml):endpoints.myFunc}
76+
path: getStuff
77+
method: get
78+
documentation: ${file(serverless.doc.yml):endpoints.myFunc}
7979
```
8080

8181
For more info on `serverless.yml` syntax, see their docs.
@@ -333,6 +333,8 @@ serverless | grep "ServerlessOpenAPIDocumentation"
333333

334334
It should return `ServerlessOpenAPIDocumentation` as one of the plugins on the list.
335335

336+
> Note: Add this plugin _after_ `serverless-offline` to prevent issues with `String.replaceAll` being overridden incorrectly.
337+
336338
## License
337339

338340
MIT

Diff for: package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,9 @@
3333
"build": "scripts/build.bash"
3434
},
3535
"devDependencies": {
36+
"@types/bluebird": "^3.5.8",
3637
"@types/chalk": "^0.4.31",
37-
"@types/fs-extra": "^3.0.3",
38+
"@types/fs-extra": "^4.0.0",
3839
"@types/jest": "^20.0.2",
3940
"@types/js-yaml": "^3.5.31",
4041
"@types/node": "^8.0.7",
@@ -48,11 +49,10 @@
4849
"typescript": "^2.4.1"
4950
},
5051
"dependencies": {
51-
"@jdw/jst": "^1.0.0",
52-
"@types/bluebird": "^3.5.8",
52+
"@jdw/jst": "^2.0.0-beta.9",
5353
"bluebird": "^3.5.0",
5454
"chalk": "^2.0.1",
55-
"fs-extra": "^3.0.1",
55+
"fs-extra": "^4.0.1",
5656
"js-yaml": "^3.8.4",
5757
"lutils": "^2.4.0",
5858
"swagger2openapi": "^2.5.0",

Diff for: src/DefinitionGenerator.ts

+36-17
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
import { dereference } from '@jdw/jst';
2-
import * as openApiValidator from 'swagger2openapi/validate';
3-
2+
// tslint:disable-next-line no-submodule-imports
3+
import { validateSync as openApiValidatorSync } from 'swagger2openapi/validate';
44
import * as uuid from 'uuid';
5-
import { IDefinition, IDefinitionConfig, IParameterConfig, IServerlessFunctionConfig } from './types';
6-
import { clone, merge } from './utils';
5+
import { IDefinition, IDefinitionConfig, IOperation, IParameterConfig, IServerlessFunctionConfig } from './types';
6+
import { clone, isIterable, merge } from './utils';
77

88
export class DefinitionGenerator {
99
// The OpenAPI version we currently validate against
10-
public version = '3.0.0-RC2';
10+
public version = '3.0.0';
1111

1212
// Base configuration object
1313
public definition = <IDefinition> {
@@ -43,7 +43,7 @@ export class DefinitionGenerator {
4343
},
4444
});
4545

46-
if (models) {
46+
if (isIterable(models)) {
4747
for (const model of models) {
4848
this.definition.components.schemas[model.name] = this.cleanSchema(
4949
dereference(model.schema),
@@ -58,9 +58,9 @@ export class DefinitionGenerator {
5858
const payload: any = {};
5959

6060
try {
61-
openApiValidator.validateSync(this.definition, payload);
61+
openApiValidatorSync(this.definition, payload);
6262
} catch (error) {
63-
payload.error = JSON.parse(error.message.replace(/^Failed OpenAPI3 schema validation: /, ''));
63+
payload.error = error.message;
6464
}
6565

6666
return payload;
@@ -78,18 +78,13 @@ export class DefinitionGenerator {
7878
const httpEventConfig = httpEvent.http;
7979

8080
if (httpEventConfig.documentation) {
81-
const documentationConfig = httpEventConfig.documentation;
8281
// Build OpenAPI path configuration structure for each method
8382
const pathConfig = {
8483
[`/${httpEventConfig.path}`]: {
85-
[httpEventConfig.method]: {
86-
operationId: funcConfig._functionName,
87-
summary: documentationConfig.summary || '',
88-
description: documentationConfig.description || '',
89-
responses: this.getResponsesFromConfig(documentationConfig),
90-
parameters: this.getParametersFromConfig(documentationConfig),
91-
requestBody: this.getRequestBodiesFromConfig(documentationConfig),
92-
},
84+
[httpEventConfig.method]: this.getOperationFromConfig(
85+
funcConfig._functionName,
86+
httpEventConfig.documentation,
87+
),
9388
},
9489
};
9590

@@ -117,6 +112,26 @@ export class DefinitionGenerator {
117112
return cleanedSchema;
118113
}
119114

115+
/**
116+
* Generate Operation objects from the Serverless Config.
117+
*
118+
* @link https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#operationObject
119+
* @param funcName
120+
* @param documentationConfig
121+
*/
122+
private getOperationFromConfig (funcName: string, documentationConfig): IOperation {
123+
return {
124+
operationId: funcName,
125+
tags: documentationConfig.tags || [],
126+
deprecated: documentationConfig.deprecated || false,
127+
summary: documentationConfig.summary || '',
128+
description: documentationConfig.description || '',
129+
parameters: this.getParametersFromConfig(documentationConfig),
130+
requestBody: this.getRequestBodiesFromConfig(documentationConfig),
131+
responses: this.getResponsesFromConfig(documentationConfig),
132+
};
133+
}
134+
120135
/**
121136
* Derives Path, Query and Request header parameters from Serverless documentation
122137
* @param documentationConfig
@@ -181,6 +196,10 @@ export class DefinitionGenerator {
181196
parameterConfig.examples = parameter.examples;
182197
}
183198

199+
if (parameter.content) {
200+
parameterConfig.content = parameter.content;
201+
}
202+
184203
parameters.push(parameterConfig);
185204
}
186205
}

Diff for: src/ServerlessOpenApiDocumentation.ts

+13-9
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ export class ServerlessOpenApiDocumentation {
4848
shortcut: 'f',
4949
},
5050
indent: {
51-
usage: 'File indentation in spaces[default: 2]',
51+
usage: 'File indentation in spaces [default: 2]',
5252
shortcut: 'i',
5353
},
5454
},
@@ -128,14 +128,18 @@ export class ServerlessOpenApiDocumentation {
128128
this.log(`${ c.bold.green('[VALIDATION]') } OpenAPI valid: ${c.bold.green('true')}\n\n`);
129129
} else {
130130
this.log(`${c.bold.red('[VALIDATION]')} Failed to validate OpenAPI document: \n\n`);
131-
this.log(`${c.bold.green('Path:')} ${JSON.stringify(validation.context, null, 2)}\n`);
132-
133-
for (const info of validation.error) {
134-
this.log(c.grey('\n\n--------\n\n'));
135-
this.log(' ', c.blue(info.dataPath), '\n');
136-
this.log(' ', info.schemaPath, c.bold.yellow(info.message));
137-
this.log(c.grey('\n\n--------\n\n'));
138-
this.log(`${inspect(info, { colors: true, depth: 2 })}\n\n`);
131+
this.log(`${c.bold.green('Context:')} ${JSON.stringify(validation.context, null, 2)}\n`);
132+
133+
if (typeof validation.error === 'string') {
134+
this.log(`${validation.error}\n\n`);
135+
} else {
136+
for (const info of validation.error) {
137+
this.log(c.grey('\n\n--------\n\n'));
138+
this.log(' ', c.blue(info.dataPath), '\n');
139+
this.log(' ', info.schemaPath, c.bold.yellow(info.message));
140+
this.log(c.grey('\n\n--------\n\n'));
141+
this.log(`${inspect(info, { colors: true, depth: 2 })}\n\n`);
142+
}
139143
}
140144
}
141145

Diff for: src/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { ServerlessOpenApiDocumentation } from './ServerlessOpenApiDocumentation';
22

3+
// tslint:disable-next-line no-default-export
34
export default ServerlessOpenApiDocumentation;
45
module.exports = ServerlessOpenApiDocumentation;

Diff for: src/types.ts

+28-2
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,27 @@ export interface IServerlessFunctionConfig {
2929
events?: any[];
3030
}
3131

32+
// TODO: We could use another TS based OpenAPI project to get type information
33+
// for OpenAPI definitions.
34+
// @see https://github.com/Mermade/awesome-openapi3#parsersmodelsvalidators
35+
36+
// @see https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#operation-object
37+
export interface IOperation {
38+
tags?: string[];
39+
summary?: string;
40+
description?: string;
41+
externalDocs?: any;
42+
operationId?: string;
43+
parameters?: IParameterConfig[];
44+
requestBody?: any;
45+
responses?: any;
46+
callbacks?: any;
47+
deprecated?: boolean;
48+
security?: any[];
49+
servers?: any[];
50+
}
51+
52+
// @see https://github.com/OAI/OpenAPI-Specification/blob/3.0.0/versions/3.0.0.md#parameterObject
3253
export interface IParameterConfig {
3354
name: string;
3455
in: 'path' | 'query' | 'header' | 'cookie';
@@ -41,15 +62,20 @@ export interface IParameterConfig {
4162
explode?: boolean;
4263
allowReserved?: boolean;
4364
example?: any;
44-
examples?: [any];
65+
examples?: any[];
66+
content?: Map<string, any>;
4567
}
4668

4769
// FIXME:
4870
export interface IDefinition {
4971
openapi: string;
50-
components: any;
5172
info: any;
73+
servers?: any[];
5274
paths: any;
75+
components?: any;
76+
security?: any[];
77+
tags?: any[];
78+
externalDocs: any;
5379
}
5480

5581
export type ILog = (...str: string[]) => void;

Diff for: src/utils.ts

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ import { Clone, IMerge, Merge } from 'lutils';
22

33
export const merge: IMerge = new Merge({ depth: 100 }).merge;
44
export const clone = new Clone({ depth: 100 }).clone;
5+
export const isIterable = (obj) => obj != null && typeof obj[Symbol.iterator] === 'function'

Diff for: test/project/serverless.yml

+2-2
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ functions:
1111
createUser:
1212
handler: handler.create
1313
events:
14-
- http:
14+
- http:
1515
path: create
1616
method: post
1717
documentation:
@@ -38,7 +38,7 @@ functions:
3838
cookieParams:
3939
- name: SessionId
4040
description: A Session ID variable
41-
schema:
41+
schema:
4242
type: string
4343
methodResponses:
4444
- statusCode: 201

Diff for: tsconfig.json

+5-4
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,18 @@
33
"target": "es6",
44
"module": "commonjs",
55
"moduleResolution": "node",
6-
"outDir":"./build",
6+
"outDir": "./build",
77
"sourceMap": true,
88
"declaration": true,
99
"noUnusedLocals": true,
1010
"downlevelIteration": true,
1111
"lib": [
1212
"es6",
1313
"es7",
14-
"dom"
14+
"dom",
15+
"esnext.asynciterable"
1516
]
1617
},
17-
"exclude": [],
18-
"include": [ "src/**/*" ]
18+
"include": [ "src/**/*" ],
19+
"exclude": [ "node_modules" ]
1920
}

0 commit comments

Comments
 (0)