From a33bbeb1155095f20b01cda18f2c32e9c001b428 Mon Sep 17 00:00:00 2001 From: Kanad Gupta <8854718+kanadgupta@users.noreply.github.com> Date: Tue, 21 Nov 2023 16:25:32 -0600 Subject: [PATCH] feat(openapi/convert): support openapi in convert command (#941) * fix(openapi/convert): support openapi in convert command * test: fix single-threaded test --- __tests__/__snapshots__/index.test.ts.snap | 9 +++-- __tests__/cmds/openapi/convert.test.ts | 38 +++++++++++++++++-- .../lib/__snapshots__/commands.test.ts.snap | 2 +- .../single-threaded/openapi/convert.test.ts | 2 +- src/cmds/openapi/convert.ts | 16 +++++--- 5 files changed, 53 insertions(+), 14 deletions(-) diff --git a/__tests__/__snapshots__/index.test.ts.snap b/__tests__/__snapshots__/index.test.ts.snap index a0f20ffbf..18d23afde 100644 --- a/__tests__/__snapshots__/index.test.ts.snap +++ b/__tests__/__snapshots__/index.test.ts.snap @@ -39,7 +39,8 @@ Options Related commands - $ rdme openapi:convert Convert a Swagger or Postman Collection to OpenAPI. + $ rdme openapi:convert Convert an API definition to OpenAPI and bundle any + external references. $ rdme openapi:inspect Analyze an OpenAPI/Swagger definition for various OpenAPI and ReadMe feature usage. $ rdme openapi:reduce Reduce an OpenAPI definition into a smaller subset. @@ -86,7 +87,8 @@ Options Related commands - $ rdme openapi:convert Convert a Swagger or Postman Collection to OpenAPI. + $ rdme openapi:convert Convert an API definition to OpenAPI and bundle any + external references. $ rdme openapi:inspect Analyze an OpenAPI/Swagger definition for various OpenAPI and ReadMe feature usage. $ rdme openapi:reduce Reduce an OpenAPI definition into a smaller subset. @@ -133,7 +135,8 @@ Options Related commands - $ rdme openapi:convert Convert a Swagger or Postman Collection to OpenAPI. + $ rdme openapi:convert Convert an API definition to OpenAPI and bundle any + external references. $ rdme openapi:inspect Analyze an OpenAPI/Swagger definition for various OpenAPI and ReadMe feature usage. $ rdme openapi:reduce Reduce an OpenAPI definition into a smaller subset. diff --git a/__tests__/cmds/openapi/convert.test.ts b/__tests__/cmds/openapi/convert.test.ts index 6ec768720..dd1cf692b 100644 --- a/__tests__/cmds/openapi/convert.test.ts +++ b/__tests__/cmds/openapi/convert.test.ts @@ -7,7 +7,7 @@ import OpenAPIConvertCommand from '../../../src/cmds/openapi/convert.js'; const convert = new OpenAPIConvertCommand(); -const successfulConversion = () => 'Your converted API definition has been saved to output.json!'; +const successfulConversion = () => 'Your API definition has been converted and bundled and saved to output.json!'; describe('rdme openapi:convert', () => { describe('converting', () => { @@ -41,13 +41,45 @@ describe('rdme openapi:convert', () => { it.each([['json'], ['yaml']])('should fail if given an OpenAPI 3.0 definition (format: %s)', async format => { const spec = require.resolve(`@readme/oas-examples/3.0/${format}/petstore.${format}`); + const consoleWarnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {}); + + let reducedSpec; + fs.writeFileSync = vi.fn((fileName, data) => { + reducedSpec = JSON.parse(data as string); + }); + + prompts.inject(['output.json']); + await expect( convert.run({ spec, }), - ).rejects.toStrictEqual( - new Error("Sorry, this API definition is already an OpenAPI definition and doesn't need to be converted."), + ).resolves.toBe(successfulConversion()); + + expect(fs.writeFileSync).toHaveBeenCalledWith('output.json', expect.any(String)); + expect(reducedSpec.tags).toHaveLength(3); + expect(Object.keys(reducedSpec.paths)).toStrictEqual([ + '/pet', + '/pet/findByStatus', + '/pet/findByTags', + '/pet/{petId}', + '/pet/{petId}/uploadImage', + '/store/inventory', + '/store/order', + '/store/order/{orderId}', + '/user', + '/user/createWithArray', + '/user/createWithList', + '/user/login', + '/user/logout', + '/user/{username}', + ]); + expect(Object.keys(reducedSpec.paths['/pet/{petId}'])).toStrictEqual(['get', 'post', 'delete']); + expect(consoleWarnSpy).toHaveBeenCalledWith( + '⚠️ Warning! The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled.', ); + + consoleWarnSpy.mockRestore(); }); }); }); diff --git a/__tests__/lib/__snapshots__/commands.test.ts.snap b/__tests__/lib/__snapshots__/commands.test.ts.snap index 4846239fd..769abe424 100644 --- a/__tests__/lib/__snapshots__/commands.test.ts.snap +++ b/__tests__/lib/__snapshots__/commands.test.ts.snap @@ -30,7 +30,7 @@ exports[`utils > #listByCategory > should list commands by category 1`] = ` "name": "openapi", }, { - "description": "Convert a Swagger or Postman Collection to OpenAPI.", + "description": "Convert an API definition to OpenAPI and bundle any external references.", "hidden": false, "name": "openapi:convert", }, diff --git a/__tests__/single-threaded/openapi/convert.test.ts b/__tests__/single-threaded/openapi/convert.test.ts index 9d0c2f266..89f042293 100644 --- a/__tests__/single-threaded/openapi/convert.test.ts +++ b/__tests__/single-threaded/openapi/convert.test.ts @@ -6,7 +6,7 @@ import OpenAPIConvertCommand from '../../../src/cmds/openapi/convert.js'; const convert = new OpenAPIConvertCommand(); -const successfulConversion = () => 'Your converted API definition has been saved to output.json!'; +const successfulConversion = () => 'Your API definition has been converted and bundled and saved to output.json!'; describe('rdme openapi:convert (single-threaded)', () => { let testWorkingDir: string; diff --git a/src/cmds/openapi/convert.ts b/src/cmds/openapi/convert.ts index 1dfb3f6d3..b834afcd0 100644 --- a/src/cmds/openapi/convert.ts +++ b/src/cmds/openapi/convert.ts @@ -24,7 +24,7 @@ export default class OpenAPIConvertCommand extends Command { this.command = 'openapi:convert'; this.usage = 'openapi:convert [file|url] [options]'; - this.description = 'Convert a Swagger or Postman Collection to OpenAPI.'; + this.description = 'Convert an API definition to OpenAPI and bundle any external references.'; this.cmdCategory = CommandCategories.APIS; this.hiddenArgs = ['spec']; @@ -58,7 +58,9 @@ export default class OpenAPIConvertCommand extends Command { const parsedPreparedSpec: OASDocument = JSON.parse(preparedSpec); if (specType === 'OpenAPI') { - throw new Error("Sorry, this API definition is already an OpenAPI definition and doesn't need to be converted."); + Command.warn( + 'The input file is already OpenAPI, so no conversion is necessary. Any external references will be bundled.', + ); } prompts.override({ @@ -69,7 +71,7 @@ export default class OpenAPIConvertCommand extends Command { { type: 'text', name: 'outputPath', - message: 'Enter the path to save your converted API definition to:', + message: 'Enter the path to save your converted/bundled API definition to:', initial: () => { const extension = path.extname(specPath); return `${path.basename(specPath).split(extension)[0]}.openapi${extension}`; @@ -78,12 +80,14 @@ export default class OpenAPIConvertCommand extends Command { }, ]); - Command.debug(`saving converted spec to ${promptResults.outputPath}`); + Command.debug(`saving converted/bundled spec to ${promptResults.outputPath}`); fs.writeFileSync(promptResults.outputPath, JSON.stringify(parsedPreparedSpec, null, 2)); - Command.debug('converted spec saved'); + Command.debug('converted/bundled spec saved'); - return Promise.resolve(chalk.green(`Your converted API definition has been saved to ${promptResults.outputPath}!`)); + return Promise.resolve( + chalk.green(`Your API definition has been converted and bundled and saved to ${promptResults.outputPath}!`), + ); } }