Skip to content

Commit 4c6ff56

Browse files
authored
fix(custom generators): prioritize custom generator JAR when provided (#1189)
1 parent 7991687 commit 4c6ff56

File tree

6 files changed

+170
-20
lines changed

6 files changed

+170
-20
lines changed

apps/generator-cli/src/app/app.module.spec.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -114,6 +114,67 @@ describe('AppModule', () => {
114114
expect(programMock.parse).toHaveBeenNthCalledWith(1, process.argv);
115115
});
116116
});
117+
118+
describe('a custom generator is provided', () => {
119+
beforeEach(async () => {
120+
process.argv = [
121+
'foo',
122+
'baz',
123+
'--custom-generator',
124+
'/path/to/custom.jar',
125+
];
126+
programMock.parse.mockImplementation(() => {
127+
expect(passThroughServiceMock.init).toHaveBeenCalledTimes(1);
128+
});
129+
versionManagerServiceMock.getSelectedVersion.mockReturnValue('1.2.3');
130+
await fixture.onApplicationBootstrap();
131+
});
132+
133+
it('does not search for the latest version', () => {
134+
expect(versionManagerServiceMock.search).toHaveBeenCalledTimes(0);
135+
});
136+
137+
it('does not set the selected version', () => {
138+
expect(
139+
versionManagerServiceMock.setSelectedVersion,
140+
).toHaveBeenCalledTimes(0);
141+
});
142+
143+
it('does not download', () => {
144+
expect(
145+
versionManagerServiceMock.downloadIfNeeded,
146+
).toHaveBeenCalledTimes(0);
147+
});
148+
149+
it('parses the command', () => {
150+
expect(programMock.parse).toHaveBeenNthCalledWith(1, process.argv);
151+
});
152+
});
153+
154+
describe('a custom generator is provided with = syntax', () => {
155+
beforeEach(async () => {
156+
process.argv = [
157+
'foo',
158+
'baz',
159+
'--custom-generator=/path/to/custom.jar',
160+
];
161+
programMock.parse.mockImplementation(() => {
162+
expect(passThroughServiceMock.init).toHaveBeenCalledTimes(1);
163+
});
164+
versionManagerServiceMock.getSelectedVersion.mockReturnValue('1.2.3');
165+
await fixture.onApplicationBootstrap();
166+
});
167+
168+
it('does not download', () => {
169+
expect(
170+
versionManagerServiceMock.downloadIfNeeded,
171+
).toHaveBeenCalledTimes(0);
172+
});
173+
174+
it('parses the command', () => {
175+
expect(programMock.parse).toHaveBeenNthCalledWith(1, process.argv);
176+
});
177+
});
117178
});
118179
});
119180
});

apps/generator-cli/src/app/app.module.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -63,17 +63,25 @@ export class AppModule implements OnApplicationBootstrap {
6363
) {}
6464

6565
onApplicationBootstrap = async () => {
66-
let selectedVersion = this.versionManager.getSelectedVersion();
66+
const hasCustomGenerator = process.argv.some(
67+
(arg) =>
68+
arg === '--custom-generator' || arg.startsWith('--custom-generator='),
69+
);
6770

68-
if (!selectedVersion) {
69-
const [{ version }] = await this.versionManager
70-
.search(['latest'])
71-
.toPromise();
72-
await this.versionManager.setSelectedVersion(version);
73-
selectedVersion = version;
71+
if (!hasCustomGenerator) {
72+
let selectedVersion = this.versionManager.getSelectedVersion();
73+
74+
if (!selectedVersion) {
75+
const [{ version }] = await this.versionManager
76+
.search(['latest'])
77+
.toPromise();
78+
await this.versionManager.setSelectedVersion(version);
79+
selectedVersion = version;
80+
}
81+
82+
await this.versionManager.downloadIfNeeded(selectedVersion);
7483
}
7584

76-
await this.versionManager.downloadIfNeeded(selectedVersion);
7785
await this.passThroughService.init();
7886
this.program.parse(process.argv);
7987
};

apps/generator-cli/src/app/services/generator.service.spec.ts

Lines changed: 35 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -116,7 +116,9 @@ describe('GeneratorService', () => {
116116

117117
beforeEach(() => {
118118
executedCommands = [];
119-
fs.existsSync.mockImplementation((p) => !!config[p]);
119+
fs.existsSync.mockImplementation(
120+
(p) => p === '/path/to/4.2.1.jar' || !!config[p],
121+
);
120122
fs.readJSONSync.mockImplementation((p) => config[p]);
121123
glob.sync.mockImplementation((g) => specFiles[g]);
122124
concurrently.mockImplementation((ec, cfg) => {
@@ -275,6 +277,38 @@ describe('GeneratorService', () => {
275277
expect(returnValue).toEqual(expectedCommands.length > 0);
276278
});
277279
});
280+
281+
describe('custom jar only (standard jar missing)', () => {
282+
let returnValue: boolean;
283+
284+
beforeEach(async () => {
285+
fs.existsSync.mockImplementation((p) => {
286+
if (p === '/path/to/4.2.1.jar') return false;
287+
return !!config[p];
288+
});
289+
configGet.mockImplementation(
290+
(path, defaultValue) => config['bar.json'] || defaultValue,
291+
);
292+
returnValue = await fixture.generate('../some/custom.jar');
293+
});
294+
295+
it('uses only the custom jar', () => {
296+
expect(executedCommands).toEqual([
297+
{
298+
name: '[bar] api/cat.yaml',
299+
command: `java -jar "../some/custom.jar" generate --input-spec="${cwd}/api/cat.yaml" --output="bar/cat" --some-bool`,
300+
},
301+
{
302+
name: '[bar] api/bird.json',
303+
command: `java -jar "../some/custom.jar" generate --input-spec="${cwd}/api/bird.json" --output="bar/bird" --some-bool`,
304+
},
305+
]);
306+
});
307+
308+
it('resolved to true', () => {
309+
expect(returnValue).toEqual(true);
310+
});
311+
});
278312
});
279313
});
280314
});

