serverless-openapi-documenter 0.0.68 → 0.0.70
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/package.json +2 -2
- package/src/schemaHandler.js +210 -170
package/README.md
CHANGED
|
@@ -114,7 +114,7 @@ Options:
|
|
|
114
114
|
| path[path].[operation].parameters.name | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.name |
|
|
115
115
|
| path[path].[operation].parameters.in | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params |
|
|
116
116
|
| path[path].[operation].parameters.description | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.description |
|
|
117
|
-
| path[path].[operation].parameters.required | functions.functions.[http OR httpApi].documentation.[
|
|
117
|
+
| path[path].[operation].parameters.required | functions.functions.[http OR httpApi].documentation.[query/cookie/header]Params.required |
|
|
118
118
|
| path[path].[operation].parameters.deprecated | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.deprecated |
|
|
119
119
|
| path[path].[operation].parameters.allowEmptyValue | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.allowEmptyValue |
|
|
120
120
|
| path[path].[operation].parameters.style | functions.functions.[http OR httpApi].documentation.[path/query/cookie/header]Params.style |
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "serverless-openapi-documenter",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.70",
|
|
4
4
|
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"keywords": [
|
|
@@ -43,7 +43,7 @@
|
|
|
43
43
|
"json-schema-for-openapi": "^0.3.2",
|
|
44
44
|
"lodash.isequal": "^4.5.0",
|
|
45
45
|
"oas-validator": "^5.0.8",
|
|
46
|
-
"openapi-to-postmanv2": "^4.
|
|
46
|
+
"openapi-to-postmanv2": "^4.16.0",
|
|
47
47
|
"swagger2openapi": "^7.0.8",
|
|
48
48
|
"uuid": "^9.0.0"
|
|
49
49
|
},
|
package/src/schemaHandler.js
CHANGED
|
@@ -1,195 +1,235 @@
|
|
|
1
|
-
|
|
1
|
+
"use strict";
|
|
2
2
|
|
|
3
|
-
const path = require(
|
|
3
|
+
const path = require("path");
|
|
4
4
|
|
|
5
|
-
const $RefParser = require("@apidevtools/json-schema-ref-parser")
|
|
6
|
-
const SchemaConvertor = require(
|
|
7
|
-
const isEqual = require(
|
|
8
|
-
const { v4: uuid } = require(
|
|
5
|
+
const $RefParser = require("@apidevtools/json-schema-ref-parser");
|
|
6
|
+
const SchemaConvertor = require("json-schema-for-openapi");
|
|
7
|
+
const isEqual = require("lodash.isequal");
|
|
8
|
+
const { v4: uuid } = require("uuid");
|
|
9
9
|
|
|
10
10
|
class SchemaHandler {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
11
|
+
constructor(serverless, openAPI) {
|
|
12
|
+
this.apiGatewayModels =
|
|
13
|
+
serverless.service?.provider?.apiGateway?.request?.schemas || {};
|
|
14
|
+
this.documentation = serverless.service.custom.documentation;
|
|
15
|
+
this.openAPI = openAPI;
|
|
15
16
|
|
|
16
|
-
|
|
17
|
+
this.modelReferences = {};
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
this.__standardiseModels();
|
|
19
20
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
}
|
|
21
|
+
try {
|
|
22
|
+
this.refParserOptions = require(path.resolve("options", "ref-parser.js"));
|
|
23
|
+
} catch (err) {
|
|
24
|
+
this.refParserOptions = {};
|
|
25
25
|
}
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* Standardises the models to a specific format
|
|
30
|
+
*/
|
|
31
|
+
__standardiseModels() {
|
|
32
|
+
const standardModel = (model) => {
|
|
33
|
+
if (model.schema) {
|
|
34
|
+
return model;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const contentType = Object.keys(model.content)[0];
|
|
38
|
+
model.contentType = contentType;
|
|
39
|
+
model.schema = model.content[contentType].schema;
|
|
40
|
+
|
|
41
|
+
return model;
|
|
42
|
+
};
|
|
43
|
+
|
|
44
|
+
const standardisedModels =
|
|
45
|
+
this.documentation?.models?.map(standardModel) || [];
|
|
46
|
+
const standardisedModelsList =
|
|
47
|
+
this.documentation?.modelsList?.map(standardModel) || [];
|
|
48
|
+
|
|
49
|
+
const standardisedGatewayModels =
|
|
50
|
+
Object.keys(this.apiGatewayModels).flatMap((key) => {
|
|
51
|
+
const gatewayModel = this.apiGatewayModels[key];
|
|
52
|
+
return standardModel(gatewayModel);
|
|
53
|
+
}) || [];
|
|
54
|
+
|
|
55
|
+
this.models = standardisedModels.concat(
|
|
56
|
+
standardisedModelsList,
|
|
57
|
+
standardisedGatewayModels
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
async addModelsToOpenAPI() {
|
|
62
|
+
for (const model of this.models) {
|
|
63
|
+
const modelName = model.name;
|
|
64
|
+
const modelSchema = model.schema;
|
|
65
|
+
|
|
66
|
+
const dereferencedSchema = await this.__dereferenceSchema(
|
|
67
|
+
modelSchema
|
|
68
|
+
).catch((err) => {
|
|
69
|
+
if (err.errors) {
|
|
70
|
+
for (const error of err?.errors) {
|
|
71
|
+
if (error.message.includes("HTTP ERROR")) {
|
|
72
|
+
// throw err;
|
|
73
|
+
throw new Error(
|
|
74
|
+
`There was an error dereferencing ${
|
|
75
|
+
model.name
|
|
76
|
+
} schema. \n\n dereferencing message: ${
|
|
77
|
+
error.message
|
|
78
|
+
} \n\n Model received: ${JSON.stringify(model)}`
|
|
79
|
+
);
|
|
34
80
|
}
|
|
35
|
-
|
|
36
|
-
const contentType = Object.keys(model.content)[0]
|
|
37
|
-
model.contentType = contentType
|
|
38
|
-
model.schema = model.content[contentType].schema
|
|
39
|
-
|
|
40
|
-
return model
|
|
81
|
+
}
|
|
41
82
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
for (const
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
}
|
|
68
|
-
return modelSchema
|
|
69
|
-
})
|
|
70
|
-
|
|
71
|
-
const convertedSchemas = SchemaConvertor.convert(dereferencedSchema, modelName)
|
|
72
|
-
|
|
73
|
-
for (const [schemaName, schemaValue] of Object.entries(convertedSchemas.schemas)) {
|
|
74
|
-
if (schemaName === modelName) {
|
|
75
|
-
this.modelReferences[schemaName] = `#/components/schemas/${modelName}`
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
this.__addToComponents('schemas', schemaValue, schemaName)
|
|
79
|
-
}
|
|
83
|
+
return modelSchema;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
const convertedSchemas = SchemaConvertor.convert(
|
|
87
|
+
dereferencedSchema,
|
|
88
|
+
modelName
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
if (
|
|
92
|
+
typeof convertedSchemas.schemas === "object" &&
|
|
93
|
+
!Array.isArray(convertedSchemas.schemas) &&
|
|
94
|
+
convertedSchemas.schemas !== null
|
|
95
|
+
) {
|
|
96
|
+
for (const [schemaName, schemaValue] of Object.entries(
|
|
97
|
+
convertedSchemas.schemas
|
|
98
|
+
)) {
|
|
99
|
+
if (schemaName === modelName) {
|
|
100
|
+
this.modelReferences[
|
|
101
|
+
schemaName
|
|
102
|
+
] = `#/components/schemas/${modelName}`;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
this.__addToComponents("schemas", schemaValue, schemaName);
|
|
80
106
|
}
|
|
107
|
+
} else {
|
|
108
|
+
throw new Error(
|
|
109
|
+
`There was an error converting the ${
|
|
110
|
+
model.name
|
|
111
|
+
} schema. Model received looks like: \n\n${JSON.stringify(model)}`
|
|
112
|
+
);
|
|
113
|
+
}
|
|
81
114
|
}
|
|
115
|
+
}
|
|
82
116
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
if (this.modelReferences[name] && schema === undefined) {
|
|
88
|
-
return this.modelReferences[name]
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
const dereferencedSchema = await this.__dereferenceSchema(schema)
|
|
92
|
-
.catch(err => {
|
|
93
|
-
throw err
|
|
94
|
-
})
|
|
95
|
-
|
|
96
|
-
const convertedSchemas = SchemaConvertor.convert(dereferencedSchema, name)
|
|
97
|
-
|
|
98
|
-
for (const [schemaName, schemaValue] of Object.entries(convertedSchemas.schemas)) {
|
|
99
|
-
if (this.__existsInComponents(schemaName)) {
|
|
100
|
-
if (this.__isTheSameSchema(schemaValue, schemaName) === false) {
|
|
101
|
-
if (schemaName === originalName) {
|
|
102
|
-
finalName = `${schemaName}-${uuid()}`
|
|
103
|
-
this.__addToComponents('schemas', schemaValue, finalName)
|
|
104
|
-
} else {
|
|
105
|
-
this.__addToComponents('schemas', schemaValue, schemaName)
|
|
106
|
-
}
|
|
107
|
-
}
|
|
108
|
-
} else {
|
|
109
|
-
this.__addToComponents('schemas', schemaValue, schemaName)
|
|
110
|
-
}
|
|
111
|
-
}
|
|
117
|
+
async createSchema(name, schema) {
|
|
118
|
+
let originalName = name;
|
|
119
|
+
let finalName = name;
|
|
112
120
|
|
|
113
|
-
|
|
121
|
+
if (this.modelReferences[name] && schema === undefined) {
|
|
122
|
+
return this.modelReferences[name];
|
|
114
123
|
}
|
|
115
124
|
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
if (
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
Object.assign(deReferencedSchema, { ...referencedProperties })
|
|
136
|
-
|
|
137
|
-
delete deReferencedSchema.$ref
|
|
138
|
-
deReferencedSchema = await this.__dereferenceSchema(deReferencedSchema)
|
|
139
|
-
.catch((err) => {
|
|
140
|
-
throw err
|
|
141
|
-
})
|
|
125
|
+
const dereferencedSchema = await this.__dereferenceSchema(schema).catch(
|
|
126
|
+
(err) => {
|
|
127
|
+
throw err;
|
|
128
|
+
}
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
const convertedSchemas = SchemaConvertor.convert(dereferencedSchema, name);
|
|
132
|
+
|
|
133
|
+
for (const [schemaName, schemaValue] of Object.entries(
|
|
134
|
+
convertedSchemas.schemas
|
|
135
|
+
)) {
|
|
136
|
+
if (this.__existsInComponents(schemaName)) {
|
|
137
|
+
if (this.__isTheSameSchema(schemaValue, schemaName) === false) {
|
|
138
|
+
if (schemaName === originalName) {
|
|
139
|
+
finalName = `${schemaName}-${uuid()}`;
|
|
140
|
+
this.__addToComponents("schemas", schemaValue, finalName);
|
|
141
|
+
} else {
|
|
142
|
+
this.__addToComponents("schemas", schemaValue, schemaName);
|
|
143
|
+
}
|
|
142
144
|
}
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
/**
|
|
148
|
-
* @function existsInComponents
|
|
149
|
-
* @param {string} name - The name of the Schema
|
|
150
|
-
* @returns {boolean} Whether it exists in components already
|
|
151
|
-
*/
|
|
152
|
-
__existsInComponents(name) {
|
|
153
|
-
return Boolean(this.openAPI?.components?.schemas?.[name])
|
|
145
|
+
} else {
|
|
146
|
+
this.__addToComponents("schemas", schemaValue, schemaName);
|
|
147
|
+
}
|
|
154
148
|
}
|
|
155
149
|
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
150
|
+
return `#/components/schemas/${finalName}`;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
async __dereferenceSchema(schema) {
|
|
154
|
+
const bundledSchema = await $RefParser
|
|
155
|
+
.bundle(schema, this.refParserOptions)
|
|
156
|
+
.catch((err) => {
|
|
157
|
+
throw err;
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
let deReferencedSchema = await $RefParser
|
|
161
|
+
.dereference(bundledSchema, this.refParserOptions)
|
|
162
|
+
.catch((err) => {
|
|
163
|
+
throw err;
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
// deal with schemas that have been de-referenced poorly: naive
|
|
167
|
+
if (deReferencedSchema?.$ref === "#") {
|
|
168
|
+
const oldRef = bundledSchema.$ref;
|
|
169
|
+
const path = oldRef.split("/");
|
|
170
|
+
|
|
171
|
+
const pathTitle = path[path.length - 1];
|
|
172
|
+
const referencedProperties = deReferencedSchema.definitions[pathTitle];
|
|
173
|
+
|
|
174
|
+
Object.assign(deReferencedSchema, { ...referencedProperties });
|
|
175
|
+
|
|
176
|
+
delete deReferencedSchema.$ref;
|
|
177
|
+
deReferencedSchema = await this.__dereferenceSchema(
|
|
178
|
+
deReferencedSchema
|
|
179
|
+
).catch((err) => {
|
|
180
|
+
throw err;
|
|
181
|
+
});
|
|
164
182
|
}
|
|
165
183
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
184
|
+
return deReferencedSchema;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
/**
|
|
188
|
+
* @function existsInComponents
|
|
189
|
+
* @param {string} name - The name of the Schema
|
|
190
|
+
* @returns {boolean} Whether it exists in components already
|
|
191
|
+
*/
|
|
192
|
+
__existsInComponents(name) {
|
|
193
|
+
return Boolean(this.openAPI?.components?.schemas?.[name]);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* @function isTheSameSchema
|
|
198
|
+
* @param {object} schema - The schema value
|
|
199
|
+
* @param {string} otherSchemaName - The name of the schema
|
|
200
|
+
* @returns {boolean} Whether the schema provided is the same one as in components already
|
|
201
|
+
*/
|
|
202
|
+
__isTheSameSchema(schema, otherSchemaName) {
|
|
203
|
+
return isEqual(schema, this.openAPI.components.schemas[otherSchemaName]);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* @function addToComponents
|
|
208
|
+
* @param {string} type - The component type
|
|
209
|
+
* @param {object} schema - The schema
|
|
210
|
+
* @param {string} name - The name of the schema
|
|
211
|
+
*/
|
|
212
|
+
__addToComponents(type, schema, name) {
|
|
213
|
+
const schemaObj = {
|
|
214
|
+
[name]: schema,
|
|
215
|
+
};
|
|
216
|
+
|
|
217
|
+
if (this.openAPI?.components) {
|
|
218
|
+
if (this.openAPI.components[type]) {
|
|
219
|
+
Object.assign(this.openAPI.components[type], schemaObj);
|
|
220
|
+
} else {
|
|
221
|
+
Object.assign(this.openAPI.components, { [type]: schemaObj });
|
|
222
|
+
}
|
|
223
|
+
} else {
|
|
224
|
+
const components = {
|
|
225
|
+
components: {
|
|
226
|
+
[type]: schemaObj,
|
|
227
|
+
},
|
|
228
|
+
};
|
|
229
|
+
|
|
230
|
+
Object.assign(this.openAPI, components);
|
|
192
231
|
}
|
|
232
|
+
}
|
|
193
233
|
}
|
|
194
234
|
|
|
195
235
|
module.exports = SchemaHandler;
|