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",