Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@
</a>
</p>

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

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.

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

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "serverless-openapi-documenter",
"version": "0.0.114",
"version": "0.0.115-beta.1",
"description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config",
"main": "index.js",
"keywords": [
Expand Down
29 changes: 22 additions & 7 deletions src/schemaHandler.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ class SchemaHandler {
this.documentation = serverless.service.custom.documentation;
this.openAPI = openAPI;

this.shouldConvert = true;
if (/(3\.1\.\d)/g.test(this.openAPI.openapi)) this.shouldConvert = false;

this.modelReferences = {};

this.__standardiseModels();
Expand Down Expand Up @@ -157,18 +160,30 @@ class SchemaHandler {
}
);

this.logger.verbose(
`dereferenced model: ${JSON.stringify(dereferencedSchema)}`
);
if (this.shouldConvert) {
this.logger.verbose(
`dereferenced model: ${JSON.stringify(dereferencedSchema)}`
);

this.logger.verbose(`converting model: ${name}`);
const convertedSchemas = SchemaConvertor.convert(dereferencedSchema, name);
this.logger.verbose(`converting model: ${name}`);
const convertedSchemas = SchemaConvertor.convert(
dereferencedSchema,
name
);

this.logger.verbose(
`converted schemas: ${JSON.stringify(convertedSchemas)}`
);
return convertedSchemas;
}

this.logger.verbose(
`converted schemas: ${JSON.stringify(convertedSchemas)}`
`dereferenced model: ${JSON.stringify({
schemas: { [name]: dereferencedSchema },
})}`
);

return convertedSchemas;
return { schemas: { [name]: dereferencedSchema } };
}

