Skip to content

Commit 23cf972

Browse files
committed
fix(api): add option to specify custom base path
1 parent 0eeecd2 commit 23cf972

File tree

13 files changed

+270
-110
lines changed

13 files changed

+270
-110
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ $ openapi --help
5555
--exportServices <value> Write services to disk [true, false, regexp] (default: true)
5656
--exportModels <value> Write models to disk [true, false, regexp] (default: true)
5757
--exportSchemas <value> Write schemas to disk (default: false)
58+
--base <value> Manually set base in OpenAPI config instead of inferring from server value
5859
--indent <value> Indentation options [4, 2, tab] (default: "4")
5960
--postfixServices Service name postfix (default: "Service")
6061
--postfixModels Model name postfix

bin/index.js

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ const params = program
1717
.option('--useOptions [value]', 'Use options instead of arguments', false)
1818
.option('--useUnionTypes', 'Use union types instead of enums')
1919
.option('--autoformat', 'Process generated files with autoformatter', false)
20+
.option('--base [value]', 'Manually set base in OpenAPI config instead of inferring from server value')
2021
.option('--exportCore <value>', 'Write core files to disk', true)
2122
.option('--exportServices <value>', 'Write services to disk', true)
2223
.option('--exportModels <value>', 'Write models to disk', true)
@@ -43,24 +44,17 @@ const parseBooleanOrString = value => {
4344

4445
if (OpenAPI) {
4546
OpenAPI.generate({
47+
...params,
4648
autoformat: JSON.parse(params.autoformat) === true,
4749
clientName: params.name,
4850
exportCore: JSON.parse(params.exportCore) === true,
4951
exportModels: parseBooleanOrString(params.exportModels),
5052
exportSchemas: JSON.parse(params.exportSchemas) === true,
5153
exportServices: parseBooleanOrString(params.exportServices),
5254
httpClient: params.client,
53-
indent: params.indent,
54-
input: params.input,
55-
output: params.output,
56-
postfixModels: params.postfixModels,
57-
postfixServices: params.postfixServices,
58-
request: params.request,
59-
serviceResponse: params.serviceResponse,
6055
useDateType: JSON.parse(params.useDateType) === true,
6156
useOperationId: JSON.parse(params.useOperationId) === true,
6257
useOptions: JSON.parse(params.useOptions) === true,
63-
useUnionTypes: params.useUnionTypes,
6458
})
6559
.then(() => {
6660
process.exit(0);

src/client/interfaces/Client.d.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,8 @@ import type { Model } from './Model';
22
import type { Service } from './Service';
33

44
export interface Client {
5-
version: string;
6-
server: string;
75
models: Model[];
6+
server: string;
87
services: Service[];
8+
version: string;
99
}

src/client/interfaces/Options.d.ts

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,84 @@ import { Indent } from '../../Indent';
44
export type ServiceResponse = 'body' | 'generics' | 'response';
55

66
export interface Options {
7+
/**
8+
* Process generated files with autoformatter
9+
*/
710
autoformat?: boolean;
11+
/**
12+
* Manually set base in OpenAPI config instead of inferring from server value
13+
*/
14+
base?: string;
15+
/**
16+
* Custom client class name
17+
*/
818
clientName?: string;
19+
/**
20+
* Generate core client classes
21+
*/
922
exportCore?: boolean;
23+
/**
24+
* Generate models
25+
*/
1026
exportModels?: boolean | string;
27+
/**
28+
* Generate schemas
29+
*/
1130
exportSchemas?: boolean;
31+
/**
32+
* Generate services
33+
*/
1234
exportServices?: boolean | string;
35+
/**
36+
* The selected httpClient (fetch, xhr, node or axios)
37+
*/
1338
httpClient?: HttpClient;
39+
/**
40+
* Indentation options (4, 2 or tab)
41+
*/
1442
indent?: Indent;
43+
/**
44+
* The relative location of the OpenAPI spec
45+
*/
1546
input: string | Record<string, any>;
47+
/**
48+
* The relative location of the output directory
49+
*/
1650
output: string;
51+
/**
52+
* Model name postfix
53+
*/
1754
postfixModels?: string;
55+
/**
56+
* Service name postfix
57+
*/
1858
postfixServices?: string;
59+
/**
60+
* Path to custom request file
61+
*/
1962
request?: string;
63+
/**
64+
* Define shape of returned value from service calls
65+
*/
2066
serviceResponse?: ServiceResponse;
67+
/**
68+
* Output Date instead of string for the format "date-time" in the models
69+
*/
2170
useDateType?: boolean;
71+
/**
72+
* Should the operationId be used when generating operation names?
73+
*/
2274
useOperationId?: boolean;
75+
/**
76+
* Use options or arguments functions
77+
*/
2378
useOptions?: boolean;
79+
/**
80+
* Use union types instead of enums
81+
*/
2482
useUnionTypes?: boolean;
83+
/**
84+
* Write the files to disk (true or false)
85+
*/
2586
write?: boolean;
2687
}

src/index.ts

Lines changed: 4 additions & 43 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,8 @@
11
import type { Options } from './client/interfaces/Options';
22
import { HttpClient } from './HttpClient';
33
import { Indent } from './Indent';
4-
import { parse as parseV2 } from './openApi/v2';
5-
import { parse as parseV3 } from './openApi/v3';
64
import { getOpenApiSpec } from './utils/getOpenApiSpec';
7-
import { getOpenApiVersion, OpenApiVersion } from './utils/getOpenApiVersion';
8-
import { isString } from './utils/isString';
5+
import { getOpenApiSpecParser } from './utils/getOpenApiSpecParser';
96
import { postProcessClient } from './utils/postProcessClient';
107
import { registerHandlebarTemplates } from './utils/registerHandlebarTemplates';
118
import { writeClient } from './utils/writeClient';
@@ -17,25 +14,7 @@ export { Indent } from './Indent';
1714
* Generate the OpenAPI client. This method will read the OpenAPI specification and based on the
1815
* given language it will generate the client, including the typed models, validation schemas,
1916
* service layer, etc.
20-
* @param autoformat Process generated files with autoformatter
21-
* @param clientName Custom client class name
22-
* @param exportCore Generate core client classes
23-
* @param exportModels Generate models
24-
* @param exportSchemas Generate schemas
25-
* @param exportServices Generate services
26-
* @param httpClient The selected httpClient (fetch, xhr, node or axios)
27-
* @param indent Indentation options (4, 2 or tab)
28-
* @param input The relative location of the OpenAPI spec
29-
* @param output The relative location of the output directory
30-
* @param postfixModels Model name postfix
31-
* @param postfixServices Service name postfix
32-
* @param request Path to custom request file
33-
* @param serviceResponse Define shape of returned value from service calls
34-
* @param useDateType Output Date instead of string for the format "date-time" in the models
35-
* @param useOperationId should the operationId be used when generating operation names
36-
* @param useOptions Use options or arguments functions
37-
* @param useUnionTypes Use union types instead of enums
38-
* @param write Write the files to disk (true or false)
17+
* @param options Options passed to the generate method
3918
*/
4019
export const generate = async (options: Options): Promise<void> => {
4120
const {
@@ -53,33 +32,15 @@ export const generate = async (options: Options): Promise<void> => {
5332
useUnionTypes = false,
5433
write = true,
5534
} = options;
56-
const openApi = isString(options.input) ? await getOpenApiSpec(options.input) : options.input;
57-
const openApiVersion = getOpenApiVersion(openApi);
35+
const openApi = typeof options.input === 'string' ? await getOpenApiSpec(options.input) : options.input;
36+
const parser = getOpenApiSpecParser(openApi);
5837
const templates = registerHandlebarTemplates({
5938
httpClient,
6039
serviceResponse,
6140
useUnionTypes,
6241
useOptions,
6342
});
6443

65-
let parser: typeof parseV2 | typeof parseV3;
66-
67-
switch (openApiVersion) {
68-
case OpenApiVersion.V2: {
69-
parser = parseV2;
70-
break;
71-
}
72-
73-
case OpenApiVersion.V3: {
74-
parser = parseV3;
75-
break;
76-
}
77-
}
78-
79-
if (!parser) {
80-
return;
81-
}
82-
8344
const client = parser(openApi, options);
8445
const clientFinal = postProcessClient(client);
8546
if (write) {

src/openApi/v3/index.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,5 +18,10 @@ export const parse = (openApi: OpenApi, options: Options): Client => {
1818
const models = getModels(openApi);
1919
const services = getServices(openApi, options);
2020

21-
return { version, server, models, services };
21+
return {
22+
models,
23+
server,
24+
services,
25+
version,
26+
};
2227
};
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import { getOpenApiSpecParser } from '../getOpenApiSpecParser';
2+
3+
jest.mock('../../openApi/v2', () => ({
4+
parse: 'parseV2',
5+
}));
6+
jest.mock('../../openApi/v3', () => ({
7+
parse: 'parseV3',
8+
}));
9+
10+
describe('getOpenApiSpecParser', () => {
11+
it('returns v2 parser', () => {
12+
expect(getOpenApiSpecParser({ openapi: '2' })).toEqual('parseV2');
13+
expect(getOpenApiSpecParser({ openapi: '2.0' })).toEqual('parseV2');
14+
15+
expect(getOpenApiSpecParser({ swagger: '2' })).toEqual('parseV2');
16+
expect(getOpenApiSpecParser({ swagger: '2.0' })).toEqual('parseV2');
17+
});
18+
19+
it('returns v3 parser', () => {
20+
expect(getOpenApiSpecParser({ openapi: '3' })).toEqual('parseV3');
21+
expect(getOpenApiSpecParser({ openapi: '3.0' })).toEqual('parseV3');
22+
expect(getOpenApiSpecParser({ openapi: '3.1.0' })).toEqual('parseV3');
23+
24+
expect(getOpenApiSpecParser({ swagger: '3' })).toEqual('parseV3');
25+
expect(getOpenApiSpecParser({ swagger: '3.0' })).toEqual('parseV3');
26+
expect(getOpenApiSpecParser({ swagger: '3.1.0' })).toEqual('parseV3');
27+
});
28+
29+
it('throws on unknown version', () => {
30+
expect(() => getOpenApiSpecParser({})).toThrow('Unsupported Open API version: "undefined"');
31+
expect(() => getOpenApiSpecParser({ swagger: '4.0' })).toThrow('Unsupported Open API version: "4.0"');
32+
});
33+
});

src/utils/getOpenApiSpecParser.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
import { parse as parseV2 } from '../openApi/v2';
2+
import { parse as parseV3 } from '../openApi/v3';
3+
4+
type SupportedMajorVersions = '2' | '3';
5+
6+
/**
7+
* @param openApi The loaded spec
8+
*/
9+
export const getOpenApiSpecParser = (openApi: Record<string, any>): typeof parseV2 | typeof parseV3 => {
10+
const versionString = openApi.swagger || openApi.openapi;
11+
if (typeof versionString === 'string') {
12+
const versionMajor = versionString.charAt(0) as SupportedMajorVersions;
13+
switch (versionMajor) {
14+
case '2':
15+
return parseV2;
16+
case '3':
17+
return parseV3;
18+
default:
19+
break;
20+
}
21+
}
22+
throw new Error(`Unsupported Open API version: "${String(versionString)}"`);
23+
};

src/utils/getOpenApiVersion.spec.ts

Lines changed: 0 additions & 15 deletions
This file was deleted.

src/utils/getOpenApiVersion.ts

Lines changed: 0 additions & 22 deletions
This file was deleted.

0 commit comments

Comments
 (0)