serverless-openapi-documenter 0.0.113 → 0.0.115-beta.1
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
CHANGED
|
@@ -12,7 +12,9 @@
|
|
|
12
12
|
</a>
|
|
13
13
|
</p>
|
|
14
14
|
|
|
15
|
-
This will generate an OpenAPI V3 (up to v3.0.4) file for you from your serverless file. It can optionally generate a [Postman Collection V2](https://github.com/postmanlabs/openapi-to-postman) from the OpenAPI file for you too. This currently works for `http` and `httpApi` configurations.
|
|
15
|
+
This will generate an [OpenAPI V3](https://spec.openapis.org/oas/v3.0.0.html) (up to v3.0.4) specification file for you from your serverless file. It can optionally generate a [Postman Collection V2](https://github.com/postmanlabs/openapi-to-postman) from the OpenAPI file for you too. This currently works for `http` and `httpApi` configurations.
|
|
16
|
+
|
|
17
|
+
If you are using the beta of 0.0.115, it will now try and create [OpenAPI V3.1 (3.1.x)](https://spec.openapis.org/oas/v3.1.0.html) specification file for you, should you run the command `serverless openapi generate -o openapi.json -f json -a 3.1.1 -p postman.json`. Please see this [guide on migrating to V3.1](https://www.openapis.org/blog/2021/02/16/migrating-from-openapi-3-0-to-3-1-0). Whilst I perosnally use this plugin all the time, please do open and report bugs, and I will do my best to fix them.
|
|
16
18
|
|
|
17
19
|
Originally based off of: https://github.com/temando/serverless-openapi-documentation
|
|
18
20
|
|
|
@@ -318,6 +320,8 @@ custom:
|
|
|
318
320
|
|
|
319
321
|
It accepts all available Security Schemes and follows the specification: https://spec.openapis.org/oas/v3.0.4#security-scheme-object
|
|
320
322
|
|
|
323
|
+
This can be extended using the `^x-` specification extension.
|
|
324
|
+
|
|
321
325
|
#### Security on each operation
|
|
322
326
|
|
|
323
327
|
To apply an overall security scheme to all of your operations without having to add the documentation to each one, you can write it like:
|
package/package.json
CHANGED
|
@@ -920,6 +920,12 @@ class DefinitionGenerator {
|
|
|
920
920
|
break;
|
|
921
921
|
}
|
|
922
922
|
|
|
923
|
+
const extendedSpec = this.extendSpecification(securityScheme);
|
|
924
|
+
|
|
925
|
+
if (Object.keys(extendedSpec).length) {
|
|
926
|
+
Object.assign(schema, extendedSpec);
|
|
927
|
+
}
|
|
928
|
+
|
|
923
929
|
this.addToComponents(this.componentTypes.securitySchemes, schema, scheme);
|
|
924
930
|
}
|
|
925
931
|
}
|
package/src/schemaHandler.js
CHANGED
|
@@ -16,6 +16,9 @@ class SchemaHandler {
|
|
|
16
16
|
this.documentation = serverless.service.custom.documentation;
|
|
17
17
|
this.openAPI = openAPI;
|
|
18
18
|
|
|
19
|
+
this.shouldConvert = true;
|
|
20
|
+
if (/(3\.1\.\d)/g.test(this.openAPI.openapi)) this.shouldConvert = false;
|
|
21
|
+
|
|
19
22
|
this.modelReferences = {};
|
|
20
23
|
|
|
21
24
|
this.__standardiseModels();
|
|
@@ -157,18 +160,30 @@ class SchemaHandler {
|
|
|
157
160
|
}
|
|
158
161
|
);
|
|
159
162
|
|
|
160
|
-
this.
|
|
161
|
-
|
|
162
|
-
|
|
163
|
+
if (this.shouldConvert) {
|
|
164
|
+
this.logger.verbose(
|
|
165
|
+
`dereferenced model: ${JSON.stringify(dereferencedSchema)}`
|
|
166
|
+
);
|
|
163
167
|
|
|
164
|
-
|
|
165
|
-
|
|
168
|
+
this.logger.verbose(`converting model: ${name}`);
|
|
169
|
+
const convertedSchemas = SchemaConvertor.convert(
|
|
170
|
+
dereferencedSchema,
|
|
171
|
+
name
|
|
172
|
+
);
|
|
173
|
+
|
|
174
|
+
this.logger.verbose(
|
|
175
|
+
`converted schemas: ${JSON.stringify(convertedSchemas)}`
|
|
176
|
+
);
|
|
177
|
+
return convertedSchemas;
|
|
178
|
+
}
|
|
166
179
|
|
|
167
180
|
this.logger.verbose(
|
|
168
|
-
`
|
|
181
|
+
`dereferenced model: ${JSON.stringify({
|
|
182
|
+
schemas: { [name]: dereferencedSchema },
|
|
183
|
+
})}`
|
|
169
184
|
);
|
|
170
185
|
|
|
171
|
-
return
|
|
186
|
+
return { schemas: { [name]: dereferencedSchema } };
|
|
172
187
|
}
|
|
173
188
|
|
|
174
189
|
async __dereferenceSchema(schema) {
|
|
@@ -38,7 +38,7 @@ describe("DefinitionGenerator", () => {
|
|
|
38
38
|
expect(expected).to.be.an.instanceOf(DefinitionGenerator);
|
|
39
39
|
});
|
|
40
40
|
|
|
41
|
-
it("should default to version 3.0.0 of
|
|
41
|
+
it("should default to version 3.0.0 of OpenAPI when OpenAPI version is not passed in", function () {
|
|
42
42
|
const serverlessWithoutOpenAPIVersion = structuredClone(mockServerless);
|
|
43
43
|
delete serverlessWithoutOpenAPIVersion.processedInput;
|
|
44
44
|
let expected = new DefinitionGenerator(
|
|
@@ -105,7 +105,7 @@ describe("DefinitionGenerator", () => {
|
|
|
105
105
|
expect(expected.version).to.be.equal("3.0.0");
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
-
it("should respect the version of
|
|
108
|
+
it("should respect the version of OpenAPI when passed in", function () {
|
|
109
109
|
const serverlessWithOpenAPIVersion = structuredClone(mockServerless);
|
|
110
110
|
serverlessWithOpenAPIVersion.processedInput.options.openApiVersion =
|
|
111
111
|
"3.0.2";
|
|
@@ -157,7 +157,7 @@ describe("DefinitionGenerator", () => {
|
|
|
157
157
|
});
|
|
158
158
|
|
|
159
159
|
describe("createInfo", () => {
|
|
160
|
-
it("should create
|
|
160
|
+
it("should create OpenAPI info object correctly", function () {
|
|
161
161
|
const definitionGenerator = new DefinitionGenerator(
|
|
162
162
|
mockServerless,
|
|
163
163
|
logger
|
|
@@ -886,10 +886,47 @@ describe("DefinitionGenerator", () => {
|
|
|
886
886
|
).to.have.property("api_key");
|
|
887
887
|
});
|
|
888
888
|
});
|
|
889
|
+
|
|
890
|
+
describe("x-amazon-* extensions", () => {
|
|
891
|
+
it("should add an x-amazon-* security scheme to components", function () {
|
|
892
|
+
mockServerless.service.custom.documentation.securitySchemes = {
|
|
893
|
+
x_amazon_api_key: {
|
|
894
|
+
type: "apiKey",
|
|
895
|
+
name: "x-amz-security-token",
|
|
896
|
+
in: "header",
|
|
897
|
+
"x-amazon-apigateway-authtype": "awsSigv4",
|
|
898
|
+
},
|
|
899
|
+
};
|
|
900
|
+
|
|
901
|
+
const definitionGenerator = new DefinitionGenerator(
|
|
902
|
+
mockServerless,
|
|
903
|
+
logger
|
|
904
|
+
);
|
|
905
|
+
definitionGenerator.createSecuritySchemes(
|
|
906
|
+
mockServerless.service.custom.documentation.securitySchemes
|
|
907
|
+
);
|
|
908
|
+
|
|
909
|
+
expect(definitionGenerator.openAPI).to.be.an("object");
|
|
910
|
+
expect(definitionGenerator.openAPI.components).to.be.an("object");
|
|
911
|
+
expect(definitionGenerator.openAPI.components).to.have.property(
|
|
912
|
+
"securitySchemes"
|
|
913
|
+
);
|
|
914
|
+
expect(definitionGenerator.openAPI.components.securitySchemes).to.be.an(
|
|
915
|
+
"object"
|
|
916
|
+
);
|
|
917
|
+
expect(
|
|
918
|
+
definitionGenerator.openAPI.components.securitySchemes
|
|
919
|
+
).to.have.property("x_amazon_api_key");
|
|
920
|
+
expect(
|
|
921
|
+
definitionGenerator.openAPI.components.securitySchemes
|
|
922
|
+
.x_amazon_api_key
|
|
923
|
+
).to.have.property("x-amazon-apigateway-authtype");
|
|
924
|
+
});
|
|
925
|
+
});
|
|
889
926
|
});
|
|
890
927
|
|
|
891
928
|
describe("createTags", () => {
|
|
892
|
-
it("should add tags to the
|
|
929
|
+
it("should add tags to the OpenAPI object correctly", function () {
|
|
893
930
|
mockServerless.service.custom.documentation.tags = [{ name: "tag1" }];
|
|
894
931
|
|
|
895
932
|
const definitionGenerator = new DefinitionGenerator(
|
|
@@ -68,7 +68,7 @@ describe("OpenAPIGenerator", () => {
|
|
|
68
68
|
});
|
|
69
69
|
|
|
70
70
|
describe("generationAndValidation", () => {
|
|
71
|
-
it("should correctly generate a valid
|
|
71
|
+
it("should correctly generate a valid OpenAPI document", async function () {
|
|
72
72
|
const succSpy = sinon.spy(logOutput.log, "success");
|
|
73
73
|
const errSpy = sinon.spy(logOutput.log, "error");
|
|
74
74
|
|
|
@@ -99,7 +99,7 @@ describe("OpenAPIGenerator", () => {
|
|
|
99
99
|
getFuncStub.reset();
|
|
100
100
|
});
|
|
101
101
|
|
|
102
|
-
xit("should throw an error when trying to generate an invalid
|
|
102
|
+
xit("should throw an error when trying to generate an invalid OpenAPI document", async function () {
|
|
103
103
|
const succSpy = sinon.spy(logOutput.log, "success");
|
|
104
104
|
const errSpy = sinon.spy(logOutput.log, "error");
|
|
105
105
|
|
|
@@ -135,7 +135,7 @@ describe("OpenAPIGenerator", () => {
|
|
|
135
135
|
getFuncStub.reset();
|
|
136
136
|
});
|
|
137
137
|
|
|
138
|
-
it("should correctly validate a valid
|
|
138
|
+
it("should correctly validate a valid OpenAPI document", async function () {
|
|
139
139
|
const succSpy = sinon.spy(logOutput.log, "success");
|
|
140
140
|
const errSpy = sinon.spy(logOutput.log, "error");
|
|
141
141
|
|
|
@@ -168,7 +168,7 @@ describe("OpenAPIGenerator", () => {
|
|
|
168
168
|
getFuncStub.reset();
|
|
169
169
|
});
|
|
170
170
|
|
|
171
|
-
it("should throw an error when trying to validate an invalid
|
|
171
|
+
it("should throw an error when trying to validate an invalid OpenAPI document", async function () {
|
|
172
172
|
const succSpy = sinon.spy(logOutput.log, "success");
|
|
173
173
|
const errSpy = sinon.spy(logOutput.log, "error");
|
|
174
174
|
|
|
@@ -212,7 +212,7 @@ describe("OpenAPIGenerator", () => {
|
|
|
212
212
|
});
|
|
213
213
|
|
|
214
214
|
describe("createPostman", () => {
|
|
215
|
-
it("should generate a postman collection when a valid
|
|
215
|
+
it("should generate a postman collection when a valid OpenAPI file is generated", function () {
|
|
216
216
|
const fsStub = sinon.stub(fs, "writeFileSync").returns(true);
|
|
217
217
|
const succSpy = sinon.spy(logOutput.log, "success");
|
|
218
218
|
const errSpy = sinon.spy(logOutput.log, "error");
|
|
@@ -4,7 +4,9 @@ const fs = require("fs").promises;
|
|
|
4
4
|
const path = require("path");
|
|
5
5
|
|
|
6
6
|
const expect = require("chai").expect;
|
|
7
|
+
const SchemaConvertor = require("json-schema-for-openapi");
|
|
7
8
|
const nock = require("nock");
|
|
9
|
+
const sinon = require("sinon");
|
|
8
10
|
|
|
9
11
|
const modelsDocumentOG = require("../models/models/models.json");
|
|
10
12
|
const modelsAltDocumentOG = require("../models/models/models-alt.json");
|
|
@@ -36,7 +38,7 @@ describe(`SchemaHandler`, function () {
|
|
|
36
38
|
);
|
|
37
39
|
|
|
38
40
|
const openAPISchema = {
|
|
39
|
-
|
|
41
|
+
openapi: "3.0.3",
|
|
40
42
|
components: {
|
|
41
43
|
schemas: {},
|
|
42
44
|
},
|
|
@@ -277,7 +279,7 @@ describe(`SchemaHandler`, function () {
|
|
|
277
279
|
|
|
278
280
|
describe(`addModelsToOpenAPI`, function () {
|
|
279
281
|
describe(`embedded simple schemas`, function () {
|
|
280
|
-
it(`should add the model to the
|
|
282
|
+
it(`should add the model to the OpenAPI schema`, async function () {
|
|
281
283
|
Object.assign(
|
|
282
284
|
mockServerless.service.custom.documentation,
|
|
283
285
|
modelsDocument
|
|
@@ -306,7 +308,7 @@ describe(`SchemaHandler`, function () {
|
|
|
306
308
|
});
|
|
307
309
|
});
|
|
308
310
|
|
|
309
|
-
it(`should add a model with references to the
|
|
311
|
+
it(`should add a model with references to the OpenAPI schema`, async function () {
|
|
310
312
|
Object.assign(
|
|
311
313
|
mockServerless.service.custom.documentation,
|
|
312
314
|
modelsDocument
|
|
@@ -379,7 +381,7 @@ describe(`SchemaHandler`, function () {
|
|
|
379
381
|
});
|
|
380
382
|
});
|
|
381
383
|
|
|
382
|
-
it(`should add a model with poorly dereferenced references to the
|
|
384
|
+
it(`should add a model with poorly dereferenced references to the OpenAPI schema`, async function () {
|
|
383
385
|
Object.assign(
|
|
384
386
|
mockServerless.service.custom.documentation,
|
|
385
387
|
modelsDocument
|
|
@@ -446,7 +448,7 @@ describe(`SchemaHandler`, function () {
|
|
|
446
448
|
});
|
|
447
449
|
|
|
448
450
|
describe(`component references`, function () {
|
|
449
|
-
it(`should add schemas with component references to the
|
|
451
|
+
it(`should add schemas with component references to the OpenAPI schema`, async function () {
|
|
450
452
|
Object.assign(
|
|
451
453
|
mockServerless.service.custom.documentation,
|
|
452
454
|
modelsDocument
|
|
@@ -517,10 +519,86 @@ describe(`SchemaHandler`, function () {
|
|
|
517
519
|
type: "string",
|
|
518
520
|
});
|
|
519
521
|
});
|
|
522
|
+
|
|
523
|
+
it(`should add schemas with component references to the OpenAPI schema using OpenAPI 3.1`, async function () {
|
|
524
|
+
Object.assign(
|
|
525
|
+
mockServerless.service.custom.documentation,
|
|
526
|
+
modelsDocument
|
|
527
|
+
);
|
|
528
|
+
|
|
529
|
+
mockServerless.service.custom.documentation.models.push({
|
|
530
|
+
name: "SuccessResponse",
|
|
531
|
+
contentType: "application/json",
|
|
532
|
+
schema: {
|
|
533
|
+
type: "array",
|
|
534
|
+
items: {
|
|
535
|
+
$ref: "#/components/schemas/Agency",
|
|
536
|
+
},
|
|
537
|
+
},
|
|
538
|
+
});
|
|
539
|
+
|
|
540
|
+
mockServerless.service.custom.documentation.models.push({
|
|
541
|
+
name: "Agency",
|
|
542
|
+
contentType: "application/json",
|
|
543
|
+
schema: {
|
|
544
|
+
type: "string",
|
|
545
|
+
},
|
|
546
|
+
});
|
|
547
|
+
|
|
548
|
+
openAPI.openapi = "3.1.0";
|
|
549
|
+
|
|
550
|
+
const schemaHandler = new SchemaHandler(
|
|
551
|
+
mockServerless,
|
|
552
|
+
openAPI,
|
|
553
|
+
logger
|
|
554
|
+
);
|
|
555
|
+
|
|
556
|
+
await schemaHandler.addModelsToOpenAPI();
|
|
557
|
+
|
|
558
|
+
expect(schemaHandler.openAPI).to.have.property("components");
|
|
559
|
+
expect(schemaHandler.openAPI.components).to.have.property("schemas");
|
|
560
|
+
expect(schemaHandler.openAPI.components.schemas).to.have.property(
|
|
561
|
+
"ErrorResponse"
|
|
562
|
+
);
|
|
563
|
+
expect(
|
|
564
|
+
schemaHandler.openAPI.components.schemas.ErrorResponse
|
|
565
|
+
).to.be.an("object");
|
|
566
|
+
expect(
|
|
567
|
+
schemaHandler.openAPI.components.schemas.ErrorResponse
|
|
568
|
+
).to.be.eql({
|
|
569
|
+
type: "object",
|
|
570
|
+
properties: { error: { type: "string" } },
|
|
571
|
+
});
|
|
572
|
+
|
|
573
|
+
expect(schemaHandler.openAPI.components.schemas).to.have.property(
|
|
574
|
+
"SuccessResponse"
|
|
575
|
+
);
|
|
576
|
+
expect(
|
|
577
|
+
schemaHandler.openAPI.components.schemas.SuccessResponse
|
|
578
|
+
).to.be.an("object");
|
|
579
|
+
expect(
|
|
580
|
+
schemaHandler.openAPI.components.schemas.SuccessResponse
|
|
581
|
+
).to.be.eql({
|
|
582
|
+
type: "array",
|
|
583
|
+
items: {
|
|
584
|
+
$ref: "#/components/schemas/Agency",
|
|
585
|
+
},
|
|
586
|
+
});
|
|
587
|
+
|
|
588
|
+
expect(schemaHandler.openAPI.components.schemas).to.have.property(
|
|
589
|
+
"Agency"
|
|
590
|
+
);
|
|
591
|
+
expect(schemaHandler.openAPI.components.schemas.Agency).to.be.an(
|
|
592
|
+
"object"
|
|
593
|
+
);
|
|
594
|
+
expect(schemaHandler.openAPI.components.schemas.Agency).to.be.eql({
|
|
595
|
+
type: "string",
|
|
596
|
+
});
|
|
597
|
+
});
|
|
520
598
|
});
|
|
521
599
|
|
|
522
600
|
describe(`other references`, function () {
|
|
523
|
-
it(`should add a model that is a webUrl to the
|
|
601
|
+
it(`should add a model that is a webUrl to the OpenAPI schema`, async function () {
|
|
524
602
|
Object.assign(
|
|
525
603
|
mockServerless.service.custom.documentation,
|
|
526
604
|
modelsDocument
|
|
@@ -589,7 +667,7 @@ describe(`SchemaHandler`, function () {
|
|
|
589
667
|
});
|
|
590
668
|
});
|
|
591
669
|
|
|
592
|
-
it(`should add a complex model that is a webUrl to the
|
|
670
|
+
it(`should add a complex model that is a webUrl to the OpenAPI schema`, async function () {
|
|
593
671
|
Object.assign(
|
|
594
672
|
mockServerless.service.custom.documentation,
|
|
595
673
|
modelsDocument
|
|
@@ -710,6 +788,28 @@ describe(`SchemaHandler`, function () {
|
|
|
710
788
|
});
|
|
711
789
|
|
|
712
790
|
describe(`createSchema`, function () {
|
|
791
|
+
it(`does not convert schemas when using OpenAPI 3.1.x`, async function () {
|
|
792
|
+
Object.assign(
|
|
793
|
+
mockServerless.service.custom.documentation,
|
|
794
|
+
modelsDocument
|
|
795
|
+
);
|
|
796
|
+
|
|
797
|
+
openAPI.openapi = "3.1.0";
|
|
798
|
+
|
|
799
|
+
const schemaHandler = new SchemaHandler(mockServerless, openAPI, logger);
|
|
800
|
+
|
|
801
|
+
const spy = sinon.spy(SchemaConvertor, "convert");
|
|
802
|
+
|
|
803
|
+
await schemaHandler.addModelsToOpenAPI();
|
|
804
|
+
|
|
805
|
+
const expected = await schemaHandler.createSchema("ErrorResponse");
|
|
806
|
+
|
|
807
|
+
expect(expected).to.be.equal("#/components/schemas/ErrorResponse");
|
|
808
|
+
expect(spy.calledOnce).to.be.false;
|
|
809
|
+
|
|
810
|
+
spy.restore();
|
|
811
|
+
});
|
|
812
|
+
|
|
713
813
|
it(`returns a reference to the schema when the schema already exists in components and we don't pass through a schema`, async function () {
|
|
714
814
|
Object.assign(
|
|
715
815
|
mockServerless.service.custom.documentation,
|