diff --git a/package-lock.json b/package-lock.json index 25093a3..cfeb455 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "serverless-openapi-documenter", - "version": "0.0.112", + "version": "0.0.113", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "serverless-openapi-documenter", - "version": "0.0.112", + "version": "0.0.113", "license": "MIT", "dependencies": { "@apidevtools/json-schema-ref-parser": "^9.1.0", diff --git a/package.json b/package.json index 3aa6ac4..4ec37dd 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "serverless-openapi-documenter", - "version": "0.0.112", + "version": "0.0.113", "description": "Generate OpenAPI v3 documentation and Postman Collections from your Serverless Config", "main": "index.js", "keywords": [ diff --git a/src/definitionGenerator.js b/src/definitionGenerator.js index 9730932..9bfb223 100644 --- a/src/definitionGenerator.js +++ b/src/definitionGenerator.js @@ -624,7 +624,7 @@ class DefinitionGenerator { continue; } - const obj = JSON.parse(JSON.stringify(this.DEFAULT_CORS_HEADERS[key])); + const obj = structuredClone(this.DEFAULT_CORS_HEADERS[key]); if (key === "Access-Control-Allow-Origin") { if ( diff --git a/src/openAPIGenerator.js b/src/openAPIGenerator.js index 5d7aa63..7beab14 100644 --- a/src/openAPIGenerator.js +++ b/src/openAPIGenerator.js @@ -241,7 +241,7 @@ class OpenAPIGenerator { }; PostmanGenerator.convert( - { type: "json", data: JSON.parse(JSON.stringify(openAPI)) }, + { type: "json", data: structuredClone(openAPI) }, {}, postmanGeneration ); diff --git a/src/schemaHandler.js b/src/schemaHandler.js index 62107be..d66bc61 100644 --- a/src/schemaHandler.js +++ b/src/schemaHandler.js @@ -71,26 +71,15 @@ class SchemaHandler { const modelName = model.name; const modelSchema = model.schema; - this.logger.verbose(`dereferencing model: ${model.name}`); - const dereferencedSchema = await this.__dereferenceSchema( - modelSchema + const convertedSchemas = await this.__dereferenceAndConvert( + modelSchema, + modelName, + model ).catch((err) => { - if (err.errors) { - for (const error of err?.errors) { - this.__HTTPError(error, model); - } - } else { - this.__HTTPError(err, model); - } - return modelSchema; + if (err instanceof Error) throw err; + else return err; }); - this.logger.verbose(`convering model: ${model.name}`); - const convertedSchemas = SchemaConvertor.convert( - dereferencedSchema, - modelName - ); - if ( typeof convertedSchemas.schemas === "object" && !Array.isArray(convertedSchemas.schemas) && @@ -129,13 +118,12 @@ class SchemaHandler { return this.modelReferences[name]; } - const dereferencedSchema = await this.__dereferenceSchema(schema).catch( - (err) => { - throw err; - } - ); - - const convertedSchemas = SchemaConvertor.convert(dereferencedSchema, name); + const convertedSchemas = await this.__dereferenceAndConvert(schema, name, { + name, + schema, + }).catch((err) => { + throw err; + }); for (const [schemaName, schemaValue] of Object.entries( convertedSchemas.schemas @@ -157,6 +145,32 @@ class SchemaHandler { return `#/components/schemas/${finalName}`; } + async __dereferenceAndConvert(schema, name, model) { + this.logger.verbose(`dereferencing model: ${name}`); + const dereferencedSchema = await this.__dereferenceSchema(schema).catch( + (err) => { + this.__checkForHTTPErrorsAndThrow(err, model); + + this.__checkForMissingPathAndThrow(err); + + return schema; + } + ); + + this.logger.verbose( + `dereferenced model: ${JSON.stringify(dereferencedSchema)}` + ); + + this.logger.verbose(`converting model: ${name}`); + const convertedSchemas = SchemaConvertor.convert(dereferencedSchema, name); + + this.logger.verbose( + `converted schemas: ${JSON.stringify(convertedSchemas)}` + ); + + return convertedSchemas; + } + async __dereferenceSchema(schema) { const bundledSchema = await $RefParser .bundle(schema, this.refParserOptions) @@ -238,9 +252,23 @@ class SchemaHandler { } } + __checkForMissingPathAndThrow(error) { + if (error.message === "Expected a file path, URL, or object. Got undefined") + throw error; + } + + __checkForHTTPErrorsAndThrow(error, model) { + if (error.errors) { + for (const err of error?.errors) { + this.__HTTPError(err, model); + } + } else { + this.__HTTPError(error, model); + } + } + __HTTPError(error, model) { if (error.message.includes("HTTP ERROR")) { - // throw err; throw new Error( `There was an error dereferencing ${ model.name diff --git a/test/unit/definitionGenerator.spec.js b/test/unit/definitionGenerator.spec.js index 6b6eb39..8f94c0a 100644 --- a/test/unit/definitionGenerator.spec.js +++ b/test/unit/definitionGenerator.spec.js @@ -23,7 +23,7 @@ describe("DefinitionGenerator", () => { ); beforeEach(function () { - mockServerless = JSON.parse(JSON.stringify(serverlessMock)); + mockServerless = structuredClone(serverlessMock); Object.assign(mockServerless.service.custom.documentation, modelsDocument); }); @@ -39,9 +39,7 @@ describe("DefinitionGenerator", () => { }); it("should default to version 3.0.0 of openAPI when openAPI version is not passed in", function () { - const serverlessWithoutOpenAPIVersion = JSON.parse( - JSON.stringify(mockServerless) - ); + const serverlessWithoutOpenAPIVersion = structuredClone(mockServerless); delete serverlessWithoutOpenAPIVersion.processedInput; let expected = new DefinitionGenerator( serverlessWithoutOpenAPIVersion, @@ -108,9 +106,7 @@ describe("DefinitionGenerator", () => { }); it("should respect the version of openAPI when passed in", function () { - const serverlessWithOpenAPIVersion = JSON.parse( - JSON.stringify(mockServerless) - ); + const serverlessWithOpenAPIVersion = structuredClone(mockServerless); serverlessWithOpenAPIVersion.processedInput.options.openApiVersion = "3.0.2"; let expected = new DefinitionGenerator( diff --git a/test/unit/openAPIGenerator.spec.js b/test/unit/openAPIGenerator.spec.js index af5d18e..4436083 100644 --- a/test/unit/openAPIGenerator.spec.js +++ b/test/unit/openAPIGenerator.spec.js @@ -107,9 +107,7 @@ describe("OpenAPIGenerator", () => { const getAllFuncsStub = sinon .stub(sls.service, "getAllFunctions") .returns(["createUser"]); - const basicInvalidFunction = JSON.parse( - JSON.stringify(basicValidFunction) - ); + const basicInvalidFunction = structuredClone(basicValidFunction); delete basicInvalidFunction.createUser.events[0].http.documentation .methodResponses[0].responseModels; @@ -145,9 +143,7 @@ describe("OpenAPIGenerator", () => { const getAllFuncsStub = sinon .stub(sls.service, "getAllFunctions") .returns(["createUser"]); - const basicInvalidFunction = JSON.parse( - JSON.stringify(basicValidFunction) - ); + const basicInvalidFunction = structuredClone(basicValidFunction); const getFuncStub = sinon .stub(sls.service, "getFunction") @@ -182,9 +178,7 @@ describe("OpenAPIGenerator", () => { .stub(sls.service, "getAllFunctions") .returns(["createUser"]); - const basicInvalidFunction = JSON.parse( - JSON.stringify(basicValidFunction) - ); + const basicInvalidFunction = structuredClone(basicValidFunction); delete basicInvalidFunction.createUser.events[0].http.documentation .pathParams; diff --git a/test/unit/owasp.spec.js b/test/unit/owasp.spec.js index 523c68d..6315092 100644 --- a/test/unit/owasp.spec.js +++ b/test/unit/owasp.spec.js @@ -56,7 +56,7 @@ describe(`owasp`, function () { }); it(`adds any properties contained in a new release`, async function () { - const newOWASPJSONAdded = JSON.parse(JSON.stringify(newOWASPJSON)); + const newOWASPJSONAdded = structuredClone(newOWASPJSON); newOWASPJSONAdded.headers.push({ name: "x-added", value: "true" }); nock("https://owasp.org") diff --git a/test/unit/schemaHandler.spec.js b/test/unit/schemaHandler.spec.js index f4b6f28..d5a1cbc 100644 --- a/test/unit/schemaHandler.spec.js +++ b/test/unit/schemaHandler.spec.js @@ -43,12 +43,12 @@ describe(`SchemaHandler`, function () { }; beforeEach(function () { - mockServerless = JSON.parse(JSON.stringify(serverlessMock)); - modelsDocument = JSON.parse(JSON.stringify(modelsDocumentOG)); - modelsAltDocument = JSON.parse(JSON.stringify(modelsAltDocumentOG)); - modelsListDocument = JSON.parse(JSON.stringify(modelsListDocumentOG)); - modelsListAltDocument = JSON.parse(JSON.stringify(modelsListAltDocumentOG)); - openAPI = JSON.parse(JSON.stringify(openAPISchema)); + mockServerless = structuredClone(serverlessMock); + modelsDocument = structuredClone(modelsDocumentOG); + modelsAltDocument = structuredClone(modelsAltDocumentOG); + modelsListDocument = structuredClone(modelsListDocumentOG); + modelsListAltDocument = structuredClone(modelsListAltDocumentOG); + openAPI = structuredClone(openAPISchema); }); describe(`constuctor`, function () { @@ -135,7 +135,7 @@ describe(`SchemaHandler`, function () { }); it(`should standardise mixed models syntax in to the correct format`, function () { - const newModelsDocument = JSON.parse(JSON.stringify(modelsDocument)); + const newModelsDocument = structuredClone(modelsDocument); Object.assign( mockServerless.service.custom.documentation, newModelsDocument @@ -168,9 +168,7 @@ describe(`SchemaHandler`, function () { }); it(`should standardise mixed modelsList syntax in to the correct format`, function () { - const newModelsDocument = JSON.parse( - JSON.stringify(modelsListDocument) - ); + const newModelsDocument = structuredClone(modelsListDocument); Object.assign( mockServerless.service.custom.documentation, newModelsDocument @@ -203,9 +201,7 @@ describe(`SchemaHandler`, function () { }); it(`should standardise mixed models and modelsList syntax in to the correct format`, function () { - const newModelsDocument = JSON.parse( - JSON.stringify(modelsListDocument) - ); + const newModelsDocument = structuredClone(modelsListDocument); Object.assign( mockServerless.service.custom.documentation, newModelsDocument