apps/generator-cli/src/app/services/generator.service.ts

Lines changed: 10 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -242,11 +242,16 @@ export class GeneratorService {
242242
}
243243

244244
const cliPath = this.versionManager.filePath();
245-
const subCmd = customGenerator
246-
? `-cp "${[cliPath, customGenerator].join(
247-
this.isWin() ? ';' : ':'
248-
)}" org.openapitools.codegen.OpenAPIGenerator`
249-
: `-jar "${cliPath}"`;
245+
let subCmd: string;
246+
if (customGenerator) {
247+
subCmd = fs.existsSync(cliPath)
248+
? `-cp "${[cliPath, customGenerator].join(
249+
this.isWin() ? ';' : ':'
250+
)}" org.openapitools.codegen.OpenAPIGenerator`
251+
: `-jar "${customGenerator}"`;
252+
} else {
253+
subCmd = `-jar "${cliPath}"`;
254+
}
250255
return ['java', process.env['JAVA_OPTS'], subCmd, 'generate', appendix]
251256
.filter((str): str is string => str != null && typeof str === 'string')
252257
.join(' ');

apps/generator-cli/src/app/services/pass-through.service.spec.ts

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,8 +8,11 @@ import { VersionManagerService } from './version-manager.service';
88
import { ConfigService } from './config.service';
99

1010
jest.mock('child_process');
11+
jest.mock('fs');
1112
// eslint-disable-next-line @typescript-eslint/no-var-requires
1213
const childProcess = jest.mocked(require('child_process'));
14+
// eslint-disable-next-line @typescript-eslint/no-var-requires
15+
const fs = jest.mocked(require('fs'));
1316

1417
describe('PassThroughService', () => {
1518
let fixture: PassThroughService;
@@ -56,6 +59,7 @@ describe('PassThroughService', () => {
5659
fixture = moduleRef.get(PassThroughService);
5760

5861
childProcess.spawn.mockReset().mockReturnValue({ on: jest.fn() });
62+
fs.existsSync.mockReset().mockReturnValue(true);
5963
configServiceMock.get.mockClear();
6064
configServiceMock.get.mockReset();
6165
configServiceMock.useDocker = false;
@@ -232,6 +236,23 @@ describe('PassThroughService', () => {
232236
);
233237
});
234238

239+
it('can delegate with custom jar only when standard jar is missing', async () => {
240+
fs.existsSync.mockReturnValue(false);
241+
await program.parseAsync(
242+
[name, ...argv, '--custom-generator=../some/custom.jar'],
243+
{ from: 'user' }
244+
);
245+
246+
expect(childProcess.spawn).toHaveBeenNthCalledWith(
247+
1,
248+
`java -jar "../some/custom.jar" ${name} ${argv.join(' ')}`,
249+
{
250+
stdio: 'inherit',
251+
shell: true,
252+
}
253+
);
254+
});
255+
235256
if (name === 'generate') {
236257
it('can delegate with custom jar to generate command', async () => {
237258
await program.parseAsync(

apps/generator-cli/src/app/services/pass-through.service.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ import { Inject, Injectable } from '@nestjs/common';
22
import chalk from 'chalk';
33
import { exec, spawn } from 'child_process';
44
import { Command } from 'commander';
5+
import * as fs from 'fs';
56
import { COMMANDER_PROGRAM, LOGGER } from '../constants';
67
import { GeneratorService } from './generator.service';
78
import { VersionManagerService } from './version-manager.service';
@@ -139,14 +140,21 @@ export class PassThroughService {
139140
].join(' ');
140141
}
141142

142-
const customGenerator = this.program.opts()?.customGenerator;
143+
const customGenerator =
144+
this.program.opts()?.customGenerator ||
145+
this.getCustomGeneratorFromArgs();
143146
const cliPath = this.versionManager.filePath();
144147

145-
const subCmd = customGenerator
146-
? `-cp "${[cliPath, customGenerator].join(
147-
this.isWin() ? ';' : ':'
148-
)}" org.openapitools.codegen.OpenAPIGenerator`
149-
: `-jar "${cliPath}"`;
148+
let subCmd: string;
149+
if (customGenerator) {
150+
subCmd = fs.existsSync(cliPath)
151+
? `-cp "${[cliPath, customGenerator].join(
152+
this.isWin() ? ';' : ':'
153+
)}" org.openapitools.codegen.OpenAPIGenerator`
154+
: `-jar "${customGenerator}"`;
155+
} else {
156+
subCmd = `-jar "${cliPath}"`;
157+
}
150158

151159
return ['java', process.env['JAVA_OPTS'], subCmd]
152160
.filter((str): str is string => str != null && typeof str === 'string')
@@ -157,5 +165,18 @@ export class PassThroughService {
157165
console.log(chalk.cyanBright(cmd.helpInformation()));
158166
}
159167

168+
private getCustomGeneratorFromArgs(): string | undefined {
169+
for (let i = 0; i < process.argv.length; i++) {
170+
const arg = process.argv[i];
171+
if (arg === '--custom-generator' && i + 1 < process.argv.length) {
172+
return process.argv[i + 1];
173+
}
174+
if (arg.startsWith('--custom-generator=')) {
175+
return arg.substring('--custom-generator='.length);
176+
}
177+
}
178+
return undefined;
179+
}
180+
160181
private isWin = () => process.platform === 'win32';
161182
}

0 commit comments

Comments
 (0)