snp-batch-validation/src/main/java/com/snp/batch/global/controller/BypassConfigController.java
HYOJIN ce27c60985 feat: 같은 도메인에 여러 BYPASS API 엔드포인트 등록 지원 (#63)
- BypassApiConfig에 endpointName 필드 추가 (externalPath에서 자동 추출)
- domainName unique 제약 → domainName + endpointName 복합 unique로 변경
- 코드 생성 시 같은 도메인의 모든 설정을 합쳐 Controller 1개 생성
- Service/DTO는 엔드포인트별로 별도 생성
- CodeGenerationResult에 servicePaths/dtoPaths 목록 반환
- 프론트엔드 타입/UI 업데이트

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 08:25:03 +09:00

137 lines
6.1 KiB
Java

package com.snp.batch.global.controller;
import com.snp.batch.common.web.ApiResponse;
import com.snp.batch.global.dto.BypassConfigRequest;
import com.snp.batch.global.dto.BypassConfigResponse;
import com.snp.batch.global.dto.BypassFieldDto;
import com.snp.batch.global.dto.CodeGenerationResult;
import com.snp.batch.global.model.BypassApiConfig;
import com.snp.batch.global.repository.BypassApiConfigRepository;
import com.snp.batch.service.BypassCodeGenerator;
import com.snp.batch.service.BypassConfigService;
import com.snp.batch.service.JsonSchemaParser;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.Map;
/**
* BYPASS API 설정 관리 및 코드 생성 컨트롤러
*/
@Slf4j
@RestController
@RequestMapping("/api/bypass-config")
@RequiredArgsConstructor
@Tag(name = "Bypass Config", description = "BYPASS API 설정 관리 및 코드 생성")
public class BypassConfigController {
private final BypassConfigService bypassConfigService;
private final BypassCodeGenerator bypassCodeGenerator;
private final JsonSchemaParser jsonSchemaParser;
private final BypassApiConfigRepository configRepository;
@Operation(summary = "설정 목록 조회")
@GetMapping
public ResponseEntity<ApiResponse<List<BypassConfigResponse>>> getConfigs() {
return ResponseEntity.ok(ApiResponse.success(bypassConfigService.getConfigs()));
}
@Operation(summary = "설정 상세 조회")
@GetMapping("/{id}")
public ResponseEntity<ApiResponse<BypassConfigResponse>> getConfig(@PathVariable Long id) {
return ResponseEntity.ok(ApiResponse.success(bypassConfigService.getConfig(id)));
}
@Operation(summary = "설정 등록")
@PostMapping
public ResponseEntity<ApiResponse<BypassConfigResponse>> createConfig(
@RequestBody BypassConfigRequest request) {
return ResponseEntity.ok(ApiResponse.success(bypassConfigService.createConfig(request)));
}
@Operation(summary = "설정 수정")
@PutMapping("/{id}")
public ResponseEntity<ApiResponse<BypassConfigResponse>> updateConfig(
@PathVariable Long id,
@RequestBody BypassConfigRequest request) {
return ResponseEntity.ok(ApiResponse.success(bypassConfigService.updateConfig(id, request)));
}
@Operation(summary = "설정 삭제")
@DeleteMapping("/{id}")
public ResponseEntity<ApiResponse<Void>> deleteConfig(@PathVariable Long id) {
bypassConfigService.deleteConfig(id);
return ResponseEntity.ok(ApiResponse.success("삭제 완료", null));
}
@Operation(
summary = "코드 생성",
description = "등록된 설정의 도메인 전체를 기반으로 Controller, Service, DTO Java 소스 코드를 생성합니다. 같은 도메인의 모든 설정을 하나의 Controller로 합칩니다."
)
@PostMapping("/{id}/generate")
public ResponseEntity<ApiResponse<CodeGenerationResult>> generateCode(
@PathVariable Long id,
@RequestParam(defaultValue = "false") boolean force) {
try {
BypassApiConfig config = configRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("설정을 찾을 수 없습니다: " + id));
// 같은 도메인의 모든 설정을 조회하여 함께 생성
List<BypassApiConfig> domainConfigs = configRepository.findByDomainNameOrderById(config.getDomainName());
CodeGenerationResult result = bypassCodeGenerator.generate(domainConfigs, force);
// 같은 도메인의 모든 설정을 generated로 마킹
domainConfigs.forEach(c -> bypassConfigService.markAsGenerated(c.getId()));
return ResponseEntity.ok(ApiResponse.success(result));
} catch (IllegalStateException e) {
return ResponseEntity.badRequest().body(ApiResponse.error(e.getMessage()));
} catch (Exception e) {
log.error("코드 생성 실패", e);
return ResponseEntity.internalServerError().body(ApiResponse.error("코드 생성 실패: " + e.getMessage()));
}
}
@Operation(
summary = "JSON 샘플 파싱",
description = "JSON 샘플에서 DTO 필드 목록을 추출합니다."
)
@PostMapping("/parse-json")
public ResponseEntity<ApiResponse<List<BypassFieldDto>>> parseJson(@RequestBody String jsonSample) {
try {
List<BypassFieldDto> fields = jsonSchemaParser.parse(jsonSample);
return ResponseEntity.ok(ApiResponse.success(fields));
} catch (Exception e) {
return ResponseEntity.badRequest().body(ApiResponse.error("JSON 파싱 실패: " + e.getMessage()));
}
}
@Operation(
summary = "WebClient 빈 목록",
description = "사용 가능한 WebClient 빈 이름 목록을 반환합니다."
)
@GetMapping("/webclient-beans")
public ResponseEntity<ApiResponse<List<Map<String, String>>>> getWebclientBeans() {
List<Map<String, String>> beans = List.of(
Map.of("name", "maritimeApiWebClient", "description", "Ship API (shipsapi.maritime.spglobal.com)"),
Map.of("name", "maritimeAisApiWebClient", "description", "AIS API (aisapi.maritime.spglobal.com)"),
Map.of("name", "maritimeServiceApiWebClient", "description", "Web Service API (webservices.maritime.spglobal.com)")
);
return ResponseEntity.ok(ApiResponse.success(beans));
}
}