diff --git a/README.md b/README.md index 41d32010d..615375052 100644 --- a/README.md +++ b/README.md @@ -6,6 +6,7 @@ Mainly, it's because the original project maintainer [doesn't have time](https:/ - ability to select which services to export and naming strategies for generated methods - support for non-ASCII characters - support for x-body-name header (compatible with Connexion v3.x) +- ability to autoformat output with Prettier # OpenAPI Typescript Codegen diff --git a/bin/index.js b/bin/index.js index 56b9a498f..c0e465fac 100755 --- a/bin/index.js +++ b/bin/index.js @@ -20,10 +20,10 @@ const params = program .option('--exportCore ', 'Write core files to disk', true) .option('--exportServices ', 'Write services to disk', true) .option('--exportModels ', 'Write models to disk', true) - .option('--useOperationId ', 'Use operation id to generate operation names', true) .option('--exportSchemas ', 'Write schemas to disk', false) .option('--indent ', 'Indentation options [4, 2, tabs]', '4') .option('--postfixServices ', 'Service name postfix', 'Service') + .option('--useOperationId ', 'Use operation id to generate operation names', true) .option('--postfixModels ', 'Model name postfix') .option('--request ', 'Path to custom request file') .parse(process.argv) @@ -41,22 +41,22 @@ const parseBooleanOrString = value => { if (OpenAPI) { OpenAPI.generate({ - input: params.input, - output: params.output, - httpClient: params.client, - clientName: params.name, - useOptions: params.useOptions, - useUnionTypes: params.useUnionTypes, autoformat: JSON.parse(params.autoformat) === true, + clientName: params.name, exportCore: JSON.parse(params.exportCore) === true, - exportServices: parseBooleanOrString(params.exportServices), exportModels: parseBooleanOrString(params.exportModels), exportSchemas: JSON.parse(params.exportSchemas) === true, - useOperationId: JSON.parse(params.useOperationId) === true, + exportServices: parseBooleanOrString(params.exportServices), + httpClient: params.client, indent: params.indent, - postfixServices: params.postfixServices, + input: params.input, + output: params.output, postfixModels: params.postfixModels, + postfixServices: params.postfixServices, request: params.request, + useOperationId: JSON.parse(params.useOperationId) === true, + useOptions: params.useOptions, + useUnionTypes: params.useUnionTypes, }) .then(() => { process.exit(0); diff --git a/src/client/interfaces/Options.d.ts b/src/client/interfaces/Options.d.ts new file mode 100644 index 000000000..3d030a8d1 --- /dev/null +++ b/src/client/interfaces/Options.d.ts @@ -0,0 +1,19 @@ +export interface Options { + autoformat?: boolean; + clientName?: string; + exportCore?: boolean; + exportModels?: boolean | string; + exportSchemas?: boolean; + exportServices?: boolean | string; + httpClient?: HttpClient; + indent?: Indent; + input: string | Record; + output: string; + postfixModels?: string; + postfixServices?: string; + request?: string; + useOperationId?: boolean; + useOptions?: boolean; + useUnionTypes?: boolean; + write?: boolean; +} diff --git a/src/index.ts b/src/index.ts index fadabba73..f78c8b7ee 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,3 +1,4 @@ +import type { Options } from './client/interfaces/Options'; import { HttpClient } from './HttpClient'; import { Indent } from './Indent'; import { parse as parseV2 } from './openApi/v2'; @@ -12,26 +13,6 @@ import { writeClient } from './utils/writeClient'; export { HttpClient } from './HttpClient'; export { Indent } from './Indent'; -export type Options = { - input: string | Record; - output: string; - httpClient?: HttpClient; - clientName?: string; - useOptions?: boolean; - useUnionTypes?: boolean; - autoformat?: boolean; - exportCore?: boolean; - exportServices?: boolean | string; - exportModels?: boolean | string; - exportSchemas?: boolean; - useOperationId?: boolean; - indent?: Indent; - postfixServices?: string; - postfixModels?: string; - request?: string; - write?: boolean; -}; - /** * Generate the OpenAPI client. This method will read the OpenAPI specification and based on the * given language it will generate the client, including the typed models, validation schemas, @@ -54,26 +35,21 @@ export type Options = { * @param request Path to custom request file * @param write Write the files to disk (true or false) */ -export const generate = async ({ - input, - output, - httpClient = HttpClient.FETCH, - clientName, - useOptions = false, - useUnionTypes = false, - autoformat = false, - exportCore = true, - exportServices = true, - exportModels = true, - exportSchemas = false, - useOperationId = true, - indent = Indent.SPACE_4, - postfixServices = 'Service', - postfixModels = '', - request, - write = true, -}: Options): Promise => { - const openApi = isString(input) ? await getOpenApiSpec(input) : input; +export const generate = async (options: Options): Promise => { + const { + httpClient = HttpClient.FETCH, + useOptions = false, + useUnionTypes = false, + exportCore = true, + exportServices = true, + exportModels = true, + exportSchemas = false, + indent = Indent.SPACE_4, + postfixServices = 'Service', + postfixModels = '', + write = true, + } = options; + const openApi = isString(options.input) ? await getOpenApiSpec(options.input) : options.input; const openApiVersion = getOpenApiVersion(openApi); const templates = registerHandlebarTemplates({ httpClient, @@ -96,17 +72,16 @@ export const generate = async ({ } if (parser) { - const client = parser(openApi, useOperationId); + const client = parser(openApi, options); const clientFinal = postProcessClient(client); if (write) { await writeClient( clientFinal, templates, - output, + options.output, httpClient, useOptions, useUnionTypes, - autoformat, exportCore, exportServices, exportModels, @@ -114,8 +89,7 @@ export const generate = async ({ indent, postfixServices, postfixModels, - clientName, - request + options ); } } diff --git a/src/openApi/v2/index.ts b/src/openApi/v2/index.ts index 26eea55a0..643e6c41b 100644 --- a/src/openApi/v2/index.ts +++ b/src/openApi/v2/index.ts @@ -1,4 +1,5 @@ import type { Client } from '../../client/interfaces/Client'; +import type { Options } from '../../client/interfaces/Options'; import type { OpenApi } from './interfaces/OpenApi'; import { getModels } from './parser/getModels'; import { getServer } from './parser/getServer'; @@ -9,13 +10,13 @@ import { getServiceVersion } from './parser/getServiceVersion'; * Parse the OpenAPI specification to a Client model that contains * all the models, services and schema's we should output. * @param openApi The OpenAPI spec that we have loaded from disk. - * @param useOperationId should the operationId be used when generating operation names + * @param options Options passed to the generate method */ -export const parse = (openApi: OpenApi, useOperationId: boolean): Client => { +export const parse = (openApi: OpenApi, options: Options): Client => { const version = getServiceVersion(openApi.info.version); const server = getServer(openApi); const models = getModels(openApi); - const services = getServices(openApi, useOperationId); + const services = getServices(openApi, options); return { version, server, models, services }; }; diff --git a/src/openApi/v2/parser/getOperation.ts b/src/openApi/v2/parser/getOperation.ts index b1e147fff..f393ce791 100644 --- a/src/openApi/v2/parser/getOperation.ts +++ b/src/openApi/v2/parser/getOperation.ts @@ -1,5 +1,6 @@ import type { Operation } from '../../../client/interfaces/Operation'; import type { OperationParameters } from '../../../client/interfaces/OperationParameters'; +import type { Options } from '../../../client/interfaces/Options'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiOperation } from '../interfaces/OpenApiOperation'; import { getOperationErrors } from './getOperationErrors'; @@ -18,10 +19,10 @@ export const getOperation = ( tag: string, op: OpenApiOperation, pathParams: OperationParameters, - useOperationId: boolean + options: Options ): Operation => { const serviceName = getServiceName(tag); - const operationName = getOperationName(url, method, useOperationId, op.operationId); + const operationName = getOperationName(url, method, options, op.operationId); // Create a new operation object for this method. const operation: Operation = { diff --git a/src/openApi/v2/parser/getOperationName.spec.ts b/src/openApi/v2/parser/getOperationName.spec.ts index 1cd946463..f2ac934db 100644 --- a/src/openApi/v2/parser/getOperationName.spec.ts +++ b/src/openApi/v2/parser/getOperationName.spec.ts @@ -1,33 +1,51 @@ +import type { Options } from '../../../client/interfaces/Options'; import { getOperationName } from './getOperationName'; describe('getOperationName', () => { it('should produce correct result', () => { - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'GetAllUsers')).toEqual('getAllUsers'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, undefined)).toEqual('getApiUsers'); - expect(getOperationName('/api/v{api-version}/users', 'POST', true, undefined)).toEqual('postApiUsers'); - expect(getOperationName('/api/v1/users', 'GET', true, 'GetAllUsers')).toEqual('getAllUsers'); - expect(getOperationName('/api/v1/users', 'GET', true, undefined)).toEqual('getApiV1Users'); - expect(getOperationName('/api/v1/users', 'POST', true, undefined)).toEqual('postApiV1Users'); - expect(getOperationName('/api/v1/users/{id}', 'GET', true, undefined)).toEqual('getApiV1UsersById'); - expect(getOperationName('/api/v1/users/{id}', 'POST', true, undefined)).toEqual('postApiV1UsersById'); + const options: Options = { + input: '', + output: '', + }; + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'GetAllUsers')).toEqual('getAllUsers'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, undefined)).toEqual('getApiUsers'); + expect(getOperationName('/api/v{api-version}/users', 'POST', options, undefined)).toEqual('postApiUsers'); + expect(getOperationName('/api/v1/users', 'GET', options, 'GetAllUsers')).toEqual('getAllUsers'); + expect(getOperationName('/api/v1/users', 'GET', options, undefined)).toEqual('getApiV1Users'); + expect(getOperationName('/api/v1/users', 'POST', options, undefined)).toEqual('postApiV1Users'); + expect(getOperationName('/api/v1/users/{id}', 'GET', options, undefined)).toEqual('getApiV1UsersById'); + expect(getOperationName('/api/v1/users/{id}', 'POST', options, undefined)).toEqual('postApiV1UsersById'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'fooBar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'FooBar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'Foo Bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'foo bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'foo-bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'foo_bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '@foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '$foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '_foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '-foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '123.foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'fooBar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'FooBar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'Foo Bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'foo bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'foo-bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'foo_bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '@foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '$foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '_foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '-foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '123.foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v1/users', 'GET', false, 'GetAllUsers')).toEqual('getApiV1Users'); - expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'fooBar')).toEqual('getApiUsers'); + const optionsIgnoreOperationId: Options = { + ...options, + useOperationId: false, + }; + expect(getOperationName('/api/v1/users', 'GET', optionsIgnoreOperationId, 'GetAllUsers')).toEqual( + 'getApiV1Users' + ); + expect(getOperationName('/api/v{api-version}/users', 'GET', optionsIgnoreOperationId, 'fooBar')).toEqual( + 'getApiUsers' + ); expect( - getOperationName('/api/v{api-version}/users/{userId}/location/{locationId}', 'GET', false, 'fooBar') + getOperationName( + '/api/v{api-version}/users/{userId}/location/{locationId}', + 'GET', + optionsIgnoreOperationId, + 'fooBar' + ) ).toEqual('getApiUsersByUserIdLocationByLocationId'); }); }); diff --git a/src/openApi/v2/parser/getOperationName.ts b/src/openApi/v2/parser/getOperationName.ts index fb54bb575..40da445e3 100644 --- a/src/openApi/v2/parser/getOperationName.ts +++ b/src/openApi/v2/parser/getOperationName.ts @@ -1,5 +1,6 @@ import camelCase from 'camelcase'; +import type { Options } from '../../../client/interfaces/Options'; import sanitizeOperationName from '../../../utils/sanitizeOperationName'; /** @@ -7,12 +8,8 @@ import sanitizeOperationName from '../../../utils/sanitizeOperationName'; * This will use the operation ID - if available - and otherwise fallback * on a generated name from the URL */ -export const getOperationName = ( - url: string, - method: string, - useOperationId: boolean, - operationId?: string -): string => { +export const getOperationName = (url: string, method: string, options: Options, operationId?: string): string => { + const { useOperationId = true } = options; if (useOperationId && operationId) { return camelCase(sanitizeOperationName(operationId).trim()); } diff --git a/src/openApi/v2/parser/getServices.spec.ts b/src/openApi/v2/parser/getServices.spec.ts index 0d10d810b..72913f150 100644 --- a/src/openApi/v2/parser/getServices.spec.ts +++ b/src/openApi/v2/parser/getServices.spec.ts @@ -1,7 +1,13 @@ +import type { Options } from '../../../client/interfaces/Options'; import { getServices } from './getServices'; describe('getServices', () => { it('should create a unnamed service if tags are empty', () => { + const options: Options = { + input: '', + output: '', + useOperationId: false, + }; const services = getServices( { swagger: '2.0', @@ -25,7 +31,7 @@ describe('getServices', () => { }, }, }, - false + options ); expect(services).toHaveLength(1); diff --git a/src/openApi/v2/parser/getServices.ts b/src/openApi/v2/parser/getServices.ts index 596537cab..4ccbb8a8c 100644 --- a/src/openApi/v2/parser/getServices.ts +++ b/src/openApi/v2/parser/getServices.ts @@ -1,3 +1,4 @@ +import type { Options } from '../../../client/interfaces/Options'; import type { Service } from '../../../client/interfaces/Service'; import { unique } from '../../../utils/unique'; import type { OpenApi } from '../interfaces/OpenApi'; @@ -7,7 +8,7 @@ import { getOperationParameters } from './getOperationParameters'; /** * Get the OpenAPI services */ -export const getServices = (openApi: OpenApi, useOperationId: boolean): Service[] => { +export const getServices = (openApi: OpenApi, options: Options): Service[] => { const services = new Map(); for (const url in openApi.paths) { if (openApi.paths.hasOwnProperty(url)) { @@ -30,15 +31,7 @@ export const getServices = (openApi: OpenApi, useOperationId: boolean): Service[ const op = path[method]!; const tags = op.tags?.length ? op.tags.filter(unique) : ['Default']; tags.forEach(tag => { - const operation = getOperation( - openApi, - url, - method, - tag, - op, - pathParams, - useOperationId - ); + const operation = getOperation(openApi, url, method, tag, op, pathParams, options); // If we have already declared a service, then we should fetch that and // append the new method to it. Otherwise we should create a new service object. diff --git a/src/openApi/v3/index.ts b/src/openApi/v3/index.ts index 26eea55a0..643e6c41b 100644 --- a/src/openApi/v3/index.ts +++ b/src/openApi/v3/index.ts @@ -1,4 +1,5 @@ import type { Client } from '../../client/interfaces/Client'; +import type { Options } from '../../client/interfaces/Options'; import type { OpenApi } from './interfaces/OpenApi'; import { getModels } from './parser/getModels'; import { getServer } from './parser/getServer'; @@ -9,13 +10,13 @@ import { getServiceVersion } from './parser/getServiceVersion'; * Parse the OpenAPI specification to a Client model that contains * all the models, services and schema's we should output. * @param openApi The OpenAPI spec that we have loaded from disk. - * @param useOperationId should the operationId be used when generating operation names + * @param options Options passed to the generate method */ -export const parse = (openApi: OpenApi, useOperationId: boolean): Client => { +export const parse = (openApi: OpenApi, options: Options): Client => { const version = getServiceVersion(openApi.info.version); const server = getServer(openApi); const models = getModels(openApi); - const services = getServices(openApi, useOperationId); + const services = getServices(openApi, options); return { version, server, models, services }; }; diff --git a/src/openApi/v3/parser/getOperation.ts b/src/openApi/v3/parser/getOperation.ts index 27f7ad528..2d6ed70f8 100644 --- a/src/openApi/v3/parser/getOperation.ts +++ b/src/openApi/v3/parser/getOperation.ts @@ -1,5 +1,6 @@ import type { Operation } from '../../../client/interfaces/Operation'; import type { OperationParameters } from '../../../client/interfaces/OperationParameters'; +import type { Options } from '../../../client/interfaces/Options'; import type { OpenApi } from '../interfaces/OpenApi'; import type { OpenApiOperation } from '../interfaces/OpenApiOperation'; import type { OpenApiRequestBody } from '../interfaces/OpenApiRequestBody'; @@ -21,10 +22,10 @@ export const getOperation = ( tag: string, op: OpenApiOperation, pathParams: OperationParameters, - useOperationId: boolean + options: Options ): Operation => { const serviceName = getServiceName(tag); - const operationName = getOperationName(url, method, useOperationId, op.operationId); + const operationName = getOperationName(url, method, options, op.operationId); // Create a new operation object for this method. const operation: Operation = { diff --git a/src/openApi/v3/parser/getOperationName.spec.ts b/src/openApi/v3/parser/getOperationName.spec.ts index 1cd946463..f2ac934db 100644 --- a/src/openApi/v3/parser/getOperationName.spec.ts +++ b/src/openApi/v3/parser/getOperationName.spec.ts @@ -1,33 +1,51 @@ +import type { Options } from '../../../client/interfaces/Options'; import { getOperationName } from './getOperationName'; describe('getOperationName', () => { it('should produce correct result', () => { - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'GetAllUsers')).toEqual('getAllUsers'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, undefined)).toEqual('getApiUsers'); - expect(getOperationName('/api/v{api-version}/users', 'POST', true, undefined)).toEqual('postApiUsers'); - expect(getOperationName('/api/v1/users', 'GET', true, 'GetAllUsers')).toEqual('getAllUsers'); - expect(getOperationName('/api/v1/users', 'GET', true, undefined)).toEqual('getApiV1Users'); - expect(getOperationName('/api/v1/users', 'POST', true, undefined)).toEqual('postApiV1Users'); - expect(getOperationName('/api/v1/users/{id}', 'GET', true, undefined)).toEqual('getApiV1UsersById'); - expect(getOperationName('/api/v1/users/{id}', 'POST', true, undefined)).toEqual('postApiV1UsersById'); + const options: Options = { + input: '', + output: '', + }; + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'GetAllUsers')).toEqual('getAllUsers'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, undefined)).toEqual('getApiUsers'); + expect(getOperationName('/api/v{api-version}/users', 'POST', options, undefined)).toEqual('postApiUsers'); + expect(getOperationName('/api/v1/users', 'GET', options, 'GetAllUsers')).toEqual('getAllUsers'); + expect(getOperationName('/api/v1/users', 'GET', options, undefined)).toEqual('getApiV1Users'); + expect(getOperationName('/api/v1/users', 'POST', options, undefined)).toEqual('postApiV1Users'); + expect(getOperationName('/api/v1/users/{id}', 'GET', options, undefined)).toEqual('getApiV1UsersById'); + expect(getOperationName('/api/v1/users/{id}', 'POST', options, undefined)).toEqual('postApiV1UsersById'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'fooBar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'FooBar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'Foo Bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'foo bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'foo-bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'foo_bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '@foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '$foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '_foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '-foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v{api-version}/users', 'GET', true, '123.foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'fooBar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'FooBar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'Foo Bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'foo bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'foo-bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'foo_bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, 'foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '@foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '$foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '_foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '-foo.bar')).toEqual('fooBar'); + expect(getOperationName('/api/v{api-version}/users', 'GET', options, '123.foo.bar')).toEqual('fooBar'); - expect(getOperationName('/api/v1/users', 'GET', false, 'GetAllUsers')).toEqual('getApiV1Users'); - expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'fooBar')).toEqual('getApiUsers'); + const optionsIgnoreOperationId: Options = { + ...options, + useOperationId: false, + }; + expect(getOperationName('/api/v1/users', 'GET', optionsIgnoreOperationId, 'GetAllUsers')).toEqual( + 'getApiV1Users' + ); + expect(getOperationName('/api/v{api-version}/users', 'GET', optionsIgnoreOperationId, 'fooBar')).toEqual( + 'getApiUsers' + ); expect( - getOperationName('/api/v{api-version}/users/{userId}/location/{locationId}', 'GET', false, 'fooBar') + getOperationName( + '/api/v{api-version}/users/{userId}/location/{locationId}', + 'GET', + optionsIgnoreOperationId, + 'fooBar' + ) ).toEqual('getApiUsersByUserIdLocationByLocationId'); }); }); diff --git a/src/openApi/v3/parser/getOperationName.ts b/src/openApi/v3/parser/getOperationName.ts index fb54bb575..40da445e3 100644 --- a/src/openApi/v3/parser/getOperationName.ts +++ b/src/openApi/v3/parser/getOperationName.ts @@ -1,5 +1,6 @@ import camelCase from 'camelcase'; +import type { Options } from '../../../client/interfaces/Options'; import sanitizeOperationName from '../../../utils/sanitizeOperationName'; /** @@ -7,12 +8,8 @@ import sanitizeOperationName from '../../../utils/sanitizeOperationName'; * This will use the operation ID - if available - and otherwise fallback * on a generated name from the URL */ -export const getOperationName = ( - url: string, - method: string, - useOperationId: boolean, - operationId?: string -): string => { +export const getOperationName = (url: string, method: string, options: Options, operationId?: string): string => { + const { useOperationId = true } = options; if (useOperationId && operationId) { return camelCase(sanitizeOperationName(operationId).trim()); } diff --git a/src/openApi/v3/parser/getServices.spec.ts b/src/openApi/v3/parser/getServices.spec.ts index abd49fb91..6e1277c2f 100644 --- a/src/openApi/v3/parser/getServices.spec.ts +++ b/src/openApi/v3/parser/getServices.spec.ts @@ -1,8 +1,13 @@ -/* eslint-disable */ +import type { Options } from '../../../client/interfaces/Options'; import { getServices } from './getServices'; describe('getServices', () => { it('should create a unnamed service if tags are empty', () => { + const options: Options = { + input: '', + output: '', + useOperationId: false, + }; const services = getServices( { openapi: '3.0.0', @@ -26,7 +31,7 @@ describe('getServices', () => { }, }, }, - false, + options ); expect(services).toHaveLength(1); diff --git a/src/openApi/v3/parser/getServices.ts b/src/openApi/v3/parser/getServices.ts index 596537cab..4ccbb8a8c 100644 --- a/src/openApi/v3/parser/getServices.ts +++ b/src/openApi/v3/parser/getServices.ts @@ -1,3 +1,4 @@ +import type { Options } from '../../../client/interfaces/Options'; import type { Service } from '../../../client/interfaces/Service'; import { unique } from '../../../utils/unique'; import type { OpenApi } from '../interfaces/OpenApi'; @@ -7,7 +8,7 @@ import { getOperationParameters } from './getOperationParameters'; /** * Get the OpenAPI services */ -export const getServices = (openApi: OpenApi, useOperationId: boolean): Service[] => { +export const getServices = (openApi: OpenApi, options: Options): Service[] => { const services = new Map(); for (const url in openApi.paths) { if (openApi.paths.hasOwnProperty(url)) { @@ -30,15 +31,7 @@ export const getServices = (openApi: OpenApi, useOperationId: boolean): Service[ const op = path[method]!; const tags = op.tags?.length ? op.tags.filter(unique) : ['Default']; tags.forEach(tag => { - const operation = getOperation( - openApi, - url, - method, - tag, - op, - pathParams, - useOperationId - ); + const operation = getOperation(openApi, url, method, tag, op, pathParams, options); // If we have already declared a service, then we should fetch that and // append the new method to it. Otherwise we should create a new service object. diff --git a/src/utils/writeClient.spec.ts b/src/utils/writeClient.spec.ts index 2bfc3e284..922a21a2e 100644 --- a/src/utils/writeClient.spec.ts +++ b/src/utils/writeClient.spec.ts @@ -43,14 +43,17 @@ describe('writeClient', () => { HttpClient.FETCH, false, false, - false, true, true, true, true, Indent.SPACE_4, 'Service', - 'AppClient' + 'AppClient', + { + input: '', + output: '', + } ); expect(rmdir).toBeCalled(); diff --git a/src/utils/writeClient.ts b/src/utils/writeClient.ts index c1c06ba39..e56569437 100644 --- a/src/utils/writeClient.ts +++ b/src/utils/writeClient.ts @@ -3,6 +3,7 @@ import { createRequire } from 'module'; import { resolve } from 'path'; import type { Client } from '../client/interfaces/Client'; +import type { Options } from '../client/interfaces/Options'; import type { HttpClient } from '../HttpClient'; import type { Indent } from '../Indent'; import { mkdir, rmdir } from './fileSystem'; @@ -41,7 +42,6 @@ export const writeClient = async ( httpClient: HttpClient, useOptions: boolean, useUnionTypes: boolean, - autoformat: boolean, exportCore: boolean, exportServices: boolean | string, exportModels: boolean | string, @@ -49,8 +49,7 @@ export const writeClient = async ( indent: Indent, postfixServices: string, postfixModels: string, - clientName?: string, - request?: string + options: Options ): Promise => { const outputPath = resolve(process.cwd(), output); const outputPathCore = resolve(outputPath, 'core'); @@ -75,7 +74,15 @@ export const writeClient = async ( if (exportCore) { await rmdir(outputPathCore); await mkdir(outputPathCore); - await writeClientCore(client, templates, outputPathCore, httpClient, indent, clientName, request); + await writeClientCore( + client, + templates, + outputPathCore, + httpClient, + indent, + options.clientName, + options.request + ); } if (exportServices) { @@ -90,7 +97,7 @@ export const writeClient = async ( useOptions, indent, postfixServices, - clientName + options.clientName ); } @@ -106,9 +113,9 @@ export const writeClient = async ( await writeClientModels(client.models, templates, outputPathModels, httpClient, useUnionTypes, indent); } - if (isDefined(clientName)) { + if (isDefined(options.clientName)) { await mkdir(outputPath); - await writeClientClass(client, templates, outputPath, httpClient, clientName, indent, postfixServices); + await writeClientClass(client, templates, outputPath, httpClient, options.clientName, indent, postfixServices); } if (exportCore || exportServices || exportSchemas || exportModels) { @@ -124,11 +131,11 @@ export const writeClient = async ( exportSchemas, postfixServices, postfixModels, - clientName + options.clientName ); } - if (autoformat) { + if (options.autoformat) { const pathPackageJson = resolve(process.cwd(), 'package.json'); const require = createRequire('/'); const json = require(pathPackageJson);