Skip to content

Commit

Permalink
Merge pull request #16 from askvortcov/allowIgnoringOperationId
Browse files Browse the repository at this point in the history
Allow ignoring operation
  • Loading branch information
mrlubos authored Feb 20, 2024
2 parents efbfba5 + e4c3846 commit 920cf80
Show file tree
Hide file tree
Showing 14 changed files with 149 additions and 95 deletions.
2 changes: 2 additions & 0 deletions bin/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ const params = program
.option('--exportCore <value>', 'Write core files to disk', true)
.option('--exportServices <value>', 'Write services to disk', true)
.option('--exportModels <value>', 'Write models to disk', true)
.option('--useOperationId <value>', 'Use operation id to generate operation names', true)
.option('--exportSchemas <value>', 'Write schemas to disk', false)
.option('--indent <value>', 'Indentation options [4, 2, tabs]', '4')
.option('--postfixServices <value>', 'Service name postfix', 'Service')
Expand Down Expand Up @@ -51,6 +52,7 @@ if (OpenAPI) {
exportServices: parseBooleanOrString(params.exportServices),
exportModels: parseBooleanOrString(params.exportModels),
exportSchemas: JSON.parse(params.exportSchemas) === true,
useOperationId: JSON.parse(params.useOperationId) === true,
indent: params.indent,
postfixServices: params.postfixServices,
postfixModels: params.postfixModels,
Expand Down
5 changes: 4 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export type Options = {
exportServices?: boolean | string;
exportModels?: boolean | string;
exportSchemas?: boolean;
useOperationId?: boolean;
indent?: Indent;
postfixServices?: string;
postfixModels?: string;
Expand All @@ -45,6 +46,7 @@ export type Options = {
* @param exportServices Generate services
* @param exportModels Generate models
* @param exportSchemas Generate schemas
* @param ignoreOperationId Ignore operationId
* @param indent Indentation options (4, 2 or tab)
* @param postfixServices Service name postfix
* @param postfixModels Model name postfix
Expand All @@ -63,6 +65,7 @@ export const generate = async ({
exportServices = true,
exportModels = true,
exportSchemas = false,
useOperationId = true,
indent = Indent.SPACE_4,
postfixServices = 'Service',
postfixModels = '',
Expand Down Expand Up @@ -92,7 +95,7 @@ export const generate = async ({
}

if (parser) {
const client = parser(openApi);
const client = parser(openApi, useOperationId);
const clientFinal = postProcessClient(client);
if (write) {
await writeClient(
Expand Down
7 changes: 4 additions & 3 deletions src/openApi/v2/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ 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 openApi The OpenAPI spec that we have loaded from disk.
* @param useOperationId should the operationId be used when generating operation names
*/
export const parse = (openApi: OpenApi): Client => {
export const parse = (openApi: OpenApi, useOperationId: boolean): Client => {
const version = getServiceVersion(openApi.info.version);
const server = getServer(openApi);
const models = getModels(openApi);
const services = getServices(openApi);
const services = getServices(openApi, useOperationId);

return { version, server, models, services };
};
5 changes: 3 additions & 2 deletions src/openApi/v2/parser/getOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,10 +17,11 @@ export const getOperation = (
method: string,
tag: string,
op: OpenApiOperation,
pathParams: OperationParameters
pathParams: OperationParameters,
useOperationId: boolean
): Operation => {
const serviceName = getServiceName(tag);
const operationName = getOperationName(url, method, op.operationId);
const operationName = getOperationName(url, method, useOperationId, op.operationId);

// Create a new operation object for this method.
const operation: Operation = {
Expand Down
46 changes: 26 additions & 20 deletions src/openApi/v2/parser/getOperationName.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@ import { getOperationName } from './getOperationName';

describe('getOperationName', () => {
it('should produce correct result', () => {
expect(getOperationName('/api/v{api-version}/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
expect(getOperationName('/api/v{api-version}/users', 'GET', undefined)).toEqual('getApiUsers');
expect(getOperationName('/api/v{api-version}/users', 'POST', undefined)).toEqual('postApiUsers');
expect(getOperationName('/api/v1/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
expect(getOperationName('/api/v1/users', 'GET', undefined)).toEqual('getApiV1Users');
expect(getOperationName('/api/v1/users', 'POST', undefined)).toEqual('postApiV1Users');
expect(getOperationName('/api/v1/users/{id}', 'GET', undefined)).toEqual('getApiV1Users');
expect(getOperationName('/api/v1/users/{id}', 'POST', undefined)).toEqual('postApiV1Users');
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');

expect(getOperationName('/api/v{api-version}/users', 'GET', 'fooBar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'FooBar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'Foo Bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo-bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo_bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '@foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '$foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '_foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '-foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '123.foo.bar')).toEqual('fooBar');
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/v1/users', 'GET', false, 'GetAllUsers')).toEqual('getApiV1Users');
expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'fooBar')).toEqual('getApiUsers');
expect(
getOperationName('/api/v{api-version}/users/{userId}/location/{locationId}', 'GET', false, 'fooBar')
).toEqual('getApiUsersByUserIdLocationByLocationId');
});
});
11 changes: 8 additions & 3 deletions src/openApi/v2/parser/getOperationName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ 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, operationId?: string): string => {
if (operationId) {
export const getOperationName = (
url: string,
method: string,
useOperationId: boolean,
operationId?: string
): string => {
if (useOperationId && operationId) {
return camelCase(sanitizeOperationName(operationId).trim());
}

const urlWithoutPlaceholders = url
.replace(/[^/]*?{api-version}.*?\//g, '')
.replace(/{(.*?)}/g, '')
.replace(/{(.*?)}/g, 'by-$1')
.replace(/\//g, '-');

return camelCase(`${method}-${urlWithoutPlaceholders}`);
Expand Down
37 changes: 20 additions & 17 deletions src/openApi/v2/parser/getServices.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,28 +2,31 @@ import { getServices } from './getServices';

describe('getServices', () => {
it('should create a unnamed service if tags are empty', () => {
const services = getServices({
swagger: '2.0',
info: {
title: 'x',
version: '1',
},
paths: {
'/api/trips': {
get: {
tags: [],
responses: {
200: {
description: 'x',
},
default: {
description: 'default',
const services = getServices(
{
swagger: '2.0',
info: {
title: 'x',
version: '1',
},
paths: {
'/api/trips': {
get: {
tags: [],
responses: {
200: {
description: 'x',
},
default: {
description: 'default',
},
},
},
},
},
},
});
false
);

expect(services).toHaveLength(1);
expect(services[0].name).toEqual('Default');
Expand Down
12 changes: 10 additions & 2 deletions src/openApi/v2/parser/getServices.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import { getOperationParameters } from './getOperationParameters';
/**
* Get the OpenAPI services
*/
export const getServices = (openApi: OpenApi): Service[] => {
export const getServices = (openApi: OpenApi, useOperationId: boolean): Service[] => {
const services = new Map<string, Service>();
for (const url in openApi.paths) {
if (openApi.paths.hasOwnProperty(url)) {
Expand All @@ -30,7 +30,15 @@ export const getServices = (openApi: OpenApi): 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);
const operation = getOperation(
openApi,
url,
method,
tag,
op,
pathParams,
useOperationId
);

// 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.
Expand Down
7 changes: 4 additions & 3 deletions src/openApi/v3/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,14 @@ 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 openApi The OpenAPI spec that we have loaded from disk.
* @param useOperationId should the operationId be used when generating operation names
*/
export const parse = (openApi: OpenApi): Client => {
export const parse = (openApi: OpenApi, useOperationId: boolean): Client => {
const version = getServiceVersion(openApi.info.version);
const server = getServer(openApi);
const models = getModels(openApi);
const services = getServices(openApi);
const services = getServices(openApi, useOperationId);

return { version, server, models, services };
};
5 changes: 3 additions & 2 deletions src/openApi/v3/parser/getOperation.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@ export const getOperation = (
method: string,
tag: string,
op: OpenApiOperation,
pathParams: OperationParameters
pathParams: OperationParameters,
useOperationId: boolean
): Operation => {
const serviceName = getServiceName(tag);
const operationName = getOperationName(url, method, op.operationId);
const operationName = getOperationName(url, method, useOperationId, op.operationId);

// Create a new operation object for this method.
const operation: Operation = {
Expand Down
46 changes: 26 additions & 20 deletions src/openApi/v3/parser/getOperationName.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,26 +2,32 @@ import { getOperationName } from './getOperationName';

describe('getOperationName', () => {
it('should produce correct result', () => {
expect(getOperationName('/api/v{api-version}/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
expect(getOperationName('/api/v{api-version}/users', 'GET', undefined)).toEqual('getApiUsers');
expect(getOperationName('/api/v{api-version}/users', 'POST', undefined)).toEqual('postApiUsers');
expect(getOperationName('/api/v1/users', 'GET', 'GetAllUsers')).toEqual('getAllUsers');
expect(getOperationName('/api/v1/users', 'GET', undefined)).toEqual('getApiV1Users');
expect(getOperationName('/api/v1/users', 'POST', undefined)).toEqual('postApiV1Users');
expect(getOperationName('/api/v1/users/{id}', 'GET', undefined)).toEqual('getApiV1Users');
expect(getOperationName('/api/v1/users/{id}', 'POST', undefined)).toEqual('postApiV1Users');
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');

expect(getOperationName('/api/v{api-version}/users', 'GET', 'fooBar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'FooBar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'Foo Bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo-bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo_bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', 'foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '@foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '$foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '_foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '-foo.bar')).toEqual('fooBar');
expect(getOperationName('/api/v{api-version}/users', 'GET', '123.foo.bar')).toEqual('fooBar');
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/v1/users', 'GET', false, 'GetAllUsers')).toEqual('getApiV1Users');
expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'fooBar')).toEqual('getApiUsers');
expect(
getOperationName('/api/v{api-version}/users/{userId}/location/{locationId}', 'GET', false, 'fooBar')
).toEqual('getApiUsersByUserIdLocationByLocationId');
});
});
11 changes: 8 additions & 3 deletions src/openApi/v3/parser/getOperationName.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,19 @@ 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, operationId?: string): string => {
if (operationId) {
export const getOperationName = (
url: string,
method: string,
useOperationId: boolean,
operationId?: string
): string => {
if (useOperationId && operationId) {
return camelCase(sanitizeOperationName(operationId).trim());
}

const urlWithoutPlaceholders = url
.replace(/[^/]*?{api-version}.*?\//g, '')
.replace(/{(.*?)}/g, '')
.replace(/{(.*?)}/g, 'by-$1')
.replace(/\//g, '-');

return camelCase(`${method}-${urlWithoutPlaceholders}`);
Expand Down
Loading

0 comments on commit 920cf80

Please sign in to comment.