From 7ba0d131cd9061fcfd33a015a0f89b99e16e834b Mon Sep 17 00:00:00 2001 From: Neel Date: Fri, 6 Dec 2024 18:03:35 +0000 Subject: [PATCH] fix: expand glob before checking API count in join (#1757) --- .changeset/long-donkeys-bathe.md | 5 +++ .../cli/src/__tests__/commands/join.test.ts | 37 ++++++++++++++++++- packages/cli/src/commands/join.ts | 15 ++++---- 3 files changed, 48 insertions(+), 9 deletions(-) create mode 100644 .changeset/long-donkeys-bathe.md diff --git a/.changeset/long-donkeys-bathe.md b/.changeset/long-donkeys-bathe.md new file mode 100644 index 0000000000..ec40a158d8 --- /dev/null +++ b/.changeset/long-donkeys-bathe.md @@ -0,0 +1,5 @@ +--- +"@redocly/cli": patch +--- + +Fixed an issue where `join` would throw an error when a glob pattern was provided. diff --git a/packages/cli/src/__tests__/commands/join.test.ts b/packages/cli/src/__tests__/commands/join.test.ts index 8980be4cc3..6d42a389be 100644 --- a/packages/cli/src/__tests__/commands/join.test.ts +++ b/packages/cli/src/__tests__/commands/join.test.ts @@ -1,7 +1,11 @@ import { yellow } from 'colorette'; import { detectSpec } from '@redocly/openapi-core'; import { handleJoin } from '../../commands/join'; -import { exitWithError, writeToFileByExtension } from '../../utils/miscellaneous'; +import { + exitWithError, + getFallbackApisOrExit, + writeToFileByExtension, +} from '../../utils/miscellaneous'; import { loadConfig } from '../../__mocks__/@redocly/openapi-core'; import { ConfigFixture } from '../fixtures/config'; @@ -15,7 +19,35 @@ describe('handleJoin', () => { it('should call exitWithError because only one entrypoint', async () => { await handleJoin({ argv: { apis: ['first.yaml'] }, config: {} as any, version: 'cli-version' }); - expect(exitWithError).toHaveBeenCalledWith(`At least 2 apis should be provided.`); + expect(exitWithError).toHaveBeenCalledWith(`At least 2 APIs should be provided.`); + }); + + it('should call exitWithError if glob expands to less than 2 APIs', async () => { + (getFallbackApisOrExit as jest.Mock).mockResolvedValueOnce([{ path: 'first.yaml' }]); + + await handleJoin({ + argv: { apis: ['*.yaml'] }, + config: {} as any, + version: 'cli-version', + }); + + expect(exitWithError).toHaveBeenCalledWith(`At least 2 APIs should be provided.`); + }); + + it('should proceed if glob expands to 2 or more APIs', async () => { + (detectSpec as jest.Mock).mockReturnValue('oas3_1'); + (getFallbackApisOrExit as jest.Mock).mockResolvedValueOnce([ + { path: 'first.yaml' }, + { path: 'second.yaml' }, + ]); + + await handleJoin({ + argv: { apis: ['*.yaml'] }, + config: ConfigFixture as any, + version: 'cli-version', + }); + + expect(exitWithError).not.toHaveBeenCalled(); }); it('should call exitWithError because passed all 3 options for tags', async () => { @@ -52,6 +84,7 @@ describe('handleJoin', () => { }); it('should call exitWithError because Only OpenAPI 3.0 and OpenAPI 3.1 are supported', async () => { + (detectSpec as jest.Mock).mockReturnValueOnce('oas2_0'); await handleJoin({ argv: { apis: ['first.yaml', 'second.yaml'], diff --git a/packages/cli/src/commands/join.ts b/packages/cli/src/commands/join.ts index 07d866e481..8671e0a35d 100644 --- a/packages/cli/src/commands/join.ts +++ b/packages/cli/src/commands/join.ts @@ -64,18 +64,12 @@ export async function handleJoin({ }: CommandArgs) { const startedAt = performance.now(); - if (argv.apis.length < 2) { - return exitWithError(`At least 2 apis should be provided.`); - } - - const fileExtension = getAndValidateFileExtension(argv.output || argv.apis[0]); - const { 'prefix-components-with-info-prop': prefixComponentsWithInfoProp, 'prefix-tags-with-filename': prefixTagsWithFilename, 'prefix-tags-with-info-prop': prefixTagsWithInfoProp, 'without-x-tag-groups': withoutXTagGroups, - output: specFilename = `openapi.${fileExtension}`, + output, } = argv; const usedTagsOptions = [ @@ -91,6 +85,13 @@ export async function handleJoin({ } const apis = await getFallbackApisOrExit(argv.apis, config); + if (apis.length < 2) { + return exitWithError(`At least 2 APIs should be provided.`); + } + + const fileExtension = getAndValidateFileExtension(output || apis[0].path); + const specFilename = output || `openapi.${fileExtension}`; + const externalRefResolver = new BaseResolver(config.resolve); const documents = await Promise.all( apis.map(