@bitstack/ng-query-codegen-openapi 0.0.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/README.md +77 -0
  2. package/lib/bin/cli.mjs +186 -0
  3. package/lib/bin/cli.mjs.map +1 -0
  4. package/lib/index.d.mts +191 -0
  5. package/lib/index.d.ts +191 -0
  6. package/lib/index.js +1392 -0
  7. package/lib/index.js.map +1 -0
  8. package/lib/index.mjs +1376 -0
  9. package/lib/index.mjs.map +1 -0
  10. package/package.json +91 -0
  11. package/src/bin/cli.ts +77 -0
  12. package/src/bin/utils.ts +85 -0
  13. package/src/generators/api-service-generator.ts +112 -0
  14. package/src/generators/cache-keys-generator.ts +43 -0
  15. package/src/generators/common-types-generator.ts +33 -0
  16. package/src/generators/component-schema-generator.ts +25 -0
  17. package/src/generators/do-not-modify-generator.ts +27 -0
  18. package/src/generators/index-generator.ts +11 -0
  19. package/src/generators/query-service-generator.ts +108 -0
  20. package/src/generators/types-generator.ts +285 -0
  21. package/src/generators/utils-generator.ts +33 -0
  22. package/src/index.ts +21 -0
  23. package/src/services/api-code-generator.ts +157 -0
  24. package/src/services/api-service-generator.ts +24 -0
  25. package/src/services/endpoint-info-extractor.ts +119 -0
  26. package/src/services/file-writer-service.ts +148 -0
  27. package/src/services/group-service.ts +84 -0
  28. package/src/services/openapi-parser-service.ts +72 -0
  29. package/src/services/openapi-service.ts +61 -0
  30. package/src/services/query-code-generator.ts +24 -0
  31. package/src/services/unified-code-generator.ts +353 -0
  32. package/src/types.ts +248 -0
  33. package/src/utils/capitalize.ts +3 -0
  34. package/src/utils/directory.ts +75 -0
  35. package/src/utils/downloadSchema.ts +33 -0
  36. package/src/utils/factory.ts +29 -0
  37. package/src/utils/getOperationDefinitions.ts +20 -0
  38. package/src/utils/getV3Doc.ts +24 -0
  39. package/src/utils/http.ts +86 -0
  40. package/src/utils/index.ts +9 -0
  41. package/src/utils/isQuery.ts +16 -0
  42. package/src/utils/isValidUrl.ts +9 -0
  43. package/src/utils/messages.ts +7 -0
  44. package/src/utils/prettier.ts +51 -0
  45. package/src/utils/removeUndefined.ts +3 -0