async __dereferenceSchema(schema) {
Expand Down
11 changes: 6 additions & 5 deletions test/unit/definitionGenerator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ describe("DefinitionGenerator", () => {
expect(expected).to.be.an.instanceOf(DefinitionGenerator);
});

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

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

describe("createInfo", () => {
it("should create openAPI info object correctly", function () {
it("should create OpenAPI info object correctly", function () {
const definitionGenerator = new DefinitionGenerator(
mockServerless,
logger
Expand Down Expand Up @@ -918,14 +918,15 @@ describe("DefinitionGenerator", () => {
definitionGenerator.openAPI.components.securitySchemes
).to.have.property("x_amazon_api_key");
expect(
definitionGenerator.openAPI.components.securitySchemes.x_amazon_api_key
definitionGenerator.openAPI.components.securitySchemes
.x_amazon_api_key
).to.have.property("x-amazon-apigateway-authtype");
});
});
});

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

const definitionGenerator = new DefinitionGenerator(
Expand Down
10 changes: 5 additions & 5 deletions test/unit/openAPIGenerator.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ describe("OpenAPIGenerator", () => {
});

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

Expand Down Expand Up @@ -99,7 +99,7 @@ describe("OpenAPIGenerator", () => {
getFuncStub.reset();
});

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

Expand Down Expand Up @@ -135,7 +135,7 @@ describe("OpenAPIGenerator", () => {
getFuncStub.reset();
});

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

Expand Down Expand Up @@ -168,7 +168,7 @@ describe("OpenAPIGenerator", () => {
getFuncStub.reset();
});

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

Expand Down Expand Up @@ -212,7 +212,7 @@ describe("OpenAPIGenerator", () => {
});

describe("createPostman", () => {
it("should generate a postman collection when a valid openAPI file is generated", function () {
it("should generate a postman collection when a valid OpenAPI file is generated", function () {
const fsStub = sinon.stub(fs, "writeFileSync").returns(true);
const succSpy = sinon.spy(logOutput.log, "success");
const errSpy = sinon.spy(logOutput.log, "error");
Expand Down
114 changes: 107 additions & 7 deletions test/unit/schemaHandler.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@ const fs = require("fs").promises;
const path = require("path");

const expect = require("chai").expect;
const SchemaConvertor = require("json-schema-for-openapi");
const nock = require("nock");
const sinon = require("sinon");

const modelsDocumentOG = require("../models/models/models.json");
const modelsAltDocumentOG = require("../models/models/models-alt.json");
Expand Down Expand Up @@ -36,7 +38,7 @@ describe(`SchemaHandler`, function () {
);

const openAPISchema = {
version: "3.0.3",
openapi: "3.0.3",
components: {
schemas: {},
},
Expand Down Expand Up @@ -277,7 +279,7 @@ describe(`SchemaHandler`, function () {

describe(`addModelsToOpenAPI`, function () {
describe(`embedded simple schemas`, function () {
it(`should add the model to the openAPI schema`, async function () {
it(`should add the model to the OpenAPI schema`, async function () {
Object.assign(
mockServerless.service.custom.documentation,
modelsDocument
Expand Down Expand Up @@ -306,7 +308,7 @@ describe(`SchemaHandler`, function () {
});
});

it(`should add a model with references to the openAPI schema`, async function () {
it(`should add a model with references to the OpenAPI schema`, async function () {
Object.assign(
mockServerless.service.custom.documentation,
modelsDocument
Expand Down Expand Up @@ -379,7 +381,7 @@ describe(`SchemaHandler`, function () {
});
});

it(`should add a model with poorly dereferenced references to the openAPI schema`, async function () {
it(`should add a model with poorly dereferenced references to the OpenAPI schema`, async function () {
Object.assign(
mockServerless.service.custom.documentation,
modelsDocument
Expand Down Expand Up @@ -446,7 +448,7 @@ describe(`SchemaHandler`, function () {
});

describe(`component references`, function () {
it(`should add schemas with component references to the openAPI schema`, async function () {
it(`should add schemas with component references to the OpenAPI schema`, async function () {
Object.assign(
mockServerless.service.custom.documentation,
modelsDocument
Expand Down Expand Up @@ -517,10 +519,86 @@ describe(`SchemaHandler`, function () {
type: "string",
});
});

it(`should add schemas with component references to the OpenAPI schema using OpenAPI 3.1`, async function () {
Object.assign(
mockServerless.service.custom.documentation,
modelsDocument
);

mockServerless.service.custom.documentation.models.push({
name: "SuccessResponse",
contentType: "application/json",
schema: {
type: "array",
items: {
$ref: "#/components/schemas/Agency",
},
},
});

mockServerless.service.custom.documentation.models.push({
name: "Agency",
contentType: "application/json",
schema: {
type: "string",
},
});

openAPI.openapi = "3.1.0";

const schemaHandler = new SchemaHandler(
mockServerless,
openAPI,
logger
);

await schemaHandler.addModelsToOpenAPI();

expect(schemaHandler.openAPI).to.have.property("components");
expect(schemaHandler.openAPI.components).to.have.property("schemas");
expect(schemaHandler.openAPI.components.schemas).to.have.property(
"ErrorResponse"
);
expect(
schemaHandler.openAPI.components.schemas.ErrorResponse
).to.be.an("object");
expect(
schemaHandler.openAPI.components.schemas.ErrorResponse
).to.be.eql({
type: "object",
properties: { error: { type: "string" } },
});

expect(schemaHandler.openAPI.components.schemas).to.have.property(
"SuccessResponse"
);
expect(
schemaHandler.openAPI.components.schemas.SuccessResponse
).to.be.an("object");
expect(
schemaHandler.openAPI.components.schemas.SuccessResponse
).to.be.eql({
type: "array",
items: {
$ref: "#/components/schemas/Agency",
},
});

expect(schemaHandler.openAPI.components.schemas).to.have.property(
"Agency"
);
expect(schemaHandler.openAPI.components.schemas.Agency).to.be.an(
"object"
);
expect(schemaHandler.openAPI.components.schemas.Agency).to.be.eql({
type: "string",
});
});
});

describe(`other references`, function () {
it(`should add a model that is a webUrl to the openAPI schema`, async function () {
it(`should add a model that is a webUrl to the OpenAPI schema`, async function () {
Object.assign(
mockServerless.service.custom.documentation,
modelsDocument
Expand Down Expand Up @@ -589,7 +667,7 @@ describe(`SchemaHandler`, function () {
});
});

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

describe(`createSchema`, function () {
it(`does not convert schemas when using OpenAPI 3.1.x`, async function () {
Object.assign(
mockServerless.service.custom.documentation,
modelsDocument
);

openAPI.openapi = "3.1.0";

const schemaHandler = new SchemaHandler(mockServerless, openAPI, logger);

const spy = sinon.spy(SchemaConvertor, "convert");

await schemaHandler.addModelsToOpenAPI();

const expected = await schemaHandler.createSchema("ErrorResponse");

expect(expected).to.be.equal("#/components/schemas/ErrorResponse");
expect(spy.calledOnce).to.be.false;

spy.restore();
});

it(`returns a reference to the schema when the schema already exists in components and we don't pass through a schema`, async function () {
Object.assign(
mockServerless.service.custom.documentation,
Expand Down