Skip to content

Commit ef47e30

Browse files
authored
Merge pull request #251 from JaredCE/3.1.x-pre-release
3.1.x pre release
2 parents 352b5bc + b2474e2 commit ef47e30

File tree

7 files changed

+146
-28
lines changed

7 files changed

+146
-28
lines changed

README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,9 @@
1212
</a>
1313
</p>
1414

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.
1618

1719
Originally based off of: https://github.com/temando/serverless-openapi-documentation
1820

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "serverless-openapi-documenter",
3-
"version": "0.0.114",
3+
"version": "0.0.115-beta.1",
44
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
55
"main": "index.js",
66
"keywords": [

src/schemaHandler.js

Lines changed: 22 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@ class SchemaHandler {
1616
this.documentation = serverless.service.custom.documentation;
1717
this.openAPI = openAPI;
1818

19+
this.shouldConvert = true;
20+
if (/(3\.1\.\d)/g.test(this.openAPI.openapi)) this.shouldConvert = false;
21+
1922
this.modelReferences = {};
2023

2124
this.__standardiseModels();
@@ -157,18 +160,30 @@ class SchemaHandler {
157160
}
158161
);
159162

160-
this.logger.verbose(
161-
`dereferenced model: ${JSON.stringify(dereferencedSchema)}`
162-
);
163+
if (this.shouldConvert) {
164+
this.logger.verbose(
165+
`dereferenced model: ${JSON.stringify(dereferencedSchema)}`
166+
);
163167

164-
this.logger.verbose(`converting model: ${name}`);
165-
const convertedSchemas = SchemaConvertor.convert(dereferencedSchema, name);
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+
}
166179

167180
this.logger.verbose(
168-
`converted schemas: ${JSON.stringify(convertedSchemas)}`
181+
`dereferenced model: ${JSON.stringify({
182+
schemas: { [name]: dereferencedSchema },
183+
})}`
169184
);
170185

171-
return convertedSchemas;
186+
return { schemas: { [name]: dereferencedSchema } };
172187
}
173188

