diff --git a/src/main/java/kr/re/etri/autoflow/controllers/ExternalDataSetController.java b/src/main/java/kr/re/etri/autoflow/controllers/ExternalDataSetController.java new file mode 100644 index 0000000..749e66d --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/controllers/ExternalDataSetController.java @@ -0,0 +1,60 @@ +package kr.re.etri.autoflow.controllers; + +import io.swagger.v3.oas.annotations.Operation; +import io.swagger.v3.oas.annotations.Parameter; +import io.swagger.v3.oas.annotations.enums.ParameterIn; +import io.swagger.v3.oas.annotations.media.Content; +import io.swagger.v3.oas.annotations.responses.ApiResponse; +import io.swagger.v3.oas.annotations.responses.ApiResponses; +import io.swagger.v3.oas.annotations.tags.Tag; +import kr.re.etri.autoflow.service.DatasetService; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@Tag(name = "Dataset API", description = "외부 데이터셋 목록 조회 API (Python requests 코드 기반)") +@RestController +@RequestMapping("/api/datasets") +@RequiredArgsConstructor +public class ExternalDataSetController { + + private final DatasetService datasetService; + + @Operation( + summary = "데이터셋 목록 조회", + description = "외부 API(/export/dataset_list)를 호출하여 데이터셋 목록을 조회합니다." + ) + @ApiResponses({ + @ApiResponse(responseCode = "200", description = "성공적으로 데이터셋을 조회했습니다."), + @ApiResponse(responseCode = "400", description = "잘못된 요청입니다.", content = @Content), + @ApiResponse(responseCode = "500", description = "서버 내부 오류", content = @Content) + }) + @GetMapping("/list") + public ResponseEntity getDatasetList( + @Parameter(description = "프로젝트 인덱스", example = "10") + @RequestParam(required = false) Integer ds_prj_idx, + @Parameter(description = "검색 키워드", example = "traffic") + @RequestParam(required = false) String search_keyword, + @Parameter(description = "그룹 이름", example = "AIGroup") + @RequestParam(required = false) String grp_name + ) { + Map result = datasetService.getDatasetList(ds_prj_idx, search_keyword, grp_name); + return ResponseEntity.ok(result); + } + + @PostMapping("/download") + @Operation( + summary = "데이터셋 다운로드", + description = "외부 API 서버로 POST 요청을 보내 ZIP 파일을 다운로드합니다.", + parameters = { + @Parameter(name = "datasetName", description = "다운로드할 데이터셋 이름", in = ParameterIn.QUERY, required = true) + } + ) + public ResponseEntity downloadDataset( + @RequestParam("datasetName") String datasetName + ) { + return datasetService.downloadDataset(datasetName); + } +} diff --git a/src/main/java/kr/re/etri/autoflow/service/DatasetService.java b/src/main/java/kr/re/etri/autoflow/service/DatasetService.java new file mode 100644 index 0000000..fe7b448 --- /dev/null +++ b/src/main/java/kr/re/etri/autoflow/service/DatasetService.java @@ -0,0 +1,133 @@ +package kr.re.etri.autoflow.service; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.core.io.ByteArrayResource; +import org.springframework.http.*; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; +import org.springframework.web.reactive.function.client.WebClient; +import org.springframework.web.util.UriComponentsBuilder; + +import java.nio.charset.StandardCharsets; +import java.util.*; + +@Service +@RequiredArgsConstructor +@Slf4j +public class DatasetService { + + private final RestTemplate restTemplate; + + private static final String BASE_URL = "http://52.14.11.43:18010"; + private static final String LIST_ENDPOINT = "/export/dataset_list"; + + @SuppressWarnings("unchecked") + public Map getDatasetList(Integer ds_prj_idx, String search_keyword, String grp_name) { + try { + // ✅ URL + 파라미터 구성 + UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(BASE_URL + LIST_ENDPOINT); + if (ds_prj_idx != null) builder.queryParam("ds_prj_idx", ds_prj_idx); + if (search_keyword != null) builder.queryParam("search_keyword", search_keyword); + if (grp_name != null) builder.queryParam("grp_name", grp_name); + + String apiUrl = builder.toUriString(); + + HttpHeaders headers = new HttpHeaders(); + headers.setAccept(Collections.singletonList(MediaType.APPLICATION_JSON)); + + HttpEntity request = new HttpEntity<>(headers); + + ResponseEntity response = restTemplate.exchange( + apiUrl, + HttpMethod.GET, + request, + Map.class + ); + + Map body = response.getBody(); + + if (body == null) { + return Map.of( + "success", false, + "message", "API 응답이 비어 있습니다." + ); + } + + boolean success = Boolean.TRUE.equals(body.get("success")); + if (success) { + return Map.of( + "success", true, + "data", body.get("data"), + "total_count", body.get("total_count") + ); + } else { + return Map.of( + "success", false, + "message", body.get("message") + ); + } + + } catch (Exception e) { + return Map.of( + "success", false, + "message", "API 요청 실패: " + e.getMessage() + ); + } + } + + @Value("${external.api.base-url:http://52.14.11.43:18010}") + private String baseUrl; + + private static final String DOWNLOAD_ENDPOINT = "/export/dataset"; + + private final WebClient webClient = WebClient.builder() + .defaultHeader(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE) + .build(); + + public ResponseEntity downloadDataset(String datasetName) { + String apiUrl = baseUrl + DOWNLOAD_ENDPOINT; + log.info("외부 API 요청 URL: {}", apiUrl); + log.info("요청 데이터셋 이름: {}", datasetName); + + // 요청 본문(JSON) + String requestBody = String.format("{\"dataset_name\":\"%s\"}", datasetName); + + try { + // WebClient로 외부 API POST 요청 + return webClient.post() + .uri(apiUrl) + .bodyValue(requestBody) + .exchangeToMono(response -> { + if (response.statusCode().equals(HttpStatus.OK)) { + // 파일 스트림 수신 + return response.bodyToMono(byte[].class) + .map(bytes -> { + ByteArrayResource resource = new ByteArrayResource(bytes); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + headers.setContentDisposition(ContentDisposition.attachment() + .filename(datasetName + ".zip", StandardCharsets.UTF_8) + .build()); + return new ResponseEntity<>(resource, headers, HttpStatus.OK); + }); + } else { + // 오류 응답 처리 + return response.bodyToMono(String.class) + .map(errorBody -> { + log.error("다운로드 실패 (HTTP {}): {}", response.statusCode(), errorBody); + return ResponseEntity.status(response.statusCode()) + .contentType(MediaType.APPLICATION_JSON) + .body(errorBody); + }); + } + }) + .block(); + } catch (Exception e) { + log.error("API 요청 중 오류 발생", e); + return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR) + .body("API 요청 중 오류 발생: " + e.getMessage()); + } + } +}