From 8cd10e9b1572478a02cff5f2ec3502dace6a259d Mon Sep 17 00:00:00 2001
From: Alexander <askvortcov@gmail.com>
Date: Mon, 19 Feb 2024 23:42:02 +0100
Subject: [PATCH 1/3] allow operationId be ignored when generating operation
 names

---
 bin/index.js                                  |  2 +
 src/index.ts                                  |  5 +-
 src/openApi/v3/index.ts                       |  7 +--
 src/openApi/v3/parser/getOperation.ts         |  5 +-
 .../v3/parser/getOperationName.spec.ts        | 46 +++++++++++--------
 src/openApi/v3/parser/getOperationName.ts     | 11 +++--
 src/openApi/v3/parser/getServices.spec.ts     | 38 ++++++++-------
 src/openApi/v3/parser/getServices.ts          | 12 ++++-
 8 files changed, 78 insertions(+), 48 deletions(-)

diff --git a/bin/index.js b/bin/index.js
index 32f2fecbc..bae9a2b71 100755
--- a/bin/index.js
+++ b/bin/index.js
@@ -19,6 +19,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('--ignoreOperationId <value>', 'Use operation id', 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')
@@ -41,6 +42,7 @@ if (OpenAPI) {
         exportServices: JSON.parse(params.exportServices) === true,
         exportModels: JSON.parse(params.exportModels) === true,
         exportSchemas: JSON.parse(params.exportSchemas) === true,
+        ignoreOperationId: JSON.parse(params.ignoreOperationId) === true,
         indent: params.indent,
         postfixServices: params.postfixServices,
         postfixModels: params.postfixModels,
diff --git a/src/index.ts b/src/index.ts
index e63919085..211795fa4 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -23,6 +23,7 @@ export type Options = {
     exportServices?: boolean;
     exportModels?: boolean;
     exportSchemas?: boolean;
+    ignoreOperationId?: boolean;
     indent?: Indent;
     postfixServices?: string;
     postfixModels?: string;
@@ -44,6 +45,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
@@ -61,6 +63,7 @@ export const generate = async ({
     exportServices = true,
     exportModels = true,
     exportSchemas = false,
+    ignoreOperationId = false,
     indent = Indent.SPACE_4,
     postfixServices = 'Service',
     postfixModels = '',
@@ -101,7 +104,7 @@ export const generate = async ({
         }
 
         case OpenApiVersion.V3: {
-            const client = parseV3(openApi);
+            const client = parseV3(openApi, ignoreOperationId);
             const clientFinal = postProcessClient(client);
             if (!write) break;
             await writeClient(
diff --git a/src/openApi/v3/index.ts b/src/openApi/v3/index.ts
index 9dbdadb34..b5d60fadf 100644
--- a/src/openApi/v3/index.ts
+++ b/src/openApi/v3/index.ts
@@ -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 ignoreOperationId should the operationId be ignored when generating operation names
  */
-export const parse = (openApi: OpenApi): Client => {
+export const parse = (openApi: OpenApi, ignoreOperationId: boolean): Client => {
     const version = getServiceVersion(openApi.info.version);
     const server = getServer(openApi);
     const models = getModels(openApi);
-    const services = getServices(openApi);
+    const services = getServices(openApi, ignoreOperationId);
 
     return { version, server, models, services };
 };
diff --git a/src/openApi/v3/parser/getOperation.ts b/src/openApi/v3/parser/getOperation.ts
index aee4bd0c2..0b625f87f 100644
--- a/src/openApi/v3/parser/getOperation.ts
+++ b/src/openApi/v3/parser/getOperation.ts
@@ -20,10 +20,11 @@ export const getOperation = (
     method: string,
     tag: string,
     op: OpenApiOperation,
-    pathParams: OperationParameters
+    pathParams: OperationParameters,
+    ignoreOperationId: boolean
 ): Operation => {
     const serviceName = getServiceName(tag);
-    const operationName = getOperationName(url, method, op.operationId);
+    const operationName = getOperationName(url, method, ignoreOperationId, 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 bdaecb8f8..c0338160a 100644
--- a/src/openApi/v3/parser/getOperationName.spec.ts
+++ b/src/openApi/v3/parser/getOperationName.spec.ts
@@ -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', false, 'GetAllUsers')).toEqual('getAllUsers');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, undefined)).toEqual('getApiUsers');
+        expect(getOperationName('/api/v{api-version}/users', 'POST', false, undefined)).toEqual('postApiUsers');
+        expect(getOperationName('/api/v1/users', 'GET', false, 'GetAllUsers')).toEqual('getAllUsers');
+        expect(getOperationName('/api/v1/users', 'GET', false, undefined)).toEqual('getApiV1Users');
+        expect(getOperationName('/api/v1/users', 'POST', false, undefined)).toEqual('postApiV1Users');
+        expect(getOperationName('/api/v1/users/{id}', 'GET', false, undefined)).toEqual('getApiV1UsersById');
+        expect(getOperationName('/api/v1/users/{id}', 'POST', false, 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', false, 'fooBar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'FooBar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'Foo Bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'foo bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'foo-bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'foo_bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'foo.bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '@foo.bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '$foo.bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '_foo.bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '-foo.bar')).toEqual('fooBar');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '123.foo.bar')).toEqual('fooBar');
+
+        expect(getOperationName('/api/v1/users', 'GET', true, 'GetAllUsers')).toEqual('getApiV1Users');
+        expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'fooBar')).toEqual('getApiUsers');
+        expect(
+            getOperationName('/api/v{api-version}/users/{userId}/location/{locationId}', 'GET', true, 'fooBar')
+        ).toEqual('getApiUsersByUserIdLocationByLocationId');
     });
 });
diff --git a/src/openApi/v3/parser/getOperationName.ts b/src/openApi/v3/parser/getOperationName.ts
index 124bf66bd..67951fa56 100644
--- a/src/openApi/v3/parser/getOperationName.ts
+++ b/src/openApi/v3/parser/getOperationName.ts
@@ -5,8 +5,13 @@ import camelCase from 'camelcase';
  * 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,
+    ignoreOperationId: boolean,
+    operationId?: string
+): string => {
+    if (operationId && !ignoreOperationId) {
         return camelCase(
             operationId
                 .replace(/^[^a-zA-Z]+/g, '')
@@ -17,7 +22,7 @@ export const getOperationName = (url: string, method: string, operationId?: stri
 
     const urlWithoutPlaceholders = url
         .replace(/[^/]*?{api-version}.*?\//g, '')
-        .replace(/{(.*?)}/g, '')
+        .replace(/{(.*?)}/g, 'by-$1')
         .replace(/\//g, '-');
 
     return camelCase(`${method}-${urlWithoutPlaceholders}`);
diff --git a/src/openApi/v3/parser/getServices.spec.ts b/src/openApi/v3/parser/getServices.spec.ts
index baea07e66..abd49fb91 100644
--- a/src/openApi/v3/parser/getServices.spec.ts
+++ b/src/openApi/v3/parser/getServices.spec.ts
@@ -1,29 +1,33 @@
+/* eslint-disable */
 import { getServices } from './getServices';
 
 describe('getServices', () => {
     it('should create a unnamed service if tags are empty', () => {
-        const services = getServices({
-            openapi: '3.0.0',
-            info: {
-                title: 'x',
-                version: '1',
-            },
-            paths: {
-                '/api/trips': {
-                    get: {
-                        tags: [],
-                        responses: {
-                            200: {
-                                description: 'x',
-                            },
-                            default: {
-                                description: 'default',
+        const services = getServices(
+            {
+                openapi: '3.0.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');
diff --git a/src/openApi/v3/parser/getServices.ts b/src/openApi/v3/parser/getServices.ts
index d8fe411bb..93e03772e 100644
--- a/src/openApi/v3/parser/getServices.ts
+++ b/src/openApi/v3/parser/getServices.ts
@@ -7,7 +7,7 @@ import { getOperationParameters } from './getOperationParameters';
 /**
  * Get the OpenAPI services
  */
-export const getServices = (openApi: OpenApi): Service[] => {
+export const getServices = (openApi: OpenApi, ignoreOperationId: boolean): Service[] => {
     const services = new Map<string, Service>();
     for (const url in openApi.paths) {
         if (openApi.paths.hasOwnProperty(url)) {
@@ -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,
+                                    ignoreOperationId
+                                );
 
                                 // 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.

From c812bd0d13ec376bf88a05247b142cb5f20b1038 Mon Sep 17 00:00:00 2001
From: Alexander <askvortcov@gmail.com>
Date: Tue, 20 Feb 2024 10:04:12 +0100
Subject: [PATCH 2/3] change --igrnoreOperationId to --useOperationId; make the
 flag consistent throughout the codebase and documentation

---
 bin/index.js                                  |  4 +-
 src/index.ts                                  |  6 +--
 src/openApi/v3/index.ts                       |  6 +--
 src/openApi/v3/parser/getOperation.ts         |  4 +-
 .../v3/parser/getOperationName.spec.ts        | 46 +++++++++----------
 src/openApi/v3/parser/getOperationName.ts     |  4 +-
 src/openApi/v3/parser/getServices.ts          |  4 +-
 7 files changed, 37 insertions(+), 37 deletions(-)

diff --git a/bin/index.js b/bin/index.js
index bae9a2b71..a74ba7936 100755
--- a/bin/index.js
+++ b/bin/index.js
@@ -19,7 +19,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('--ignoreOperationId <value>', 'Use operation id', 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')
@@ -42,7 +42,7 @@ if (OpenAPI) {
         exportServices: JSON.parse(params.exportServices) === true,
         exportModels: JSON.parse(params.exportModels) === true,
         exportSchemas: JSON.parse(params.exportSchemas) === true,
-        ignoreOperationId: JSON.parse(params.ignoreOperationId) === true,
+        useOperationId: JSON.parse(params.useOperationId) === true,
         indent: params.indent,
         postfixServices: params.postfixServices,
         postfixModels: params.postfixModels,
diff --git a/src/index.ts b/src/index.ts
index 211795fa4..d3a38dc3a 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -23,7 +23,7 @@ export type Options = {
     exportServices?: boolean;
     exportModels?: boolean;
     exportSchemas?: boolean;
-    ignoreOperationId?: boolean;
+    useOperationId?: boolean;
     indent?: Indent;
     postfixServices?: string;
     postfixModels?: string;
@@ -63,7 +63,7 @@ export const generate = async ({
     exportServices = true,
     exportModels = true,
     exportSchemas = false,
-    ignoreOperationId = false,
+    useOperationId = true,
     indent = Indent.SPACE_4,
     postfixServices = 'Service',
     postfixModels = '',
@@ -104,7 +104,7 @@ export const generate = async ({
         }
 
         case OpenApiVersion.V3: {
-            const client = parseV3(openApi, ignoreOperationId);
+            const client = parseV3(openApi, useOperationId);
             const clientFinal = postProcessClient(client);
             if (!write) break;
             await writeClient(
diff --git a/src/openApi/v3/index.ts b/src/openApi/v3/index.ts
index b5d60fadf..26eea55a0 100644
--- a/src/openApi/v3/index.ts
+++ b/src/openApi/v3/index.ts
@@ -9,13 +9,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 ignoreOperationId should the operationId be ignored when generating operation names
+ * @param useOperationId should the operationId be used when generating operation names
  */
-export const parse = (openApi: OpenApi, ignoreOperationId: boolean): 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, ignoreOperationId);
+    const services = getServices(openApi, useOperationId);
 
     return { version, server, models, services };
 };
diff --git a/src/openApi/v3/parser/getOperation.ts b/src/openApi/v3/parser/getOperation.ts
index 0b625f87f..27f7ad528 100644
--- a/src/openApi/v3/parser/getOperation.ts
+++ b/src/openApi/v3/parser/getOperation.ts
@@ -21,10 +21,10 @@ export const getOperation = (
     tag: string,
     op: OpenApiOperation,
     pathParams: OperationParameters,
-    ignoreOperationId: boolean
+    useOperationId: boolean
 ): Operation => {
     const serviceName = getServiceName(tag);
-    const operationName = getOperationName(url, method, ignoreOperationId, op.operationId);
+    const operationName = getOperationName(url, method, useOperationId, 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 c0338160a..1cd946463 100644
--- a/src/openApi/v3/parser/getOperationName.spec.ts
+++ b/src/openApi/v3/parser/getOperationName.spec.ts
@@ -2,32 +2,32 @@ import { getOperationName } from './getOperationName';
 
 describe('getOperationName', () => {
     it('should produce correct result', () => {
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'GetAllUsers')).toEqual('getAllUsers');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, undefined)).toEqual('getApiUsers');
-        expect(getOperationName('/api/v{api-version}/users', 'POST', false, undefined)).toEqual('postApiUsers');
-        expect(getOperationName('/api/v1/users', 'GET', false, 'GetAllUsers')).toEqual('getAllUsers');
-        expect(getOperationName('/api/v1/users', 'GET', false, undefined)).toEqual('getApiV1Users');
-        expect(getOperationName('/api/v1/users', 'POST', false, undefined)).toEqual('postApiV1Users');
-        expect(getOperationName('/api/v1/users/{id}', 'GET', false, undefined)).toEqual('getApiV1UsersById');
-        expect(getOperationName('/api/v1/users/{id}', 'POST', false, undefined)).toEqual('postApiV1UsersById');
+        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', false, 'fooBar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'FooBar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'Foo Bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'foo bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'foo-bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'foo_bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, 'foo.bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '@foo.bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '$foo.bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '_foo.bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '-foo.bar')).toEqual('fooBar');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', false, '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', true, 'GetAllUsers')).toEqual('getApiV1Users');
-        expect(getOperationName('/api/v{api-version}/users', 'GET', true, 'fooBar')).toEqual('getApiUsers');
+        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', true, 'fooBar')
+            getOperationName('/api/v{api-version}/users/{userId}/location/{locationId}', 'GET', false, 'fooBar')
         ).toEqual('getApiUsersByUserIdLocationByLocationId');
     });
 });
diff --git a/src/openApi/v3/parser/getOperationName.ts b/src/openApi/v3/parser/getOperationName.ts
index 67951fa56..2493d7f91 100644
--- a/src/openApi/v3/parser/getOperationName.ts
+++ b/src/openApi/v3/parser/getOperationName.ts
@@ -8,10 +8,10 @@ import camelCase from 'camelcase';
 export const getOperationName = (
     url: string,
     method: string,
-    ignoreOperationId: boolean,
+    useOperationId: boolean,
     operationId?: string
 ): string => {
-    if (operationId && !ignoreOperationId) {
+    if (useOperationId && operationId) {
         return camelCase(
             operationId
                 .replace(/^[^a-zA-Z]+/g, '')
diff --git a/src/openApi/v3/parser/getServices.ts b/src/openApi/v3/parser/getServices.ts
index 93e03772e..596537cab 100644
--- a/src/openApi/v3/parser/getServices.ts
+++ b/src/openApi/v3/parser/getServices.ts
@@ -7,7 +7,7 @@ import { getOperationParameters } from './getOperationParameters';
 /**
  * Get the OpenAPI services
  */
-export const getServices = (openApi: OpenApi, ignoreOperationId: boolean): 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)) {
@@ -37,7 +37,7 @@ export const getServices = (openApi: OpenApi, ignoreOperationId: boolean): Servi
                                     tag,
                                     op,
                                     pathParams,
-                                    ignoreOperationId
+                                    useOperationId
                                 );
 
                                 // If we have already declared a service, then we should fetch that and

From af4dfb7c02e7d279ba2788e088217ce6f8d9fb32 Mon Sep 17 00:00:00 2001
From: Alexander <askvortcov@gmail.com>
Date: Tue, 20 Feb 2024 11:05:13 +0100
Subject: [PATCH 3/3] propagate useVersionId to swagger v2 parser

---
 src/index.ts                                  |  2 +-
 src/openApi/v2/index.ts                       |  7 +--
 src/openApi/v2/parser/getOperation.ts         |  5 +-
 .../v2/parser/getOperationName.spec.ts        | 46 +++++++++++--------
 src/openApi/v2/parser/getOperationName.ts     | 11 +++--
 src/openApi/v2/parser/getServices.spec.ts     | 37 ++++++++-------
 src/openApi/v2/parser/getServices.ts          | 12 ++++-
 7 files changed, 72 insertions(+), 48 deletions(-)

diff --git a/src/index.ts b/src/index.ts
index d3a38dc3a..38f12ae85 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -80,7 +80,7 @@ export const generate = async ({
 
     switch (openApiVersion) {
         case OpenApiVersion.V2: {
-            const client = parseV2(openApi);
+            const client = parseV2(openApi, useOperationId);
             const clientFinal = postProcessClient(client);
             if (!write) break;
             await writeClient(
diff --git a/src/openApi/v2/index.ts b/src/openApi/v2/index.ts
index 9dbdadb34..26eea55a0 100644
--- a/src/openApi/v2/index.ts
+++ b/src/openApi/v2/index.ts
@@ -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 };
 };
diff --git a/src/openApi/v2/parser/getOperation.ts b/src/openApi/v2/parser/getOperation.ts
index 9aa157460..b1e147fff 100644
--- a/src/openApi/v2/parser/getOperation.ts
+++ b/src/openApi/v2/parser/getOperation.ts
@@ -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 = {
diff --git a/src/openApi/v2/parser/getOperationName.spec.ts b/src/openApi/v2/parser/getOperationName.spec.ts
index bdaecb8f8..1cd946463 100644
--- a/src/openApi/v2/parser/getOperationName.spec.ts
+++ b/src/openApi/v2/parser/getOperationName.spec.ts
@@ -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');
     });
 });
diff --git a/src/openApi/v2/parser/getOperationName.ts b/src/openApi/v2/parser/getOperationName.ts
index 124bf66bd..2493d7f91 100644
--- a/src/openApi/v2/parser/getOperationName.ts
+++ b/src/openApi/v2/parser/getOperationName.ts
@@ -5,8 +5,13 @@ import camelCase from 'camelcase';
  * 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(
             operationId
                 .replace(/^[^a-zA-Z]+/g, '')
@@ -17,7 +22,7 @@ export const getOperationName = (url: string, method: string, operationId?: stri
 
     const urlWithoutPlaceholders = url
         .replace(/[^/]*?{api-version}.*?\//g, '')
-        .replace(/{(.*?)}/g, '')
+        .replace(/{(.*?)}/g, 'by-$1')
         .replace(/\//g, '-');
 
     return camelCase(`${method}-${urlWithoutPlaceholders}`);
diff --git a/src/openApi/v2/parser/getServices.spec.ts b/src/openApi/v2/parser/getServices.spec.ts
index 915ee6b09..0d10d810b 100644
--- a/src/openApi/v2/parser/getServices.spec.ts
+++ b/src/openApi/v2/parser/getServices.spec.ts
@@ -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');
diff --git a/src/openApi/v2/parser/getServices.ts b/src/openApi/v2/parser/getServices.ts
index d8fe411bb..596537cab 100644
--- a/src/openApi/v2/parser/getServices.ts
+++ b/src/openApi/v2/parser/getServices.ts
@@ -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)) {
@@ -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.