174189
async __dereferenceSchema(schema) {

test/unit/definitionGenerator.spec.js

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ describe("DefinitionGenerator", () => {
3838
expect(expected).to.be.an.instanceOf(DefinitionGenerator);
3939
});
4040

41-
it("should default to version 3.0.0 of openAPI when openAPI version is not passed in", function () {
41+
it("should default to version 3.0.0 of OpenAPI when OpenAPI version is not passed in", function () {
4242
const serverlessWithoutOpenAPIVersion = structuredClone(mockServerless);
4343
delete serverlessWithoutOpenAPIVersion.processedInput;
4444
let expected = new DefinitionGenerator(
@@ -105,7 +105,7 @@ describe("DefinitionGenerator", () => {
105105
expect(expected.version).to.be.equal("3.0.0");
106106
});
107107

108-
it("should respect the version of openAPI when passed in", function () {
108+
it("should respect the version of OpenAPI when passed in", function () {
109109
const serverlessWithOpenAPIVersion = structuredClone(mockServerless);
110110
serverlessWithOpenAPIVersion.processedInput.options.openApiVersion =
111111
"3.0.2";
@@ -157,7 +157,7 @@ describe("DefinitionGenerator", () => {
157157
});
158158

159159
describe("createInfo", () => {
160-
it("should create openAPI info object correctly", function () {
160+
it("should create OpenAPI info object correctly", function () {
161161
const definitionGenerator = new DefinitionGenerator(
162162
mockServerless,
163163
logger
@@ -918,14 +918,15 @@ describe("DefinitionGenerator", () => {
918918
definitionGenerator.openAPI.components.securitySchemes
919919
).to.have.property("x_amazon_api_key");
920920
expect(
921-
definitionGenerator.openAPI.components.securitySchemes.x_amazon_api_key
921+
definitionGenerator.openAPI.components.securitySchemes
922+
.x_amazon_api_key
922923
).to.have.property("x-amazon-apigateway-authtype");
923924
});
924925
});
925926
});
926927

927928
describe("createTags", () => {
928-
it("should add tags to the openAPI object correctly", function () {
929+
it("should add tags to the OpenAPI object correctly", function () {
929930
mockServerless.service.custom.documentation.tags = [{ name: "tag1" }];
930931

931932
const definitionGenerator = new DefinitionGenerator(

test/unit/openAPIGenerator.spec.js

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ describe("OpenAPIGenerator", () => {
6868
});
6969

7070
describe("generationAndValidation", () => {
71-
it("should correctly generate a valid openAPI document", async function () {
71+
it("should correctly generate a valid OpenAPI document", async function () {
7272
const succSpy = sinon.spy(logOutput.log, "success");
7373
const errSpy = sinon.spy(logOutput.log, "error");
7474

@@ -99,7 +99,7 @@ describe("OpenAPIGenerator", () => {
9999
getFuncStub.reset();
100100
});
101101

102-
xit("should throw an error when trying to generate an invalid openAPI document", async function () {
102+
xit("should throw an error when trying to generate an invalid OpenAPI document", async function () {
103103
const succSpy = sinon.spy(logOutput.log, "success");
104104
const errSpy = sinon.spy(logOutput.log, "error");
105105

@@ -135,7 +135,7 @@ describe("OpenAPIGenerator", () => {
135135
getFuncStub.reset();
136136
});
137137

138-
it("should correctly validate a valid openAPI document", async function () {
138+
it("should correctly validate a valid OpenAPI document", async function () {
139139
const succSpy = sinon.spy(logOutput.log, "success");
140140
const errSpy = sinon.spy(logOutput.log, "error");
141141

@@ -168,7 +168,7 @@ describe("OpenAPIGenerator", () => {
168168
getFuncStub.reset();
169169
});
170170

171-
it("should throw an error when trying to validate an invalid openAPI document", async function () {
171+
it("should throw an error when trying to validate an invalid OpenAPI document", async function () {
172172
const succSpy = sinon.spy(logOutput.log, "success");
173173
const errSpy = sinon.spy(logOutput.log, "error");
174174

@@ -212,7 +212,7 @@ describe("OpenAPIGenerator", () => {
212212
});
213213

214214
describe("createPostman", () => {
215-
it("should generate a postman collection when a valid openAPI file is generated", function () {
215+
it("should generate a postman collection when a valid OpenAPI file is generated", function () {
216216
const fsStub = sinon.stub(fs, "writeFileSync").returns(true);
217217
const succSpy = sinon.spy(logOutput.log, "success");
218218
const errSpy = sinon.spy(logOutput.log, "error");

test/unit/schemaHandler.spec.js

Lines changed: 107 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ const fs = require("fs").promises;
44
const path = require("path");
55

66
const expect = require("chai").expect;
7+
const SchemaConvertor = require("json-schema-for-openapi");
78
const nock = require("nock");
9+
const sinon = require("sinon");
810

911
const modelsDocumentOG = require("../models/models/models.json");
1012
const modelsAltDocumentOG = require("../models/models/models-alt.json");
@@ -36,7 +38,7 @@ describe(`SchemaHandler`, function () {
3638
);
3739

3840
const openAPISchema = {
39-
version: "3.0.3",
41+
openapi: "3.0.3",
4042
components: {
4143
schemas: {},
4244
},
@@ -277,7 +279,7 @@ describe(`SchemaHandler`, function () {
277279

278280
describe(`addModelsToOpenAPI`, function () {
279281
describe(`embedded simple schemas`, function () {
280-
it(`should add the model to the openAPI schema`, async function () {
282+
it(`should add the model to the OpenAPI schema`, async function () {
281283
Object.assign(
282284
mockServerless.service.custom.documentation,
283285
modelsDocument
@@ -306,7 +308,7 @@ describe(`SchemaHandler`, function () {
306308
});
307309
});
308310

309-
it(`should add a model with references to the openAPI schema`, async function () {
311+
it(`should add a model with references to the OpenAPI schema`, async function () {
310312
Object.assign(
311313
mockServerless.service.custom.documentation,
312314
modelsDocument
@@ -379,7 +381,7 @@ describe(`SchemaHandler`, function () {
379381
});
380382
});
381383

382-
it(`should add a model with poorly dereferenced references to the openAPI schema`, async function () {
384+
it(`should add a model with poorly dereferenced references to the OpenAPI schema`, async function () {
383385
Object.assign(
384386
mockServerless.service.custom.documentation,
385387
modelsDocument
@@ -446,7 +448,7 @@ describe(`SchemaHandler`, function () {
446448
});
447449

448450
describe(`component references`, function () {
449-
it(`should add schemas with component references to the openAPI schema`, async function () {
451+
it(`should add schemas with component references to the OpenAPI schema`, async function () {
450452
Object.assign(
451453
mockServerless.service.custom.documentation,
452454
modelsDocument
@@ -517,10 +519,86 @@ describe(`SchemaHandler`, function () {
517519
type: "string",
518520
});
519521
});
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+
});
520598
});
521599

522600
describe(`other references`, function () {
523-
it(`should add a model that is a webUrl to the openAPI schema`, async function () {
601+
it(`should add a model that is a webUrl to the OpenAPI schema`, async function () {
524602
Object.assign(
525603
mockServerless.service.custom.documentation,
526604
modelsDocument
@@ -589,7 +667,7 @@ describe(`SchemaHandler`, function () {
589667
});
590668
});
591669

592-
it(`should add a complex model that is a webUrl to the openAPI schema`, async function () {
670+
it(`should add a complex model that is a webUrl to the OpenAPI schema`, async function () {
593671
Object.assign(
594672
mockServerless.service.custom.documentation,
595673
modelsDocument
@@ -710,6 +788,28 @@ describe(`SchemaHandler`, function () {
710788
});
711789

712790
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+
713813
it(`returns a reference to the schema when the schema already exists in components and we don't pass through a schema`, async function () {
714814
Object.assign(
715815
mockServerless.service.custom.documentation,

0 commit comments

Comments
 (0)