diff --git a/apps/generator-cli/src/app/app.module.spec.ts b/apps/generator-cli/src/app/app.module.spec.ts index 6141d26e853..3a7224deebb 100644 --- a/apps/generator-cli/src/app/app.module.spec.ts +++ b/apps/generator-cli/src/app/app.module.spec.ts @@ -114,6 +114,67 @@ describe('AppModule', () => { expect(programMock.parse).toHaveBeenNthCalledWith(1, process.argv); }); }); + + describe('a custom generator is provided', () => { + beforeEach(async () => { + process.argv = [ + 'foo', + 'baz', + '--custom-generator', + '/path/to/custom.jar', + ]; + programMock.parse.mockImplementation(() => { + expect(passThroughServiceMock.init).toHaveBeenCalledTimes(1); + }); + versionManagerServiceMock.getSelectedVersion.mockReturnValue('1.2.3'); + await fixture.onApplicationBootstrap(); + }); + + it('does not search for the latest version', () => { + expect(versionManagerServiceMock.search).toHaveBeenCalledTimes(0); + }); + + it('does not set the selected version', () => { + expect( + versionManagerServiceMock.setSelectedVersion, + ).toHaveBeenCalledTimes(0); + }); + + it('does not download', () => { + expect( + versionManagerServiceMock.downloadIfNeeded, + ).toHaveBeenCalledTimes(0); + }); + + it('parses the command', () => { + expect(programMock.parse).toHaveBeenNthCalledWith(1, process.argv); + }); + }); + + describe('a custom generator is provided with = syntax', () => { + beforeEach(async () => { + process.argv = [ + 'foo', + 'baz', + '--custom-generator=/path/to/custom.jar', + ]; + programMock.parse.mockImplementation(() => { + expect(passThroughServiceMock.init).toHaveBeenCalledTimes(1); + }); + versionManagerServiceMock.getSelectedVersion.mockReturnValue('1.2.3'); + await fixture.onApplicationBootstrap(); + }); + + it('does not download', () => { + expect( + versionManagerServiceMock.downloadIfNeeded, + ).toHaveBeenCalledTimes(0); + }); + + it('parses the command', () => { + expect(programMock.parse).toHaveBeenNthCalledWith(1, process.argv); + }); + }); }); }); }); diff --git a/apps/generator-cli/src/app/app.module.ts b/apps/generator-cli/src/app/app.module.ts index 10c40be94f5..a2f928f51d1 100644 --- a/apps/generator-cli/src/app/app.module.ts +++ b/apps/generator-cli/src/app/app.module.ts @@ -63,17 +63,25 @@ export class AppModule implements OnApplicationBootstrap { ) {} onApplicationBootstrap = async () => { - let selectedVersion = this.versionManager.getSelectedVersion(); + const hasCustomGenerator = process.argv.some( + (arg) => + arg === '--custom-generator' || arg.startsWith('--custom-generator='), + ); - if (!selectedVersion) { - const [{ version }] = await this.versionManager - .search(['latest']) - .toPromise(); - await this.versionManager.setSelectedVersion(version); - selectedVersion = version; + if (!hasCustomGenerator) { + let selectedVersion = this.versionManager.getSelectedVersion(); + + if (!selectedVersion) { + const [{ version }] = await this.versionManager + .search(['latest']) + .toPromise(); + await this.versionManager.setSelectedVersion(version); + selectedVersion = version; + } + + await this.versionManager.downloadIfNeeded(selectedVersion); } - await this.versionManager.downloadIfNeeded(selectedVersion); await this.passThroughService.init(); this.program.parse(process.argv); }; diff --git a/apps/generator-cli/src/app/services/generator.service.spec.ts b/apps/generator-cli/src/app/services/generator.service.spec.ts index fd574b6cc9c..994d126eda0 100644 --- a/apps/generator-cli/src/app/services/generator.service.spec.ts +++ b/apps/generator-cli/src/app/services/generator.service.spec.ts @@ -116,7 +116,9 @@ describe('GeneratorService', () => { beforeEach(() => { executedCommands = []; - fs.existsSync.mockImplementation((p) => !!config[p]); + fs.existsSync.mockImplementation( + (p) => p === '/path/to/4.2.1.jar' || !!config[p], + ); fs.readJSONSync.mockImplementation((p) => config[p]); glob.sync.mockImplementation((g) => specFiles[g]); concurrently.mockImplementation((ec, cfg) => { @@ -275,6 +277,38 @@ describe('GeneratorService', () => { expect(returnValue).toEqual(expectedCommands.length > 0); }); }); + + describe('custom jar only (standard jar missing)', () => { + let returnValue: boolean; + + beforeEach(async () => { + fs.existsSync.mockImplementation((p) => { + if (p === '/path/to/4.2.1.jar') return false; + return !!config[p]; + }); + configGet.mockImplementation( + (path, defaultValue) => config['bar.json'] || defaultValue, + ); + returnValue = await fixture.generate('../some/custom.jar'); + }); + + it('uses only the custom jar', () => { + expect(executedCommands).toEqual([ + { + name: '[bar] api/cat.yaml', + command: `java -jar "../some/custom.jar" generate --input-spec="${cwd}/api/cat.yaml" --output="bar/cat" --some-bool`, + }, + { + name: '[bar] api/bird.json', + command: `java -jar "../some/custom.jar" generate --input-spec="${cwd}/api/bird.json" --output="bar/bird" --some-bool`, + }, + ]); + }); + + it('resolved to true', () => { + expect(returnValue).toEqual(true); + }); + }); }); }); }); diff --git a/apps/generator-cli/src/app/services/generator.service.ts b/apps/generator-cli/src/app/services/generator.service.ts index 83abc0f8b0c..07699304bdd 100644 --- a/apps/generator-cli/src/app/services/generator.service.ts +++ b/apps/generator-cli/src/app/services/generator.service.ts @@ -242,11 +242,16 @@ export class GeneratorService { } const cliPath = this.versionManager.filePath(); - const subCmd = customGenerator - ? `-cp "${[cliPath, customGenerator].join( - this.isWin() ? ';' : ':' - )}" org.openapitools.codegen.OpenAPIGenerator` - : `-jar "${cliPath}"`; + let subCmd: string; + if (customGenerator) { + subCmd = fs.existsSync(cliPath) + ? `-cp "${[cliPath, customGenerator].join( + this.isWin() ? ';' : ':' + )}" org.openapitools.codegen.OpenAPIGenerator` + : `-jar "${customGenerator}"`; + } else { + subCmd = `-jar "${cliPath}"`; + } return ['java', process.env['JAVA_OPTS'], subCmd, 'generate', appendix] .filter((str): str is string => str != null && typeof str === 'string') .join(' '); diff --git a/apps/generator-cli/src/app/services/pass-through.service.spec.ts b/apps/generator-cli/src/app/services/pass-through.service.spec.ts index 36780b86340..36e1c034fed 100644 --- a/apps/generator-cli/src/app/services/pass-through.service.spec.ts +++ b/apps/generator-cli/src/app/services/pass-through.service.spec.ts @@ -8,8 +8,11 @@ import { VersionManagerService } from './version-manager.service'; import { ConfigService } from './config.service'; jest.mock('child_process'); +jest.mock('fs'); // eslint-disable-next-line @typescript-eslint/no-var-requires const childProcess = jest.mocked(require('child_process')); +// eslint-disable-next-line @typescript-eslint/no-var-requires +const fs = jest.mocked(require('fs')); describe('PassThroughService', () => { let fixture: PassThroughService; @@ -56,6 +59,7 @@ describe('PassThroughService', () => { fixture = moduleRef.get(PassThroughService); childProcess.spawn.mockReset().mockReturnValue({ on: jest.fn() }); + fs.existsSync.mockReset().mockReturnValue(true); configServiceMock.get.mockClear(); configServiceMock.get.mockReset(); configServiceMock.useDocker = false; @@ -232,6 +236,23 @@ describe('PassThroughService', () => { ); }); + it('can delegate with custom jar only when standard jar is missing', async () => { + fs.existsSync.mockReturnValue(false); + await program.parseAsync( + [name, ...argv, '--custom-generator=../some/custom.jar'], + { from: 'user' } + ); + + expect(childProcess.spawn).toHaveBeenNthCalledWith( + 1, + `java -jar "../some/custom.jar" ${name} ${argv.join(' ')}`, + { + stdio: 'inherit', + shell: true, + } + ); + }); + if (name === 'generate') { it('can delegate with custom jar to generate command', async () => { await program.parseAsync( diff --git a/apps/generator-cli/src/app/services/pass-through.service.ts b/apps/generator-cli/src/app/services/pass-through.service.ts index 75a462c5f3f..dafee5437a4 100644 --- a/apps/generator-cli/src/app/services/pass-through.service.ts +++ b/apps/generator-cli/src/app/services/pass-through.service.ts @@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common'; import chalk from 'chalk'; import { exec, spawn } from 'child_process'; import { Command } from 'commander'; +import * as fs from 'fs'; import { COMMANDER_PROGRAM, LOGGER } from '../constants'; import { GeneratorService } from './generator.service'; import { VersionManagerService } from './version-manager.service'; @@ -139,14 +140,21 @@ export class PassThroughService { ].join(' '); } - const customGenerator = this.program.opts()?.customGenerator; + const customGenerator = + this.program.opts()?.customGenerator || + this.getCustomGeneratorFromArgs(); const cliPath = this.versionManager.filePath(); - const subCmd = customGenerator - ? `-cp "${[cliPath, customGenerator].join( - this.isWin() ? ';' : ':' - )}" org.openapitools.codegen.OpenAPIGenerator` - : `-jar "${cliPath}"`; + let subCmd: string; + if (customGenerator) { + subCmd = fs.existsSync(cliPath) + ? `-cp "${[cliPath, customGenerator].join( + this.isWin() ? ';' : ':' + )}" org.openapitools.codegen.OpenAPIGenerator` + : `-jar "${customGenerator}"`; + } else { + subCmd = `-jar "${cliPath}"`; + } return ['java', process.env['JAVA_OPTS'], subCmd] .filter((str): str is string => str != null && typeof str === 'string') @@ -157,5 +165,18 @@ export class PassThroughService { console.log(chalk.cyanBright(cmd.helpInformation())); } + private getCustomGeneratorFromArgs(): string | undefined { + for (let i = 0; i < process.argv.length; i++) { + const arg = process.argv[i]; + if (arg === '--custom-generator' && i + 1 < process.argv.length) { + return process.argv[i + 1]; + } + if (arg.startsWith('--custom-generator=')) { + return arg.substring('--custom-generator='.length); + } + } + return undefined; + } + private isWin = () => process.platform === 'win32'; }