snp-batch-validation/src/main/java/com/snp/batch/global/controller/BypassConfigController.java
HYOJIN b3d9938422 feat: S&P Bypass 피드백 반영 (#123)
- Response JSON 원본 반환 (ApiResponse 래핑 제거, executeRaw 추가)
- 메뉴명 변경: Bypass API → API 관리
- 사용자용 API 카탈로그 페이지 (/bypass-catalog) 추가
- 운영 환경 코드 생성 차단 (app.environment=prod 시 비활성화)
- Bypass API 코드 생성 (compliance, risk 도메인)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 08:35:07 +09:00

132 lines
6.0 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.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 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.beans.factory.annotation.Value;
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 BypassApiConfigRepository configRepository;
@Value("${app.environment:dev}")
private String environment;
@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 소스 코드를 생성합니다. 같은 도메인의 모든 설정을 하나의 Controller로 합칩니다."
)
@PostMapping("/{id}/generate")
public ResponseEntity<ApiResponse<CodeGenerationResult>> generateCode(
@PathVariable Long id,
@RequestParam(defaultValue = "false") boolean force) {
if ("prod".equals(environment)) {
return ResponseEntity.badRequest()
.body(ApiResponse.error("운영 환경에서는 코드 생성이 불가합니다. 개발 환경에서 생성 후 배포해주세요."));
}
try {
BypassApiConfig config = configRepository.findById(id)
.orElseThrow(() -> new IllegalArgumentException("설정을 찾을 수 없습니다: " + id));
List<BypassApiConfig> domainConfigs = configRepository.findByDomainNameOrderById(config.getDomainName());
CodeGenerationResult result = bypassCodeGenerator.generate(domainConfigs, force);
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 = "환경 정보", description = "현재 서버 환경 정보를 반환합니다 (dev/prod).")
@GetMapping("/environment")
public ResponseEntity<ApiResponse<Map<String, Object>>> getEnvironment() {
return ResponseEntity.ok(ApiResponse.success(Map.of(
"environment", environment,
"codeGenerationEnabled", !"prod".equals(environment)
)));
}
@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));
}
}