@@ -0,0 +1,157 @@
1
+ import type { OpenAPIV3 } from 'openapi-types';
2
+ import ts from 'typescript';
3
+ import { OpenApiParserService } from './openapi-parser-service';
4
+ import { EndpointInfoExtractor } from './endpoint-info-extractor';
5
+ import { generateTypesFile } from '../generators/types-generator';
6
+ import { generateIndexFile } from '../generators/index-generator';
7
+ import { QueryCodeGenerator } from './query-code-generator';
8
+ import { ApiServiceGenerator } from './api-service-generator';
9
+ import type { GenerationOptions, GenerateApiResult } from '../types';
10
+
11
+ /**
12
+ * API 程式碼生成器 - 負責生成單一群組的 API 相關程式碼
13
+ *
14
+ * 設計理念:
15
+ * - 類別化管理:使用 class 封裝邏輯
16
+ * - 分離關注點:將 Query 和 API 服務分開生成
17
+ * - 重用資源:接受外部已處理的 v3Doc,避免重複處理
18
+ */
19
+ export class ApiCodeGenerator {
20
+ private infoExtractor: EndpointInfoExtractor;
21
+ private queryGenerator: QueryCodeGenerator;
22
+ private apiServiceGenerator: ApiServiceGenerator;
23
+
24
+ constructor(
25
+ private parserService: OpenApiParserService,
26
+ private options: GenerationOptions
27
+ ) {
28
+
29
+ // // 初始化端點資訊提取器
30
+ this.infoExtractor = new EndpointInfoExtractor(options);
31
+ //
32
+ // 初始化分離的生成器
33
+ this.queryGenerator = new QueryCodeGenerator(options);
34
+ this.apiServiceGenerator = new ApiServiceGenerator(options);
35
+ }
36
+
37
+ /**
38
+ * 生成完整的 API 程式碼
39
+ */
40
+ async generate(): Promise<GenerateApiResult> {
41
+ // console.log('ApiCodeGenerator: 開始生成 API 程式碼...');
42
+
43
+ // 步驟 1: 獲取操作定義
44
+ const operationDefinitions = this.parserService.getOperationDefinitions(this.options.filterEndpoints);
45
+
46
+ // 步驟 2: 提取端點資訊
47
+ const endpointInfos = this.infoExtractor.extractEndpointInfos(operationDefinitions);
48
+
49
+
50
+ // 步驟 3: 生成各種內容
51
+ const typesContent = this.generateTypes(endpointInfos);
52
+
53
+
54
+ const apiServiceContent = this.generateApiService(endpointInfos);
55
+ const queryServiceContent = this.generateQueryService(endpointInfos);
56
+ const indexContent = this.generateIndex();
57
+
58
+ // 步驟 4: 收集端點快取鍵資訊(只收集 Query 類型的端點)
59
+ const allEndpointCacheKeys = endpointInfos
60
+ .filter(info => info.isQuery) // 只收集查詢類型的端點
61
+ .map(info => ({
62
+ operationName: info.operationName,
63
+ queryKeyName: info.queryKeyName,
64
+ groupKey: this.options.groupKey || '_common'
65
+ }));
66
+
67
+ // 步驟 5: 收集操作名稱
68
+ const operationNames = endpointInfos.map(info => info.operationName);
69
+
70
+ // console.log('ApiCodeGenerator: API 程式碼生成完成');
71
+
72
+ return {
73
+ operationNames,
74
+ files: {
75
+ types: typesContent,
76
+ apiService: apiServiceContent,
77
+ queryService: queryServiceContent,
78
+ index: indexContent,
79
+ allEndpointCacheKeys: allEndpointCacheKeys
80
+ }
81
+ };
82
+ }
83
+
84
+ /**
85
+ * 生成 Types 檔案內容
86
+ */
87
+ private generateTypes(endpointInfos: Array<any>): string {
88
+ const generatorOptions = {
89
+ ...this.options,
90
+ apiConfiguration: this.options.apiConfiguration || {
91
+ file: '@/store/webapi',
92
+ importName: 'WebApiConfiguration'
93
+ }
94
+ };
95
+
96
+ // 從 parser service 獲取 schema interfaces
97
+ const apiGen = this.parserService.getApiGenerator();
98
+ const schemaInterfaces = apiGen.aliases.reduce<Record<string, ts.InterfaceDeclaration | ts.TypeAliasDeclaration>>((curr, alias) => {
99
+ if (ts.isInterfaceDeclaration(alias) || ts.isTypeAliasDeclaration(alias)) {
100
+ const name = alias.name.text;
101
+ return {
102
+ ...curr,
103
+ [name]: alias,
104
+ };
105
+ }
106
+ return curr;
107
+ }, {});
108
+
109
+ // 獲取操作定義以供類型生成使用
110
+ const operationDefinitions = this.parserService.getOperationDefinitions(this.options.filterEndpoints);
111
+
112
+ return generateTypesFile(endpointInfos, generatorOptions, schemaInterfaces, operationDefinitions);
113
+ }
114
+
115
+ /**
116
+ * 生成 API Service 檔案內容
117
+ */
118
+ private generateApiService(endpointInfos: Array<any>): string {
119
+ return this.apiServiceGenerator.generateApiService(endpointInfos);
120
+ }
121
+
122
+ /**
123
+ * 生成 Query Service 檔案內容
124
+ */
125
+ private generateQueryService(endpointInfos: Array<any>): string {
126
+ return this.queryGenerator.generateQueryService(endpointInfos);
127
+ }
128
+
129
+ /**
130
+ * 生成 Index 檔案內容
131
+ */
132
+ private generateIndex(): string {
133
+ const generatorOptions = {
134
+ ...this.options,
135
+ apiConfiguration: this.options.apiConfiguration || {
136
+ file: '@/store/webapi',
137
+ importName: 'WebApiConfiguration'
138
+ }
139
+ };
140
+
141
+ return generateIndexFile(this.options.groupKey || '', generatorOptions);
142
+ }
143
+
144
+ // /**
145
+ // * 獲取已解析的 parser service(供外部使用)
146
+ // */
147
+ // getParserService(): OpenApiParserService {
148
+ // return this.parserService;
149
+ // }
150
+ //
151
+ // /**
152
+ // * 獲取端點資訊提取器(供外部使用)
153
+ // */
154
+ // getInfoExtractor(): EndpointInfoExtractor {
155
+ // return this.infoExtractor;
156
+ // }
157
+ }
@@ -0,0 +1,24 @@
1
+ import type { GenerationOptions } from '../types';
2
+ import { generateApiServiceFile } from '../generators/api-service-generator';
3
+
4
+ /**
5
+ * API 服務程式碼生成器 - 專門負責生成 API Service 相關程式碼
6
+ */
7
+ export class ApiServiceGenerator {
8
+ constructor(private options: GenerationOptions) {}
9
+
10
+ /**
11
+ * 生成 API Service 檔案內容
12
+ */
13
+ generateApiService(endpointInfos: Array<any>): string {
14
+ const generatorOptions = {
15
+ ...this.options,
16
+ apiConfiguration: this.options.apiConfiguration || {
17
+ file: '@/store/webapi',
18
+ importName: 'WebApiConfiguration'
19
+ }
20
+ };
21
+
22
+ return generateApiServiceFile(endpointInfos, generatorOptions);
23
+ }
24
+ }
@@ -0,0 +1,119 @@
1
+ import { capitalize, isQuery as testIsQuery } from '../utils';
2
+ import { getOperationName, getOverrides } from '../utils/http';
3
+ import type { OperationDefinition, GenerationOptions } from '../types';
4
+ import { supportDeepObjects } from 'oazapfts/generate';
5
+
6
+ /**
7
+ * 端點資訊介面
8
+ */
9
+ export interface EndpointInfo {
10
+ operationName: string;
11
+ argTypeName: string;
12
+ responseTypeName: string;
13
+ isQuery: boolean;
14
+ verb: string;
15
+ path: string;
16
+ queryKeyName: string;
17
+ queryParams: any[];
18
+ pathParams: any[];
19
+ isVoidArg: boolean;
20
+ summary: string;
21
+ }
22
+
23
+ /**
24
+ * 端點資訊提取器 - 專門負責從操作定義中提取端點資訊
25
+ */
26
+ export class EndpointInfoExtractor {
27
+ constructor(
28
+ private options: Pick<GenerationOptions, 'operationNameSuffix' | 'argSuffix' | 'responseSuffix' | 'queryMatch' | 'endpointOverrides'>
29
+ ) {}
30
+
31
+ /**
32
+ * 從操作定義列表提取端點資訊
33
+ * @param operationDefinitions - 操作定義列表
34
+ */
35
+ extractEndpointInfos(operationDefinitions: OperationDefinition[]): EndpointInfo[] {
36
+ return operationDefinitions.map((operationDefinition) => {
37
+ return this.extractSingleEndpointInfo(operationDefinition);
38
+ });
39
+ }
40
+
41
+ /**
42
+ * 從單一操作定義提取端點資訊
43
+ * @param operationDefinition - 操作定義
44
+ */
45
+ private extractSingleEndpointInfo(operationDefinition: OperationDefinition): EndpointInfo {
46
+ const { verb, path, operation } = operationDefinition;
47
+ const { operationNameSuffix = '', argSuffix = 'Req', responseSuffix = 'Res', queryMatch, endpointOverrides } = this.options;
48
+
49
+ // 獲取操作名稱
50
+ const operationName = getOperationName({ verb, path });
51
+ const finalOperationName = operationNameSuffix ? capitalize(operationName + operationNameSuffix) : operationName;
52
+
53
+ // 生成類型名稱
54
+ const argTypeName = capitalize(operationName + operationNameSuffix + argSuffix);
55
+ const responseTypeName = capitalize(operationName + operationNameSuffix + responseSuffix);
56
+
57
+ // 判斷是否為查詢類型
58
+ const isQuery = testIsQuery(verb, path, getOverrides(operationDefinition, endpointOverrides), queryMatch);
59
+
60
+ // 生成查詢鍵名稱
61
+ const queryKeyName = `${operationName.replace(/([A-Z])/g, '_$1').toUpperCase()}`;
62
+
63
+ // 提取 OpenAPI summary
64
+ const summary = operation.summary || `${verb.toUpperCase()} ${path}`;
65
+
66
+ // 解析參數
67
+ const { queryParams, pathParams, isVoidArg } = this.extractParameters(operationDefinition);
68
+
69
+ return {
70
+ operationName: finalOperationName,
71
+ argTypeName,
72
+ responseTypeName,
73
+ isQuery,
74
+ verb: verb.toUpperCase(),
75
+ path,
76
+ queryKeyName,
77
+ queryParams,
78
+ pathParams,
79
+ isVoidArg,
80
+ summary
81
+ };
82
+ }
83
+
84
+ /**
85
+ * 提取操作的參數資訊
86
+ * @param operationDefinition - 操作定義
87
+ */
88
+ private extractParameters(operationDefinition: OperationDefinition) {
89
+ const { operation, pathItem } = operationDefinition;
90
+
91
+ // 解析參數
92
+ const operationParameters = this.resolveArray(operation.parameters);
93
+ const pathItemParameters = this.resolveArray(pathItem.parameters)
94
+ .filter((pp) => !operationParameters.some((op) => op.name === pp.name && op.in === pp.in));
95
+
96
+ const allParameters = supportDeepObjects([...pathItemParameters, ...operationParameters])
97
+ .filter((param) => param.in !== 'header');
98
+
99
+ const queryParams = allParameters.filter(param => param.in === 'query');
100
+ const pathParams = allParameters.filter(param => param.in === 'path');
101
+
102
+ // 檢查是否為 void 類型參數
103
+ const isVoidArg = queryParams.length === 0 && pathParams.length === 0 && !operation.requestBody;
104
+
105
+ return {
106
+ queryParams,
107
+ pathParams,
108
+ isVoidArg
109
+ };
110
+ }
111
+
112
+ /**
113
+ * 解析參數陣列 (模擬 apiGen.resolveArray)
114
+ */
115
+ private resolveArray(parameters: any): any[] {
116
+ if (!parameters) return [];
117
+ return Array.isArray(parameters) ? parameters : [parameters];
118
+ }
119
+ }
@@ -0,0 +1,148 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { ensureDirectoryExists } from '../utils/directory';
4
+
5
+ /**
6
+ * 檔案寫入結果
7
+ */
8
+ export interface FileWriteResult {
9
+ path: string;
10
+ success: boolean;
11
+ error?: Error;
12
+ }
13
+
14
+ /**
15
+ * 檔案寫入服務 - 負責將產生的內容寫入檔案
16
+ */
17
+ export class FileWriterService {
18
+ /**
19
+ * 寫入單一檔案
20
+ * @param filePath - 檔案路徑
21
+ * @param content - 檔案內容
22
+ */
23
+ async writeFile(filePath: string, content: string): Promise<FileWriteResult> {
24
+ try {
25
+ const resolvedPath = path.resolve(process.cwd(), filePath);
26
+ await ensureDirectoryExists(resolvedPath);
27
+
28
+ fs.writeFileSync(resolvedPath, content);
29
+
30
+ return {
31
+ path: resolvedPath,
32
+ success: true
33
+ };
34
+ } catch (error) {
35
+ return {
36
+ path: filePath,
37
+ success: false,
38
+ error: error as Error
39
+ };
40
+ }
41
+ }
42
+
43
+ /**
44
+ * 批次寫入多個檔案
45
+ * @param files - 檔案路徑與內容的對應表
46
+ */
47
+ async writeFiles(files: Record<string, string>): Promise<FileWriteResult[]> {
48
+ const results: FileWriteResult[] = [];
49
+
50
+ for (const [filePath, content] of Object.entries(files)) {
51
+ const result = await this.writeFile(filePath, content);
52
+ results.push(result);
53
+ }
54
+
55
+ return results;
56
+ }
57
+
58
+ /**
59
+ * 為群組寫入標準檔案結構
60
+ * @param groupOutputDir - 群組輸出目錄
61
+ * @param files - 檔案內容
62
+ */
63
+ async writeGroupFiles(
64
+ groupOutputDir: string,
65
+ files: {
66
+ types?: string;
67
+ apiService?: string;
68
+ queryService?: string;
69
+ index?: string;
70
+ }
71
+ ): Promise<FileWriteResult[]> {
72
+ const filesToWrite: Record<string, string> = {};
73
+
74
+ if (files.types) {
75
+ filesToWrite[path.join(groupOutputDir, 'types.ts')] = files.types;
76
+ }
77
+
78
+ if (files.apiService) {
79
+ filesToWrite[path.join(groupOutputDir, 'api.service.ts')] = files.apiService;
80
+ }
81
+
82
+ if (files.queryService) {
83
+ filesToWrite[path.join(groupOutputDir, 'query.service.ts')] = files.queryService;
84
+ }
85
+
86
+ if (files.index) {
87
+ filesToWrite[path.join(groupOutputDir, 'index.ts')] = files.index;
88
+ }
89
+
90
+ return this.writeFiles(filesToWrite);
91
+ }
92
+
93
+ /**
94
+ * 寫入共享檔案
95
+ * @param outputDir - 輸出目錄
96
+ * @param sharedFiles - 共享檔案內容
97
+ */
98
+ async writeSharedFiles(
99
+ outputDir: string,
100
+ sharedFiles: {
101
+ commonTypes?: string;
102
+ cacheKeys?: string;
103
+ doNotModify?: string;
104
+ utils?: string;
105
+ }
106
+ ): Promise<FileWriteResult[]> {
107
+ const filesToWrite: Record<string, string> = {};
108
+
109
+ if (sharedFiles.commonTypes) {
110
+ filesToWrite[path.join(outputDir, 'common-types.ts')] = sharedFiles.commonTypes;
111
+ }
112
+
113
+ if (sharedFiles.cacheKeys) {
114
+ filesToWrite[path.join(outputDir, 'cache-keys.ts')] = sharedFiles.cacheKeys;
115
+ }
116
+
117
+ if (sharedFiles.doNotModify) {
118
+ filesToWrite[path.join(outputDir, 'DO_NOT_MODIFY.md')] = sharedFiles.doNotModify;
119
+ }
120
+
121
+ if (sharedFiles.utils) {
122
+ filesToWrite[path.join(outputDir, 'utils.ts')] = sharedFiles.utils;
123
+ }
124
+
125
+ return this.writeFiles(filesToWrite);
126
+ }
127
+
128
+
129
+
130
+
131
+ /**
132
+ * 寫入共享檔案
133
+ * @param outputDir - 輸出目錄
134
+ * @param schema
135
+ */
136
+ async writeSchemaFile(
137
+ outputDir: string,
138
+ schema: string
139
+ ): Promise<FileWriteResult[]> {
140
+ const filesToWrite: Record<string, string> = {};
141
+
142
+ filesToWrite[path.join(outputDir, 'schema.ts')] = schema;
143
+
144
+ return this.writeFiles(filesToWrite);
145
+ }
146
+
147
+
148
+ }
@@ -0,0 +1,84 @@
1
+ import camelCase from 'lodash.camelcase';
2
+
3
+ /**
4
+ * 群組配置介面
5
+ */
6
+ export interface GroupConfig {
7
+ outputDir: string;
8
+ groupKeyMatch: (path: string) => string | null;
9
+ filterEndpoint?: (operationName: string, path: string, groupKey: string) => boolean;
10
+ queryMatch?: (operationName: string) => boolean;
11
+ }
12
+
13
+ /**
14
+ * 群組信息介面
15
+ */
16
+ export interface GroupInfo {
17
+ groupKey: string;
18
+ paths: string[];
19
+ outputPath: string;
20
+ }
21
+
22
+ /**
23
+ * 群組服務 - 負責處理 API 路徑分組
24
+ */
25
+ export class GroupService {
26
+ /**
27
+ * 根據配置對路徑進行分組
28
+ * @param paths - API 路徑陣列
29
+ * @param config - 群組配置
30
+ */
31
+ groupPaths(paths: string[], config: GroupConfig): Record<string, GroupInfo> {
32
+ const { groupKeyMatch, outputDir } = config;
33
+
34
+ const groupedPaths = paths.reduce((acc, path) => {
35
+ const rawGroupKey = groupKeyMatch(path);
36
+ const groupKey = rawGroupKey ? camelCase(rawGroupKey) : '_common';
37
+
38
+ if (!acc[groupKey]) {
39
+ acc[groupKey] = [];
40
+ }
41
+ acc[groupKey].push(path);
42
+ return acc;
43
+ }, {} as Record<string, string[]>);
44
+
45
+ // 轉換為 GroupInfo 格式
46
+ const result: Record<string, GroupInfo> = {};
47
+ for (const [groupKey, paths] of Object.entries(groupedPaths)) {
48
+ result[groupKey] = {
49
+ groupKey,
50
+ paths,
51
+ outputPath: `${outputDir}/${groupKey}/query.service.ts`
52
+ };
53
+ }
54
+
55
+ return result;
56
+ }
57
+
58
+ /**
59
+ * 為特定群組建立篩選函數
60
+ * @param groupKey - 群組鍵
61
+ * @param config - 群組配置
62
+ */
63
+ createGroupFilter(
64
+ groupKey: string,
65
+ config: GroupConfig
66
+ ): (operationName: string, operationDefinition: any) => boolean {
67
+ return (operationName: string, operationDefinition: any) => {
68
+ const path = operationDefinition.path;
69
+
70
+ // 檢查路徑是否匹配當前分組
71
+ const pathGroupKey = camelCase(config.groupKeyMatch(path) || '');
72
+ if (pathGroupKey !== groupKey && (pathGroupKey || '_common') !== groupKey) {
73
+ return false;
74
+ }
75
+
76
+ // 使用 filterEndpoint 進行額外篩選
77
+ if (config.filterEndpoint) {
78
+ return config.filterEndpoint(operationName, path, groupKey);
79
+ }
80
+
81
+ return true;
82
+ };
83
+ }
84
+ }
@@ -0,0 +1,72 @@
1
+ import ApiGenerator from 'oazapfts/generate';
2
+ import type { OpenAPIV3 } from 'openapi-types';
3
+ import type { GenerationOptions } from '../types';
4
+ import { getOperationDefinitions } from '../utils';
5
+
6
+ /**
7
+ * OpenAPI 解析器服務 - 負責解析 OpenAPI 文檔並提取相關數據
8
+ */
9
+ export class OpenApiParserService {
10
+ private apiGen: ApiGenerator;
11
+
12
+ constructor(
13
+ private v3Doc: OpenAPIV3.Document,
14
+ options: Partial<GenerationOptions>
15
+ ) {
16
+ this.apiGen = new ApiGenerator(v3Doc, {
17
+ unionUndefined: options.unionUndefined,
18
+ useEnumType: options.useEnumType,
19
+ mergeReadWriteOnly: options.mergeReadWriteOnly,
20
+ });
21
+ }
22
+
23
+ /**
24
+ * 初始化 - 預處理組件
25
+ */
26
+ initialize(): void {
27
+ if (this.apiGen.spec.components?.schemas) {
28
+ this.apiGen.preprocessComponents(this.apiGen.spec.components.schemas);
29
+
30
+ // 手動為每個 schema 生成 type alias
31
+ Object.keys(this.apiGen.spec.components.schemas).forEach(schemaName => {
32
+ try {
33
+ this.apiGen.getRefAlias({ $ref: `#/components/schemas/${schemaName}` });
34
+ } catch (error) {
35
+ // 忽略無法生成的 schema
36
+ }
37
+ });
38
+ }
39
+ }
40
+
41
+ /**
42
+ * 獲取操作定義列表
43
+ * @param filterEndpoints - 端點過濾函數
44
+ */
45
+ getOperationDefinitions(filterEndpoints?: any) {
46
+ const { operationMatches } = require('../utils/http');
47
+ return getOperationDefinitions(this.v3Doc).filter(operationMatches(filterEndpoints));
48
+ }
49
+
50
+ /**
51
+ * 獲取 API 生成器實例
52
+ */
53
+ getApiGenerator(): ApiGenerator {
54
+ return this.apiGen;
55
+ }
56
+
57
+ /**
58
+ * 獲取 OpenAPI 文檔
59
+ */
60
+ getDocument(): OpenAPIV3.Document {
61
+ return this.v3Doc;
62
+ }
63
+
64
+ /**
65
+ * 獲取所有 schema 類型名稱
66
+ */
67
+ getSchemaTypeNames(): Set<string> {
68
+ const schemeTypeNames = new Set<string>();
69
+ // 這裡可以根據需要添加 schema 類型名稱的提取邏輯
70
+ return schemeTypeNames;
71
+ }
72
+ }
@@ -0,0 +1,61 @@
1
+ import type { OpenAPIV3 } from 'openapi-types';
2
+ import { getV3Doc, downloadSchemaFile } from '../utils';
3
+
4
+ /**
5
+ * OpenAPI 服務 - 負責獲取和處理 OpenAPI 文檔
6
+ *
7
+ * 單一職責:
8
+ * - 從本地文件或遠程URL獲取OpenAPI文檔
9
+ * - 管理文檔快取
10
+ * - 提供文檔基本操作方法
11
+ *
12
+ * 設計原則:
13
+ * - 非收集模式:每次調用都是獨立的操作
14
+ * - 無副作用:不修改傳入的參數
15
+ * - 可測試性:所有方法都有明確的輸入輸出
16
+ */
17
+ export class OpenApiService {
18
+ private docCache: Record<string, OpenAPIV3.Document> = {};
19
+
20
+ /**
21
+ * 獲取 OpenAPI 文檔
22
+ * @param schemaLocation - Schema 位置 (URL 或本地路徑)
23
+ * @param httpResolverOptions - HTTP 解析選項
24
+ */
25
+ async getDocument(
26
+ schemaLocation: string,
27
+ httpResolverOptions?: any
28
+ ): Promise<OpenAPIV3.Document> {
29
+ if (this.docCache[schemaLocation]) {
30
+ return this.docCache[schemaLocation];
31
+ }
32
+
33
+ const doc = await getV3Doc(schemaLocation, httpResolverOptions);
34
+ this.docCache[schemaLocation] = doc;
35
+ return doc;
36
+ }
37
+
38
+ /**
39
+ * 下載遠程 Schema 文件
40
+ * @param remoteUrl - 遠程 URL
41
+ * @param localPath - 本地儲存路徑
42
+ */
43
+ async downloadSchema(remoteUrl: string, localPath: string): Promise<string> {
44
+ return downloadSchemaFile(remoteUrl, localPath);
45
+ }
46
+
47
+ /**
48
+ * 獲取所有 API 路徑
49
+ * @param doc - OpenAPI 文檔
50
+ */
51
+ getPaths(doc: OpenAPIV3.Document): string[] {
52
+ return Object.keys(doc.paths || {});
53
+ }
54
+
55
+ /**
56
+ * 清除快取
57
+ */
58
+ clearCache(): void {
59
+ this.docCache = {};
60
+ }
61
+ }
@@ -0,0 +1,24 @@
1
+ import type { GenerationOptions } from '../types';
2
+ import { generateQueryServiceFile } from '../generators/query-service-generator';
3
+
4
+ /**
5
+ * Query 服務程式碼生成器 - 專門負責生成 Query Service 相關程式碼
6
+ */
7
+ export class QueryCodeGenerator {
8
+ constructor(private options: GenerationOptions) {}
9
+
10
+ /**
11
+ * 生成 Query Service 檔案內容
12
+ */
13
+ generateQueryService(endpointInfos: Array<any>): string {
14
+ const generatorOptions = {
15
+ ...this.options,
16
+ apiConfiguration: this.options.apiConfiguration || {
17
+ file: '@/store/webapi',
18
+ importName: 'WebApiConfiguration'
19
+ }
20
+ };
21
+
22
+ return generateQueryServiceFile(endpointInfos, generatorOptions);
23
+ }
24
+ }