diff --git a/README.md b/README.md
index 5c257db..aa51adf 100644
--- a/README.md
+++ b/README.md
@@ -204,6 +204,34 @@ await client.user.getUsersById(opts);
**注意**:rpc-group模式下,如果没有提供tags,则默认合并到`default`分组
+### rpcName
+
+类型:`'method+uri' | 'operationId'`
+默认值:`'method+uri'`
+
+指定在rpc(-group)模式下方法名的生成规则。
+
+假设有这么一段openapi文档:
+
+```json
+{
+ "paths": {
+ "/client/users": {
+ "get": {
+ "operationId": "List_users",
+ "parameters": [],
+ "summary": "Users"
+ }
+ }
+ }
+}
+```
+
+1. 如果以 `method+uri` 的形式生成,则效果为:`openapi.getClientUsers()`
+2. 如果以 `operationId` 的形式生成,则效果为:`openapi.listUsers()`
+
+**注意**:如果operationId字段不存在,则仍以`method+uri`的规则生成
+
### onDocumentLoaded
类型:`(docs: Document) => Document | void`
diff --git a/src/bin.ts b/src/bin.ts
index 37474b9..90a6b09 100755
--- a/src/bin.ts
+++ b/src/bin.ts
@@ -10,7 +10,7 @@ import { pathToOpenapi } from './lib/path-to-openapi';
import { saveToFile } from './lib/save-to-file';
import { generateTemplate } from './lib/generate-template';
import { filterTag } from './lib/filter-tag';
-import { filterUrl } from './lib/filter-url';
+import { filterUri } from './lib/filter-uri';
import { readConfig } from './lib/read-config';
import { SilentSpinner } from './silent-spinner';
@@ -80,7 +80,7 @@ spinner.add({
},
task: async (ctx) => {
ctx.configs.forEach((config, i) => {
- filterUrl(ctx.docs[i]!, config);
+ filterUri(ctx.docs[i]!, config);
});
await sleep();
},
diff --git a/src/define-config.ts b/src/define-config.ts
index 03f031e..a7e7619 100644
--- a/src/define-config.ts
+++ b/src/define-config.ts
@@ -43,6 +43,29 @@ export interface OpenapiClientConfig {
* ```
*/
classMode?: 'rest' | 'rpc' | 'rpc-group';
+ /**
+ * 指定在rpc(-group)模式下方法名的生成规则。默认:`method+uri`
+ *
+ * 假设有这么一段openapi文档:
+ * ```json
+ * {
+ * "paths": {
+ * "/client/users": {
+ * "get": {
+ * "operationId": "List_users",
+ * "parameters": [],
+ * "summary": "Users"
+ * }
+ * }
+ * }
+ * }
+ * ```
+ * 1. 如果以 `method+uri` 的形式生成,则效果为:`openapi.getClientUsers()`
+ * 2. 如果以 `operationId` 的形式生成,则效果为:`openapi.listUsers()`
+ *
+ * 注意:如果operationId字段不存在,则仍以`method+uri`的规则生成
+ */
+ rpcName?: 'method+uri' | 'operationId';
/**
* 加载完openapi文档后的事件,允许直接对文档进行修改
*/
diff --git a/src/lib/document-to-meta.ts b/src/lib/document-to-meta.ts
index e5afcd2..d4d3185 100644
--- a/src/lib/document-to-meta.ts
+++ b/src/lib/document-to-meta.ts
@@ -4,6 +4,7 @@ import { snakeCase } from 'lodash-es';
import { parseParameters } from './parse-parameters';
import { parseRequestBody } from './parse-request-body';
import { parseResponse } from './parse-response';
+import type { OpenapiClientConfig } from '../define-config';
export type Metas = Record<
Methods,
@@ -11,6 +12,7 @@ export type Metas = Record<
key: string;
uri: string;
method: Methods;
+ operationId?: string;
contentTypes: string[];
query: { optional: boolean; types: [string] | [] };
params: { optional: boolean; types: [string] | [] };
@@ -23,7 +25,10 @@ export type Metas = Record<
}[]
>;
-export const documentToMeta = (docs: OpenAPIV3.Document) => {
+export const documentToMeta = (
+ docs: OpenAPIV3.Document,
+ rpcName: OpenapiClientConfig['rpcName'],
+) => {
const metas: Metas = {
get: [],
post: [],
@@ -39,7 +44,11 @@ export const documentToMeta = (docs: OpenAPIV3.Document) => {
metas[method].push({
uri,
method,
- key: snakeCase(`${method}_${uri.replaceAll(/{(.+?)}/g, '_by_$1')}`),
+ key: snakeCase(
+ rpcName === 'operationId' && methodItem.operationId
+ ? methodItem.operationId
+ : `${method}_${uri.replaceAll(/{(.+?)}/g, '_by_$1')}`,
+ ),
query: parseParameters(docs, pathItem, methodItem, 'query'),
params: parseParameters(docs, pathItem, methodItem, 'path'),
...parseRequestBody(docs, methodItem),
diff --git a/src/lib/filter-url.ts b/src/lib/filter-uri.ts
similarity index 89%
rename from src/lib/filter-url.ts
rename to src/lib/filter-uri.ts
index c282d6a..e87f7dd 100644
--- a/src/lib/filter-url.ts
+++ b/src/lib/filter-uri.ts
@@ -1,7 +1,7 @@
import type { OpenAPIV3 } from 'openapi-types';
import type { OpenapiClientConfig } from '../define-config';
-export const filterUrl = (docs: OpenAPIV3.Document, config: OpenapiClientConfig) => {
+export const filterUri = (docs: OpenAPIV3.Document, config: OpenapiClientConfig) => {
if (!config.includeUriPrefix) return;
const patterns = Array.isArray(config.includeUriPrefix)
? config.includeUriPrefix
diff --git a/src/lib/generate-template.ts b/src/lib/generate-template.ts
index 91e4769..a69bdfc 100644
--- a/src/lib/generate-template.ts
+++ b/src/lib/generate-template.ts
@@ -9,11 +9,11 @@ import { pickContentTypes } from './template';
export const generateTemplate = async (
docs: OpenAPIV3.Document,
- config: Pick,
+ config: Pick,
) => {
- const { projectName = '', classMode = 'rest' } = config;
+ const { projectName = '', classMode = 'rest', rpcName = 'method+uri' } = config;
const className = `OpenapiClient${upperFirst(camelCase(projectName))}`;
- const metas = documentToMeta(docs);
+ const metas = documentToMeta(docs, rpcName);
const classTpl =
classMode === 'rest'
diff --git a/test/bin.test.ts b/test/bin.test.ts
index 9c11cca..a437ca6 100644
--- a/test/bin.test.ts
+++ b/test/bin.test.ts
@@ -15,7 +15,7 @@ test('生成 bin.mjs', async () => {
expect(existsSync(path.resolve('dist', 'bin.mjs')));
});
-test('生成runtime并合并代码', { timeout: 9_000 }, async () => {
+test('写入指定文件', { timeout: 9_000 }, async () => {
execSync('node dist/bin.mjs', { encoding: 'utf8', stdio: 'inherit' });
expect(readFileSync(path.resolve('src', 'openapi', 'openapi.ts'), 'utf8')).toContain(
'export namespace OpenapiClient {',
diff --git a/test/lib/document-to-meta.test.ts b/test/lib/document-to-meta.test.ts
index 828baee..5c3492b 100644
--- a/test/lib/document-to-meta.test.ts
+++ b/test/lib/document-to-meta.test.ts
@@ -12,5 +12,20 @@ test('key从路由获取', () => {
},
});
- expect(documentToMeta(docs)['get'][0]!.key).toBe('get_users_by_id_by_name');
+ expect(documentToMeta(docs, 'method+uri')['get'][0]!.key).toBe(
+ 'get_users_by_id_by_name',
+ );
+});
+
+test('key从operationId获取', () => {
+ const docs = getBasicDocument({
+ '/users/{id}/{name}': {
+ get: {
+ operationId: 'Abc_def IJK',
+ responses: {},
+ },
+ },
+ });
+
+ expect(documentToMeta(docs, 'operationId')['get'][0]!.key).toBe('abc_def_ijk');
});
diff --git a/test/lib/filter-url.test.ts b/test/lib/filter-uri.test.ts
similarity index 81%
rename from test/lib/filter-url.test.ts
rename to test/lib/filter-uri.test.ts
index b714799..6c2ca25 100644
--- a/test/lib/filter-url.test.ts
+++ b/test/lib/filter-uri.test.ts
@@ -1,6 +1,6 @@
import { expect, test } from 'vitest';
import { getBasicDocument } from '../mocks/get-basic-document';
-import { filterUrl } from '../../src/lib/filter-url';
+import { filterUri } from '../../src/lib/filter-uri';
test('未指定过滤路由则不处理', () => {
const docs = getBasicDocument({
@@ -10,7 +10,7 @@ test('未指定过滤路由则不处理', () => {
'/other': {},
});
- filterUrl(docs, { url: '' });
+ filterUri(docs, { url: '' });
expect(Object.keys(docs.paths)).toMatchInlineSnapshot(`
[
"/",
@@ -19,7 +19,7 @@ test('未指定过滤路由则不处理', () => {
"/other",
]
`);
- filterUrl(docs, { url: '', includeUriPrefix: [] });
+ filterUri(docs, { url: '', includeUriPrefix: [] });
expect(Object.keys(docs.paths)).toMatchInlineSnapshot(`
[
"/",
@@ -38,7 +38,7 @@ test('不符合前缀的路由被删除', () => {
'/other': {},
});
- filterUrl(docs, { url: '', includeUriPrefix: '/test' });
+ filterUri(docs, { url: '', includeUriPrefix: '/test' });
expect(Object.keys(docs.paths)).toMatchInlineSnapshot(`
[
"/test/a",
@@ -56,7 +56,7 @@ test('同时过滤多个路由', () => {
'/foo/bar': {},
});
- filterUrl(docs, { url: '', includeUriPrefix: ['/test', '/foo'] });
+ filterUri(docs, { url: '', includeUriPrefix: ['/test', '/foo'] });
expect(Object.keys(docs.paths)).toMatchInlineSnapshot(`
[
"/test/a",
@@ -75,7 +75,7 @@ test('支持正则表达式', () => {
'/foo/bar': {},
});
- filterUrl(docs, { url: '', includeUriPrefix: [/\/a/] });
+ filterUri(docs, { url: '', includeUriPrefix: [/\/a/] });
expect(Object.keys(docs.paths)).toMatchInlineSnapshot(`
[
"/test/a",