Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow ignoring operation #16

Merged
merged 4 commits into from
Feb 20, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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')
mrlubos marked this conversation as resolved.
Show resolved Hide resolved
.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')
mrlubos marked this conversation as resolved.
Show resolved Hide resolved
.replace(/\//g, '-');

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