@g1cloud/api-gen 1.0.0
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.
- package/.claude/settings.local.json +22 -0
- package/CLAUDE.md +63 -0
- package/README.md +379 -0
- package/dist/analyzer/controllerAnalyzer.d.ts +20 -0
- package/dist/analyzer/controllerAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/controllerAnalyzer.js +101 -0
- package/dist/analyzer/controllerAnalyzer.js.map +1 -0
- package/dist/analyzer/parameterAnalyzer.d.ts +19 -0
- package/dist/analyzer/parameterAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/parameterAnalyzer.js +207 -0
- package/dist/analyzer/parameterAnalyzer.js.map +1 -0
- package/dist/analyzer/responseAnalyzer.d.ts +12 -0
- package/dist/analyzer/responseAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/responseAnalyzer.js +116 -0
- package/dist/analyzer/responseAnalyzer.js.map +1 -0
- package/dist/analyzer/schemaGenerator.d.ts +6 -0
- package/dist/analyzer/schemaGenerator.d.ts.map +1 -0
- package/dist/analyzer/schemaGenerator.js +347 -0
- package/dist/analyzer/schemaGenerator.js.map +1 -0
- package/dist/analyzer/securityAnalyzer.d.ts +6 -0
- package/dist/analyzer/securityAnalyzer.d.ts.map +1 -0
- package/dist/analyzer/securityAnalyzer.js +177 -0
- package/dist/analyzer/securityAnalyzer.js.map +1 -0
- package/dist/generator/openapiGenerator.d.ts +14 -0
- package/dist/generator/openapiGenerator.d.ts.map +1 -0
- package/dist/generator/openapiGenerator.js +340 -0
- package/dist/generator/openapiGenerator.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +218 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +61 -0
- package/dist/lib.d.ts.map +1 -0
- package/dist/lib.js +199 -0
- package/dist/lib.js.map +1 -0
- package/dist/mcp-server.d.ts +9 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +257 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/mcp-server.mjs +45586 -0
- package/dist/parser/astAnalyzer.d.ts +87 -0
- package/dist/parser/astAnalyzer.d.ts.map +1 -0
- package/dist/parser/astAnalyzer.js +321 -0
- package/dist/parser/astAnalyzer.js.map +1 -0
- package/dist/parser/javaParser.d.ts +10 -0
- package/dist/parser/javaParser.d.ts.map +1 -0
- package/dist/parser/javaParser.js +805 -0
- package/dist/parser/javaParser.js.map +1 -0
- package/dist/types/index.d.ts +217 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/examples/CreateUserRequest.java +80 -0
- package/examples/DepartmentDTO.java +45 -0
- package/examples/Filter.java +39 -0
- package/examples/PaginatedList.java +71 -0
- package/examples/ProductController.java +136 -0
- package/examples/ProductDTO.java +129 -0
- package/examples/RoleDTO.java +47 -0
- package/examples/SearchParam.java +55 -0
- package/examples/Sort.java +70 -0
- package/examples/UpdateUserRequest.java +74 -0
- package/examples/UserController.java +98 -0
- package/examples/UserDTO.java +119 -0
- package/package.json +51 -0
- package/prompt/01_Initial.md +358 -0
- package/prompt/02_/354/266/224/352/260/200.md +31 -0
- package/src/analyzer/controllerAnalyzer.ts +125 -0
- package/src/analyzer/parameterAnalyzer.ts +259 -0
- package/src/analyzer/responseAnalyzer.ts +142 -0
- package/src/analyzer/schemaGenerator.ts +412 -0
- package/src/analyzer/securityAnalyzer.ts +200 -0
- package/src/generator/openapiGenerator.ts +378 -0
- package/src/index.ts +212 -0
- package/src/lib.ts +240 -0
- package/src/mcp-server.ts +310 -0
- package/src/parser/astAnalyzer.ts +373 -0
- package/src/parser/javaParser.ts +901 -0
- package/src/types/index.ts +238 -0
- package/test-boolean.yaml +607 -0
- package/test-filter.yaml +576 -0
- package/test-inner.ts +59 -0
- package/test-output.yaml +650 -0
- package/test-paginated.yaml +585 -0
- package/tsconfig.json +20 -0
- package/tsup.config.ts +30 -0
|
@@ -0,0 +1,358 @@
|
|
|
1
|
+
# Spring Boot → OpenAPI v3 YAML 생성기 개발
|
|
2
|
+
|
|
3
|
+
TypeScript로 Spring Boot 애플리케이션의 Java 소스코드를 분석하여 OpenAPI Specification v3 기반의 YAML 파일을 자동 생성하는 CLI 도구를 만들어주세요. 도구 이름은 `@g1cloud/api-gen` 으로 해주고, 현재 디렉토리 아래에 `api-gen` 디렉토리를 만들어 프로젝트 루트 디렉토리로 사용하세요.
|
|
4
|
+
|
|
5
|
+
## 주요 기능 요구사항
|
|
6
|
+
|
|
7
|
+
### 1. Java 소스코드 파싱
|
|
8
|
+
- `java-parser` 또는 `tree-sitter-java` NPM 패키지를 사용하여 Java 파일 파싱
|
|
9
|
+
- AST(Abstract Syntax Tree)를 분석하여 어노테이션 및 메서드 시그니처 추출
|
|
10
|
+
|
|
11
|
+
### 2. RestController 분석
|
|
12
|
+
다음 어노테이션들을 분석:
|
|
13
|
+
- `@RestController` / `@Controller` + `@ResponseBody`
|
|
14
|
+
- `@RequestMapping` (클래스/메서드 레벨)
|
|
15
|
+
- `@GetMapping`, `@PostMapping`, `@PutMapping`, `@DeleteMapping`, `@PatchMapping`
|
|
16
|
+
|
|
17
|
+
### 3. 입력 파라미터 분석
|
|
18
|
+
각 엔드포인트의 파라미터를 분석하여 OpenAPI 스키마 생성:
|
|
19
|
+
- `@RequestParam` → query parameters
|
|
20
|
+
- `@PathVariable` → path parameters
|
|
21
|
+
- `@RequestBody` → request body schema
|
|
22
|
+
- `@RequestHeader` → header parameters
|
|
23
|
+
- 파라미터의 타입, 필수 여부(`required`), 기본값(`defaultValue`) 추출
|
|
24
|
+
|
|
25
|
+
#### 특수 타입 처리: SearchParam
|
|
26
|
+
파라미터에 `SearchParam` 타입의 객체가 있는 경우, 다음 query parameters를 자동으로 추가:
|
|
27
|
+
- `offset` (integer) - 페이지네이션 오프셋
|
|
28
|
+
- `limit` (integer) - 페이지네이션 제한
|
|
29
|
+
- `filter` (Filter 타입) - 필터 조건 (객체 스키마로 확장)
|
|
30
|
+
- `sort` (Sort 타입) - 정렬 조건 (객체 스키마로 확장)
|
|
31
|
+
```yaml
|
|
32
|
+
# SearchParam 예시
|
|
33
|
+
parameters:
|
|
34
|
+
- name: offset
|
|
35
|
+
in: query
|
|
36
|
+
schema:
|
|
37
|
+
type: integer
|
|
38
|
+
description: Pagination offset
|
|
39
|
+
- name: limit
|
|
40
|
+
in: query
|
|
41
|
+
schema:
|
|
42
|
+
type: integer
|
|
43
|
+
description: Pagination limit
|
|
44
|
+
- name: filter
|
|
45
|
+
in: query
|
|
46
|
+
schema:
|
|
47
|
+
$ref: '#/components/schemas/Filter'
|
|
48
|
+
- name: sort
|
|
49
|
+
in: query
|
|
50
|
+
schema:
|
|
51
|
+
$ref: '#/components/schemas/Sort'
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
### 4. 출력 데이터 형식 분석
|
|
55
|
+
- 메서드 리턴 타입 분석 (`ResponseEntity<T>`, DTO 클래스 등)
|
|
56
|
+
- 리턴 타입의 클래스 구조를 분석하여 response schema 생성
|
|
57
|
+
- 중첩 객체(nested objects), 제네릭 타입 지원
|
|
58
|
+
- HTTP 상태 코드 추출
|
|
59
|
+
|
|
60
|
+
#### 특수 타입 처리: PaginatedList
|
|
61
|
+
리턴 타입이 `PaginatedList` 또는 `PaginatedList<T>` 인 경우, response headers에 다음을 추가:
|
|
62
|
+
- `X-Total-Count` (integer) - 전체 아이템 개수
|
|
63
|
+
- `X-Offset` (integer) - 현재 오프셋
|
|
64
|
+
- `X-Limit` (integer) - 현재 제한 값
|
|
65
|
+
```yaml
|
|
66
|
+
# PaginatedList 예시
|
|
67
|
+
responses:
|
|
68
|
+
'200':
|
|
69
|
+
description: Successful response
|
|
70
|
+
headers:
|
|
71
|
+
X-Total-Count:
|
|
72
|
+
schema:
|
|
73
|
+
type: integer
|
|
74
|
+
description: Total number of items
|
|
75
|
+
X-Offset:
|
|
76
|
+
schema:
|
|
77
|
+
type: integer
|
|
78
|
+
description: Current offset
|
|
79
|
+
X-Limit:
|
|
80
|
+
schema:
|
|
81
|
+
type: integer
|
|
82
|
+
description: Current limit
|
|
83
|
+
content:
|
|
84
|
+
application/json:
|
|
85
|
+
schema:
|
|
86
|
+
$ref: '#/components/schemas/PaginatedListUserDTO'
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
### 5. 보안/권한 분석
|
|
90
|
+
- `@PreAuthorize` / `@PostAuthorize` 어노테이션 분석
|
|
91
|
+
- `@Secured` 어노테이션 분석
|
|
92
|
+
- SpEL 표현식에서 역할(role) 및 권한(authority) 추출
|
|
93
|
+
- `hasRole('ADMIN')` → ROLE_ADMIN
|
|
94
|
+
- `hasAnyRole('USER', 'ADMIN')` → [ROLE_USER, ROLE_ADMIN]
|
|
95
|
+
- `hasAuthority('READ_PRIVILEGE')` → READ_PRIVILEGE
|
|
96
|
+
- **복잡한 SpEL 표현식 처리**: `hasRole`, `hasAnyRole`, `hasAuthority` 이외의 구문이 포함된 경우, 전체 SpEL 표현식을 `x-security-expression` 확장 필드에 그대로 기록
|
|
97
|
+
```yaml
|
|
98
|
+
# 복잡한 SpEL 표현식 예시
|
|
99
|
+
paths:
|
|
100
|
+
/api/users/{id}:
|
|
101
|
+
put:
|
|
102
|
+
security:
|
|
103
|
+
- bearerAuth: []
|
|
104
|
+
x-required-roles:
|
|
105
|
+
- ROLE_USER
|
|
106
|
+
x-security-expression: "hasRole('ADMIN') or (#user.id == #id and hasRole('USER'))"
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
- OpenAPI의 `security` 필드 또는 `x-required-roles` 확장 필드에 문서화
|
|
110
|
+
|
|
111
|
+
### 6. DTO 스키마 생성
|
|
112
|
+
- `@RequestBody`와 리턴 타입에 사용된 DTO 클래스 재귀적 분석
|
|
113
|
+
- 필드 타입, 어노테이션(`@NotNull`, `@Size` 등) 분석
|
|
114
|
+
- OpenAPI components/schemas에 스키마 정의 생성
|
|
115
|
+
- `SearchParam`, `Filter`, `Sort`, `PaginatedList` 타입도 스키마로 정의
|
|
116
|
+
|
|
117
|
+
## 기술 스택
|
|
118
|
+
- **언어**: TypeScript
|
|
119
|
+
- **Java 파서**: `java-parser` 또는 `tree-sitter-java`
|
|
120
|
+
- **YAML 생성**: `js-yaml`
|
|
121
|
+
- **CLI**: `commander` 또는 `yargs`
|
|
122
|
+
|
|
123
|
+
## CLI 인터페이스
|
|
124
|
+
```bash
|
|
125
|
+
# 사용 예시
|
|
126
|
+
npx spring-to-openapi \
|
|
127
|
+
--source ./src/main/java \
|
|
128
|
+
--output ./openapi.yaml \
|
|
129
|
+
--title "My API" \
|
|
130
|
+
--version "1.0.0" \
|
|
131
|
+
--base-path "/api"
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### CLI 옵션
|
|
135
|
+
- `--source, -s`: Java 소스코드 디렉토리 경로 (필수)
|
|
136
|
+
- `--output, -o`: 출력 YAML 파일 경로 (기본값: `openapi.yaml`)
|
|
137
|
+
- `--title, -t`: API 제목 (기본값: `API Documentation`)
|
|
138
|
+
- `--version, -v`: API 버전 (기본값: `1.0.0`)
|
|
139
|
+
- `--base-path, -b`: API 베이스 경로 (선택)
|
|
140
|
+
|
|
141
|
+
## 출력 형식 예시
|
|
142
|
+
```yaml
|
|
143
|
+
openapi: 3.0.0
|
|
144
|
+
info:
|
|
145
|
+
title: My API
|
|
146
|
+
version: 1.0.0
|
|
147
|
+
servers:
|
|
148
|
+
- url: http://localhost:8080/api
|
|
149
|
+
paths:
|
|
150
|
+
/users:
|
|
151
|
+
get:
|
|
152
|
+
summary: Search users with pagination
|
|
153
|
+
parameters:
|
|
154
|
+
- name: offset
|
|
155
|
+
in: query
|
|
156
|
+
schema:
|
|
157
|
+
type: integer
|
|
158
|
+
description: Pagination offset
|
|
159
|
+
- name: limit
|
|
160
|
+
in: query
|
|
161
|
+
schema:
|
|
162
|
+
type: integer
|
|
163
|
+
description: Pagination limit
|
|
164
|
+
- name: filter
|
|
165
|
+
in: query
|
|
166
|
+
schema:
|
|
167
|
+
$ref: '#/components/schemas/Filter'
|
|
168
|
+
- name: sort
|
|
169
|
+
in: query
|
|
170
|
+
schema:
|
|
171
|
+
$ref: '#/components/schemas/Sort'
|
|
172
|
+
responses:
|
|
173
|
+
'200':
|
|
174
|
+
description: Successful response
|
|
175
|
+
headers:
|
|
176
|
+
X-Total-Count:
|
|
177
|
+
schema:
|
|
178
|
+
type: integer
|
|
179
|
+
description: Total number of items
|
|
180
|
+
X-Offset:
|
|
181
|
+
schema:
|
|
182
|
+
type: integer
|
|
183
|
+
description: Current offset
|
|
184
|
+
X-Limit:
|
|
185
|
+
schema:
|
|
186
|
+
type: integer
|
|
187
|
+
description: Current limit
|
|
188
|
+
content:
|
|
189
|
+
application/json:
|
|
190
|
+
schema:
|
|
191
|
+
$ref: '#/components/schemas/PaginatedListUserDTO'
|
|
192
|
+
security:
|
|
193
|
+
- bearerAuth: []
|
|
194
|
+
x-required-roles:
|
|
195
|
+
- ROLE_USER
|
|
196
|
+
|
|
197
|
+
/users/{id}:
|
|
198
|
+
get:
|
|
199
|
+
summary: Get user by ID
|
|
200
|
+
parameters:
|
|
201
|
+
- name: id
|
|
202
|
+
in: path
|
|
203
|
+
required: true
|
|
204
|
+
schema:
|
|
205
|
+
type: integer
|
|
206
|
+
responses:
|
|
207
|
+
'200':
|
|
208
|
+
description: Successful response
|
|
209
|
+
content:
|
|
210
|
+
application/json:
|
|
211
|
+
schema:
|
|
212
|
+
$ref: '#/components/schemas/UserDTO'
|
|
213
|
+
security:
|
|
214
|
+
- bearerAuth: []
|
|
215
|
+
x-required-roles:
|
|
216
|
+
- ROLE_USER
|
|
217
|
+
- ROLE_ADMIN
|
|
218
|
+
|
|
219
|
+
put:
|
|
220
|
+
summary: Update user
|
|
221
|
+
parameters:
|
|
222
|
+
- name: id
|
|
223
|
+
in: path
|
|
224
|
+
required: true
|
|
225
|
+
schema:
|
|
226
|
+
type: integer
|
|
227
|
+
requestBody:
|
|
228
|
+
required: true
|
|
229
|
+
content:
|
|
230
|
+
application/json:
|
|
231
|
+
schema:
|
|
232
|
+
$ref: '#/components/schemas/UserDTO'
|
|
233
|
+
responses:
|
|
234
|
+
'200':
|
|
235
|
+
description: Successful response
|
|
236
|
+
security:
|
|
237
|
+
- bearerAuth: []
|
|
238
|
+
x-security-expression: "hasRole('ADMIN') or (#user.id == #id and hasRole('USER'))"
|
|
239
|
+
|
|
240
|
+
components:
|
|
241
|
+
schemas:
|
|
242
|
+
UserDTO:
|
|
243
|
+
type: object
|
|
244
|
+
properties:
|
|
245
|
+
id:
|
|
246
|
+
type: integer
|
|
247
|
+
name:
|
|
248
|
+
type: string
|
|
249
|
+
email:
|
|
250
|
+
type: string
|
|
251
|
+
|
|
252
|
+
PaginatedListUserDTO:
|
|
253
|
+
type: object
|
|
254
|
+
properties:
|
|
255
|
+
items:
|
|
256
|
+
type: array
|
|
257
|
+
items:
|
|
258
|
+
$ref: '#/components/schemas/UserDTO'
|
|
259
|
+
total:
|
|
260
|
+
type: integer
|
|
261
|
+
offset:
|
|
262
|
+
type: integer
|
|
263
|
+
limit:
|
|
264
|
+
type: integer
|
|
265
|
+
|
|
266
|
+
Filter:
|
|
267
|
+
type: object
|
|
268
|
+
description: Filter conditions
|
|
269
|
+
additionalProperties: true
|
|
270
|
+
|
|
271
|
+
Sort:
|
|
272
|
+
type: object
|
|
273
|
+
description: Sort conditions
|
|
274
|
+
properties:
|
|
275
|
+
field:
|
|
276
|
+
type: string
|
|
277
|
+
direction:
|
|
278
|
+
type: string
|
|
279
|
+
enum: [ASC, DESC]
|
|
280
|
+
|
|
281
|
+
securitySchemes:
|
|
282
|
+
bearerAuth:
|
|
283
|
+
type: http
|
|
284
|
+
scheme: bearer
|
|
285
|
+
bearerFormat: JWT
|
|
286
|
+
```
|
|
287
|
+
|
|
288
|
+
## 추가 요구사항
|
|
289
|
+
- 에러 핸들링: 파싱 실패 시 명확한 에러 메시지 출력
|
|
290
|
+
- 로깅: 처리 진행 상황 표시 (발견된 컨트롤러 수, 엔드포인트 수 등)
|
|
291
|
+
- 테스트: 샘플 Java 파일로 테스트 가능하도록 예제 포함
|
|
292
|
+
- SearchParam을 사용하는 예제
|
|
293
|
+
- PaginatedList를 반환하는 예제
|
|
294
|
+
- 복잡한 SpEL 표현식을 사용하는 예제
|
|
295
|
+
- README.md: 설치 및 사용법 문서 작성
|
|
296
|
+
|
|
297
|
+
## 프로젝트 구조
|
|
298
|
+
```
|
|
299
|
+
spring-to-openapi/
|
|
300
|
+
├── src/
|
|
301
|
+
│ ├── index.ts # CLI 진입점
|
|
302
|
+
│ ├── parser/
|
|
303
|
+
│ │ ├── javaParser.ts # Java 파싱 로직
|
|
304
|
+
│ │ └── astAnalyzer.ts # AST 분석
|
|
305
|
+
│ ├── analyzer/
|
|
306
|
+
│ │ ├── controllerAnalyzer.ts # RestController 분석
|
|
307
|
+
│ │ ├── parameterAnalyzer.ts # 파라미터 분석 (SearchParam 특수 처리)
|
|
308
|
+
│ │ ├── responseAnalyzer.ts # 응답 분석 (PaginatedList 특수 처리)
|
|
309
|
+
│ │ ├── securityAnalyzer.ts # 보안 어노테이션 분석 (SpEL 파싱)
|
|
310
|
+
│ │ └── schemaGenerator.ts # DTO 스키마 생성
|
|
311
|
+
│ ├── generator/
|
|
312
|
+
│ │ └── openapiGenerator.ts # OpenAPI YAML 생성
|
|
313
|
+
│ └── types/
|
|
314
|
+
│ └── index.ts # TypeScript 타입 정의
|
|
315
|
+
├── examples/ # 테스트용 샘플 Java 파일
|
|
316
|
+
│ ├── UserController.java
|
|
317
|
+
│ ├── SearchParam.java
|
|
318
|
+
│ └── PaginatedList.java
|
|
319
|
+
├── package.json
|
|
320
|
+
├── tsconfig.json
|
|
321
|
+
└── README.md
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
## 특수 타입 처리 상세
|
|
325
|
+
|
|
326
|
+
### SearchParam 감지 및 처리
|
|
327
|
+
1. 메서드 파라미터 타입이 `SearchParam` 또는 이를 상속/구현하는 타입인지 확인
|
|
328
|
+
2. 해당 파라미터가 발견되면 자동으로 `offset`, `limit`, `filter`, `sort` query parameters 추가
|
|
329
|
+
3. `Filter`와 `Sort` 타입이 정의되어 있다면 해당 스키마를 components/schemas에 추가
|
|
330
|
+
|
|
331
|
+
### PaginatedList 감지 및 처리
|
|
332
|
+
1. 메서드 리턴 타입이 `PaginatedList` 또는 `PaginatedList<T>`인지 확인
|
|
333
|
+
2. 제네릭 타입 `T`를 추출하여 실제 아이템 타입 파악
|
|
334
|
+
3. Response에 `X-Total-Count`, `X-Offset`, `X-Limit` 헤더 자동 추가
|
|
335
|
+
4. Response body 스키마는 PaginatedList 구조로 생성
|
|
336
|
+
|
|
337
|
+
### SpEL 표현식 처리
|
|
338
|
+
1. `@PreAuthorize`, `@PostAuthorize` 어노테이션의 값 추출
|
|
339
|
+
2. 정규식으로 `hasRole()`, `hasAnyRole()`, `hasAuthority()` 패턴 매칭
|
|
340
|
+
3. 단순 패턴에 매칭되면 역할/권한 추출하여 `x-required-roles`에 추가
|
|
341
|
+
4. 복잡한 표현식(논리 연산자, 비교 연산자, 변수 참조 등 포함)이면 전체 표현식을 `x-security-expression`에 기록
|
|
342
|
+
5. 두 필드 모두 존재할 수 있음 (간단한 역할 + 복잡한 전체 표현식)
|
|
343
|
+
|
|
344
|
+
### MultiLangString 처리
|
|
345
|
+
1. `MultiLangString` 은 `Map<String, String>` 형태로 처리
|
|
346
|
+
2. OpenAPI 스키마에서 `type: object`로 정의하고, `additionalProperties: { type: string }` 사용
|
|
347
|
+
|
|
348
|
+
### MultiLangStoredFile 처리
|
|
349
|
+
1. `MultiLangStoredFile` 은 `Map<String, StoredFile>` 형태로 처리
|
|
350
|
+
2. OpenAPI 스키마에서 `type: object`로 정의하고, `additionalProperties`에 `StoredFile` 스키마 참조 사용
|
|
351
|
+
3. StoredFile 은 `net.g1project.bluecomm.model.StoredFile` 클래스 참고해서 처리
|
|
352
|
+
|
|
353
|
+
### 그 외 Dto 분석 시 주의할 점
|
|
354
|
+
- static field 는 제외
|
|
355
|
+
- java.lang.Object 의 field 는 제외
|
|
356
|
+
|
|
357
|
+
|
|
358
|
+
위 요구사항에 따라 완전히 동작하는 프로젝트를 생성해주세요.
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
# RestController 파일 별로 문서 생성
|
|
2
|
+
|
|
3
|
+
디렉토리 하위의 @RestController annotation 이 추가된 파일을 검색해
|
|
4
|
+
각각의 java 파일별로 별도의 yaml 파일을 생성하도록 할 것.
|
|
5
|
+
yaml 파일 명은 java 파일명과 동일하게 할 것.
|
|
6
|
+
|
|
7
|
+
이렇게 하려면 output 을 파일로 받지 않고 디렉토리로 받아야 함. (--out-dir 처럼)
|
|
8
|
+
|
|
9
|
+
# javadoc 내용을 설명으로
|
|
10
|
+
|
|
11
|
+
Java 소스코드에 작성된 javadoc 내용을 OpenAPI 문서의 설명(description) 필드로 포함시켜줘.
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
# redoc 적용 스크립트
|
|
15
|
+
|
|
16
|
+
각 yaml 파일 별로 @redocly/cli 를 실행하는 셸 스크립트 작성해줘.
|
|
17
|
+
|
|
18
|
+
주어진 디렉토리 아래의 모든 yaml 파일에 대해서 아래처럼 실행
|
|
19
|
+
|
|
20
|
+
npx @redocly/cli build-docs (yaml 파일) -o (타겟 디렉토리)
|
|
21
|
+
|
|
22
|
+
# PaginatedList 스키마 정의
|
|
23
|
+
|
|
24
|
+
PaginatedList 가 response 로 리턴될 때는 items 만 의미가 있고, totalCount, offset, limit 등은 response header 로 전달되기 때문에 표시하지 않도록 해줘.
|
|
25
|
+
items 도 items property 는 명시하지 않고, 바로 배열 타입으로 정의되도록 해줘.
|
|
26
|
+
|
|
27
|
+
예를 들어, PaginatedList<UserDto> 라면 마치 List<UserDto> 인 것처럼 response schema 가 생성되도록 해줘.
|
|
28
|
+
|
|
29
|
+
# SearchParam
|
|
30
|
+
|
|
31
|
+
SearchParam 의 Filter 와 Sort 는 그냥 object 타입으로 정의해줘.
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import {
|
|
2
|
+
JavaClass,
|
|
3
|
+
JavaMethod,
|
|
4
|
+
ControllerInfo,
|
|
5
|
+
EndpointInfo,
|
|
6
|
+
HttpMethod,
|
|
7
|
+
ProcessingContext,
|
|
8
|
+
} from '../types';
|
|
9
|
+
import { ASTAnalyzer } from '../parser/astAnalyzer';
|
|
10
|
+
import { analyzeParameters } from './parameterAnalyzer';
|
|
11
|
+
import { analyzeResponse } from './responseAnalyzer';
|
|
12
|
+
import { analyzeMethodSecurity } from './securityAnalyzer';
|
|
13
|
+
|
|
14
|
+
export class ControllerAnalyzer {
|
|
15
|
+
private context: ProcessingContext;
|
|
16
|
+
|
|
17
|
+
constructor(context: ProcessingContext) {
|
|
18
|
+
this.context = context;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Analyze all Java classes and extract controller information
|
|
23
|
+
* If apiSourcePath is specified, only analyze controllers within that directory
|
|
24
|
+
*/
|
|
25
|
+
analyzeControllers(): ControllerInfo[] {
|
|
26
|
+
const controllers: ControllerInfo[] = [];
|
|
27
|
+
const { apiSourcePath } = this.context;
|
|
28
|
+
|
|
29
|
+
for (const [, javaClass] of this.context.javaClasses) {
|
|
30
|
+
// If apiSourcePath is specified, only include controllers from that directory
|
|
31
|
+
if (apiSourcePath && !javaClass.filePath.startsWith(apiSourcePath)) {
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (ASTAnalyzer.isRestController(javaClass)) {
|
|
36
|
+
const controllerInfo = this.analyzeController(javaClass);
|
|
37
|
+
if (controllerInfo) {
|
|
38
|
+
controllers.push(controllerInfo);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
return controllers;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Analyze a single controller class
|
|
48
|
+
*/
|
|
49
|
+
private analyzeController(javaClass: JavaClass): ControllerInfo | null {
|
|
50
|
+
const basePath = ASTAnalyzer.getClassBasePath(javaClass);
|
|
51
|
+
const endpoints: EndpointInfo[] = [];
|
|
52
|
+
|
|
53
|
+
console.log(` Found controller: ${javaClass.name} (base path: ${basePath || '/'})`);
|
|
54
|
+
|
|
55
|
+
for (const method of javaClass.methods) {
|
|
56
|
+
if (ASTAnalyzer.isEndpointMethod(method)) {
|
|
57
|
+
const endpoint = this.analyzeEndpoint(javaClass, method, basePath);
|
|
58
|
+
if (endpoint) {
|
|
59
|
+
endpoints.push(endpoint);
|
|
60
|
+
console.log(` - ${endpoint.method.toUpperCase()} ${endpoint.path}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
if (endpoints.length === 0) {
|
|
66
|
+
return null;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return {
|
|
70
|
+
className: javaClass.name,
|
|
71
|
+
basePath,
|
|
72
|
+
endpoints,
|
|
73
|
+
javaClass,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Analyze a single endpoint method
|
|
79
|
+
*/
|
|
80
|
+
private analyzeEndpoint(javaClass: JavaClass, method: JavaMethod, basePath: string): EndpointInfo | null {
|
|
81
|
+
const httpMethod = ASTAnalyzer.getHttpMethod(method);
|
|
82
|
+
if (!httpMethod) {
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
const methodPath = ASTAnalyzer.getMethodPath(method);
|
|
87
|
+
const fullPath = ASTAnalyzer.combinePaths(basePath, methodPath);
|
|
88
|
+
|
|
89
|
+
// Get @param descriptions from javadoc
|
|
90
|
+
const paramDescriptions = method.javadocInfo?.params || {};
|
|
91
|
+
|
|
92
|
+
// Analyze parameters (pass javadoc @param descriptions)
|
|
93
|
+
const { parameters, requestBody, hasSearchParam } = analyzeParameters(method, this.context, paramDescriptions);
|
|
94
|
+
|
|
95
|
+
// Analyze response
|
|
96
|
+
const { responses, hasPaginatedList, paginatedListItemType } = analyzeResponse(
|
|
97
|
+
method,
|
|
98
|
+
this.context
|
|
99
|
+
);
|
|
100
|
+
|
|
101
|
+
// Analyze security
|
|
102
|
+
const security = analyzeMethodSecurity(method);
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
path: fullPath,
|
|
106
|
+
method: httpMethod as HttpMethod,
|
|
107
|
+
operationId: ASTAnalyzer.generateOperationId(javaClass.name, method.name),
|
|
108
|
+
summary: ASTAnalyzer.generateSummary(method.name),
|
|
109
|
+
description: method.javadoc,
|
|
110
|
+
returnDescription: method.javadocInfo?.returns,
|
|
111
|
+
parameters,
|
|
112
|
+
requestBody,
|
|
113
|
+
responses,
|
|
114
|
+
security,
|
|
115
|
+
hasSearchParam,
|
|
116
|
+
hasPaginatedList,
|
|
117
|
+
paginatedListItemType,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function analyzeControllers(context: ProcessingContext): ControllerInfo[] {
|
|
123
|
+
const analyzer = new ControllerAnalyzer(context);
|
|
124
|
+
return analyzer.analyzeControllers();
|
|
125
|
+
}
|