Skip to content

Commit 804a92d

Browse files
committed
Wip
1 parent 49376ff commit 804a92d

8 files changed

+1004
-101
lines changed

src/DocumentGenerator.ts renamed to src/DefinitionGenerator.ts

+37-40
Original file line numberDiff line numberDiff line change
@@ -3,16 +3,16 @@ import * as c from 'chalk';
33
import * as openApiValidator from 'swagger2openapi/validate';
44

55
import * as uuid from 'uuid';
6-
import { ILog, IParameterConfig, IServerlessFunctionConfig, IServiceDescription } from './types';
6+
import { IDefinitionConfig, ILog, IParameterConfig, IServerlessFunctionConfig } from './types';
77
import { clone, merge } from './utils';
88

9-
export class DocumentGenerator {
9+
export class DefinitionGenerator {
1010
// The OpenAPI version we currently validate against
11-
private openapiVersion = '3.0.0-RC1';
11+
public version = '3.0.0-RC1';
1212

1313
// Base configuration object
14-
private config = {
15-
openapi: this.openapiVersion,
14+
public definition = {
15+
openapi: this.version,
1616
description: '',
1717
version: '0.0.0',
1818
title: '',
@@ -22,65 +22,64 @@ export class DocumentGenerator {
2222
},
2323
};
2424

25-
private serviceDescriptor: IServiceDescription;
25+
private config: IDefinitionConfig;
2626
private log: ILog;
2727

2828
/**
2929
* Constructor
3030
* @param serviceDescriptor IServiceDescription
3131
*/
32-
constructor ({ log, serviceDescriptor }: {
32+
constructor ({ log, config }: {
3333
log: ILog,
34-
serviceDescriptor: IServiceDescription,
34+
config: IDefinitionConfig,
3535
}) {
36-
this.serviceDescriptor = clone(serviceDescriptor);
36+
this.config = clone(config);
3737
this.log = log;
38+
}
39+
40+
public parse () {
41+
const {
42+
title = '',
43+
description = '',
44+
version = uuid.v4(),
45+
models,
46+
} = this.config;
3847

39-
merge(this.config, {
40-
openapi: this.openapiVersion,
48+
merge(this.definition, {
49+
openapi: this.version,
4150
servers: [],
42-
info: {
43-
title: serviceDescriptor.summary || '',
44-
description: serviceDescriptor.description || '',
45-
version: serviceDescriptor.version || uuid.v4(),
46-
},
51+
info: { title, description, version },
4752
paths: {},
4853
components: {
4954
schemas: {},
5055
securitySchemes: {},
5156
},
5257
});
5358

54-
for (const model of serviceDescriptor.models) {
55-
this.config.components.schemas[model.name] = this.cleanSchema(dereference(model.schema));
59+
if (models) {
60+
for (const model of models) {
61+
this.definition.components.schemas[model.name] = this.cleanSchema(
62+
dereference(model.schema),
63+
);
64+
}
5665
}
57-
}
58-
59-
public generate () {
60-
const result: any = {};
61-
this.log(`${ c.bold.yellow('[VALIDATION]') } Validating OpenAPI generated output\n`);
6266

63-
try {
64-
openApiValidator.validateSync(this.config, result);
67+
return this;
68+
}
6569

66-
this.log(`${ c.bold.green('[VALIDATION]') } OpenAPI valid: ${c.bold.green('true')}\n\n`);
70+
public validate (): { valid: boolean, context: string[], warnings: any[] } {
71+
const payload: any = {};
6772

68-
return this.config;
69-
} catch (e) {
70-
this.log(
71-
`${c.bold.red('[VALIDATION]')} Failed to validate OpenAPI document: \n\n${c.yellow(e.message)}\n\n` +
72-
`${c.bold.green('Path:')} ${JSON.stringify(result, null, 2)}\n`,
73-
);
73+
openApiValidator.validateSync(this.definition, payload);
7474

75-
throw new Error('Failed to validate OpenAPI document');
76-
}
75+
return payload;
7776
}
7877

7978
/**
8079
* Add Paths to OpenAPI Configuration from Serverless function documentation
8180
* @param config Add
8281
*/
83-
public addPathsFromFunctionConfig (config: IServerlessFunctionConfig[]): void {
82+
public readFunctions (config: IServerlessFunctionConfig[]): void {
8483
// loop through function configurations
8584
for (const funcConfig of config) {
8685
// loop through http events
@@ -102,7 +101,7 @@ export class DocumentGenerator {
102101
},
103102
};
104103
// merge path configuration into main configuration
105-
merge(this.config.paths, pathConfig);
104+
merge(this.definition.paths, pathConfig);
106105
}
107106
}
108107
}
@@ -179,7 +178,6 @@ export class DocumentGenerator {
179178
: parameter.style === 'form';
180179
}
181180

182-
// console.log(parameter);
183181
if (parameter.schema) {
184182
parameterConfig.schema = this.cleanSchema(parameter.schema);
185183
}
@@ -190,7 +188,6 @@ export class DocumentGenerator {
190188
parameterConfig.examples = parameter.examples;
191189
}
192190

193-
// Add parameter config to parameters array
194191
parameters.push(parameterConfig);
195192
}
196193
}
@@ -210,7 +207,7 @@ export class DocumentGenerator {
210207
// For each request model type (Sorted by "Content-Type")
211208
for (const requestModelType of Object.keys(documentationConfig.requestModels)) {
212209
// get schema reference information
213-
const requestModel = this.serviceDescriptor.models.filter(
210+
const requestModel = this.config.models.filter(
214211
(model) => model.name === documentationConfig.requestModels[requestModelType],
215212
).pop();
216213

@@ -287,7 +284,7 @@ export class DocumentGenerator {
287284
private getResponseContent (response) {
288285
const content = {};
289286
for (const responseKey of Object.keys(response)) {
290-
const responseModel = this.serviceDescriptor.models.filter(
287+
const responseModel = this.config.models.filter(
291288
(model) => model.name === response[responseKey],
292289
).pop();
293290
if (responseModel) {

src/ServerlessOpenApiDocumentation.ts

+29-14
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
import * as c from 'chalk';
22
import * as fs from 'fs';
33
import * as YAML from 'js-yaml';
4-
import { DocumentGenerator } from './DocumentGenerator';
5-
import { IConfigType, ILog } from './types';
4+
import { DefinitionGenerator } from './DefinitionGenerator';
5+
import { IDefinitionType, ILog } from './types';
66
import { merge } from './utils';
77

88
export class ServerlessOpenApiDocumentation {
@@ -70,8 +70,8 @@ export class ServerlessOpenApiDocumentation {
7070
* Processes CLI input by reading the input from serverless
7171
* @returns config IConfigType
7272
*/
73-
private processCliInput (): IConfigType {
74-
const config: IConfigType = {
73+
private processCliInput (): IDefinitionType {
74+
const config: IDefinitionType = {
7575
format: 'yaml',
7676
file: 'openapi.yml',
7777
indent: 2,
@@ -103,40 +103,55 @@ export class ServerlessOpenApiDocumentation {
103103
private generate () {
104104
this.log(c.bold.underline('OpenAPI v3 Documentation Generator\n\n'));
105105
// Instantiate DocumentGenerator
106-
const generator = new DocumentGenerator({
107-
serviceDescriptor: this.customVars.documentation,
106+
const generator = new DefinitionGenerator({
107+
config: this.customVars.documentation,
108108
log: this.log,
109109
});
110110

111+
generator.parse();
112+
111113
// Map function configurations
112114
const funcConfigs = this.serverless.service.getAllFunctions().map((functionName) => {
113115
const func = this.serverless.service.getFunction(functionName);
114116
return merge({ _functionName: functionName }, func);
115117
});
116118

117119
// Add Paths to OpenAPI Output from Function Configuration
118-
generator.addPathsFromFunctionConfig(funcConfigs);
120+
generator.readFunctions(funcConfigs);
119121

120122
// Process CLI Input options
121123
const config = this.processCliInput();
122124

123-
// Generate the resulting OpenAPI Object
124-
const outputObject = generator.generate();
125+
this.log(`${ c.bold.yellow('[VALIDATION]') } Validating OpenAPI generated output\n`);
126+
127+
const validation = generator.validate();
128+
129+
if (validation.valid) {
130+
this.log(`${ c.bold.green('[VALIDATION]') } OpenAPI valid: ${c.bold.green('true')}\n\n`);
131+
} else {
132+
this.log(
133+
`${c.bold.red('[VALIDATION]')} Failed to validate OpenAPI document: \n\n` +
134+
`${c.bold.green('Path:')} ${JSON.stringify(validation, null, 2)}\n`,
135+
);
136+
}
137+
138+
const { definition } = generator;
125139

126140
// Output the OpenAPI document to the correct format
127-
let outputContent = '';
141+
142+
let output;
128143
switch (config.format.toLowerCase()) {
129144
case 'json':
130-
outputContent = JSON.stringify(outputObject, null, config.indent);
145+
output = JSON.stringify(definition, null, config.indent);
131146
break;
132147
case 'yaml':
133148
default:
134-
outputContent = YAML.safeDump(outputObject, { indent: config.indent });
149+
output = YAML.safeDump(definition, { indent: config.indent });
135150
break;
136151
}
137152

138-
// Write to disk
139-
fs.writeFileSync(config.file, outputContent);
153+
fs.writeFileSync(config.file, output);
154+
140155
this.log(`${ c.bold.green('[SUCCESS]') } Output file to "${c.bold.red(config.file)}"\n`);
141156
}
142157
}

src/__tests__/DocumentGenerator.spec.ts renamed to src/__tests__/DefinitionGenerator.spec.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import * as path from 'path';
22
import * as Serverless from 'serverless';
3-
import { DocumentGenerator } from '../DocumentGenerator';
3+
import { DefinitionGenerator } from '../DefinitionGenerator';
44

55
class ServerlessInterface extends Serverless {
66
public service: any = {};
@@ -27,7 +27,7 @@ describe('OpenAPI Documentation Generator', () => {
2727
await sls.variables.populateService();
2828

2929
if ('documentation' in sls.service.custom) {
30-
const docGen = new DocumentGenerator(sls.service.custom.documentation);
30+
const docGen = new DefinitionGenerator(sls.service.custom.documentation);
3131

3232
expect(docGen).not.toBeNull();
3333
} else {

src/types.ts

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,15 +8,15 @@ export interface IModels {
88
example: object;
99
}
1010

11-
export interface IServiceDescription {
12-
version?: string;
13-
summary: string;
11+
export interface IDefinitionConfig {
12+
title: string;
1413
description: string;
14+
version?: string;
1515
models: IModels[];
1616
}
1717

18-
export interface IConfigType {
19-
file: string; // Foo blah
18+
export interface IDefinitionType {
19+
file: string;
2020
format: 'yaml' | 'json';
2121
indent: number;
2222
}

0 commit comments

Comments
 (0)