parent
41598ca440
commit
250e01a0c1
@ -0,0 +1,54 @@
|
||||
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.tags.Tag;
|
||||
import kr.re.etri.autoflow.entity.KubeflowRunEntity;
|
||||
import kr.re.etri.autoflow.payload.request.KubeflowRunSearchRequest;
|
||||
import kr.re.etri.autoflow.repository.KubeflowRunRepository;
|
||||
import kr.re.etri.autoflow.service.KubeflowRunService;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springdoc.core.annotations.ParameterObject;
|
||||
import org.springframework.data.domain.Page;
|
||||
import org.springframework.http.ResponseEntity;
|
||||
import org.springframework.web.bind.annotation.*;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@Tag(name = "Kubeflow Run", description = "Kubeflow Run 조회 API")
|
||||
@RestController
|
||||
@RequestMapping("/api/kubeflow/runs")
|
||||
@RequiredArgsConstructor
|
||||
public class KubeflowRunsNewController {
|
||||
|
||||
private final KubeflowRunRepository runRepository;
|
||||
|
||||
private final KubeflowRunService kubeflowRunService;
|
||||
|
||||
@Operation(summary = "모든 Kubeflow Run 조회")
|
||||
@GetMapping
|
||||
public ResponseEntity<List<KubeflowRunEntity>> getAllRuns() {
|
||||
List<KubeflowRunEntity> runs = runRepository.findAll();
|
||||
return ResponseEntity.ok(runs);
|
||||
}
|
||||
|
||||
@Operation(summary = "Kubeflow Run 단건 조회")
|
||||
@GetMapping("/{runId}")
|
||||
public ResponseEntity<KubeflowRunEntity> getRun(
|
||||
@Parameter(description = "Kubeflow Run ID", example = "ad980d7f-050a-4c59-a775-94394befad40")
|
||||
@PathVariable("runId") String runId) {
|
||||
|
||||
return runRepository.findById(runId)
|
||||
.map(ResponseEntity::ok)
|
||||
.orElse(ResponseEntity.notFound().build());
|
||||
}
|
||||
|
||||
@Operation(summary = "Kubeflow Run 검색 및 페이지네이션 조회")
|
||||
@GetMapping("/search")
|
||||
public ResponseEntity<Page<KubeflowRunEntity>> searchRuns(
|
||||
@ParameterObject @ModelAttribute KubeflowRunSearchRequest request) {
|
||||
|
||||
Page<KubeflowRunEntity> page = kubeflowRunService.search(request);
|
||||
return ResponseEntity.ok(page);
|
||||
}
|
||||
}
|
||||
@ -1,34 +1,63 @@
|
||||
package kr.re.etri.autoflow.entity;
|
||||
|
||||
import jakarta.persistence.Entity;
|
||||
import jakarta.persistence.Id;
|
||||
import jakarta.persistence.Table;
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Data;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.time.Instant;
|
||||
|
||||
// Entity (DB 저장용)
|
||||
/**
|
||||
* Kubeflow Run 정보를 저장하는 엔티티
|
||||
* DB 테이블: tb_runs
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "tb_runs")
|
||||
@Data
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class KubeflowRunEntity {
|
||||
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
@Schema(description = "자동 증가 내부 ID (DB 관리용)", example = "1")
|
||||
private Long Id;
|
||||
|
||||
@Column(unique = true, nullable = false)
|
||||
@Schema(description = "Kubeflow에서 제공하는 Run ID", example = "12345-abcdef")
|
||||
private String runId;
|
||||
|
||||
@Schema(description = "Run이 속한 Experiment ID", example = "exp-001")
|
||||
private String experimentId;
|
||||
|
||||
@Schema(description = "Run의 표시 이름", example = "My First Run")
|
||||
private String displayName;
|
||||
|
||||
@Schema(description = "Run의 저장 상태 (예: AVAILABLE, ARCHIVED)", example = "AVAILABLE")
|
||||
private String storageState;
|
||||
|
||||
@Schema(description = "Run 설명", example = "This run tests the new pipeline")
|
||||
private String description;
|
||||
|
||||
@Schema(description = "연결된 Pipeline ID", example = "pipeline-001")
|
||||
private String pipelineId;
|
||||
|
||||
@Schema(description = "연결된 Pipeline Version ID", example = "v1.0")
|
||||
private String pipelineVersionId;
|
||||
|
||||
@Schema(description = "실행 서비스 계정", example = "default")
|
||||
private String serviceAccount;
|
||||
|
||||
@Schema(description = "Run 생성 시각", example = "2025-09-24T08:30:00Z")
|
||||
private Instant createdAt;
|
||||
|
||||
@Schema(description = "Run 예약 시각", example = "2025-09-24T08:45:00Z")
|
||||
private Instant scheduledAt;
|
||||
|
||||
@Schema(description = "Run 완료 시각", example = "2025-09-24T09:00:00Z")
|
||||
private Instant finishedAt;
|
||||
|
||||
@Schema(description = "Run 상태 (예: RUNNING, COMPLETED, FAILED)", example = "COMPLETED")
|
||||
private String state;
|
||||
}
|
||||
|
||||
@ -0,0 +1,13 @@
|
||||
package kr.re.etri.autoflow.payload.request;
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
public class KubeflowRunSearchRequest extends BaseSearchRequest {
|
||||
@Schema(description = "실험 ID", example = "1")
|
||||
private String experimentId;
|
||||
}
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package kr.re.etri.autoflow.service;
|
||||
|
||||
import kr.re.etri.autoflow.entity.KubeflowRunEntity;
|
||||
import kr.re.etri.autoflow.payload.request.KubeflowRunSearchRequest;
|
||||
import kr.re.etri.autoflow.repository.KubeflowRunRepository;
|
||||
import kr.re.etri.autoflow.specification.KubeflowRunSpecification;
|
||||
import lombok.RequiredArgsConstructor;
|
||||
import org.springframework.data.domain.*;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
@Service
|
||||
@RequiredArgsConstructor
|
||||
public class KubeflowRunService {
|
||||
|
||||
private final KubeflowRunRepository runRepository;
|
||||
private final KubeflowRunSpecification runSpecification;
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
public Page<KubeflowRunEntity> search(KubeflowRunSearchRequest request) {
|
||||
int pageIndex = request.getPage() > 0 ? request.getPage() - 1 : 0;
|
||||
|
||||
Pageable pageable = PageRequest.of(
|
||||
pageIndex,
|
||||
request.getSize(),
|
||||
Sort.by(Sort.Direction.fromString(request.getSortDirection()), request.getSortField())
|
||||
);
|
||||
|
||||
Specification<KubeflowRunEntity> spec = runSpecification.searchByConditions(
|
||||
request.getExperimentId(), // experimentId는 필수
|
||||
request.getSearchType(),
|
||||
request.getKeyword()
|
||||
);
|
||||
|
||||
return runRepository.findAll(spec, pageable);
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,146 @@
|
||||
//package kr.re.etri.autoflow.specification;
|
||||
//
|
||||
//import jakarta.annotation.PostConstruct;
|
||||
//import jakarta.persistence.EntityManager;
|
||||
//import jakarta.persistence.metamodel.Attribute;
|
||||
//import jakarta.persistence.metamodel.EntityType;
|
||||
//import jakarta.persistence.metamodel.Metamodel;
|
||||
//import kr.re.etri.autoflow.entity.KubeflowRunEntity;
|
||||
//import lombok.extern.slf4j.Slf4j;
|
||||
//import org.springframework.data.jpa.domain.Specification;
|
||||
//import org.springframework.stereotype.Component;
|
||||
//
|
||||
//import jakarta.persistence.criteria.Predicate;
|
||||
//import java.util.Set;
|
||||
//import java.util.stream.Collectors;
|
||||
//
|
||||
//@Slf4j
|
||||
//@Component
|
||||
//public class KubeflowRunSpecification {
|
||||
//
|
||||
// private final EntityManager entityManager;
|
||||
// private Set<String> stringFields;
|
||||
//
|
||||
// public KubeflowRunSpecification(EntityManager entityManager) {
|
||||
// this.entityManager = entityManager;
|
||||
// }
|
||||
//
|
||||
// @PostConstruct
|
||||
// public void init() {
|
||||
// Metamodel metamodel = entityManager.getMetamodel();
|
||||
// EntityType<KubeflowRunEntity> entityType = metamodel.entity(KubeflowRunEntity.class);
|
||||
//
|
||||
// stringFields = entityType.getAttributes().stream()
|
||||
// .filter(attr -> attr.getJavaType().equals(String.class))
|
||||
// .map(Attribute::getName)
|
||||
// .collect(Collectors.toSet());
|
||||
//
|
||||
// log.info("KubeflowRunEntity string fields: {}", stringFields);
|
||||
// }
|
||||
//
|
||||
// public Specification<KubeflowRunEntity> searchByConditions(
|
||||
// String experimentId, String searchType, String keyword) {
|
||||
//
|
||||
// return (root, query, cb) -> {
|
||||
// Predicate predicate = cb.conjunction();
|
||||
//
|
||||
// // experimentId는 항상 조건으로 추가
|
||||
// predicate = cb.and(predicate,
|
||||
// cb.equal(root.get("experimentId"), experimentId));
|
||||
//
|
||||
// if (keyword != null && !keyword.isEmpty()) {
|
||||
// if (searchType == null || searchType.isEmpty() ||
|
||||
// "전체".equalsIgnoreCase(searchType) || "all".equalsIgnoreCase(searchType)) {
|
||||
//
|
||||
// Predicate orPredicate = cb.disjunction();
|
||||
// for (String field : stringFields) {
|
||||
// orPredicate = cb.or(orPredicate,
|
||||
// cb.like(cb.lower(root.get(field)), "%" + keyword.toLowerCase() + "%"));
|
||||
// }
|
||||
// predicate = cb.and(predicate, orPredicate);
|
||||
//
|
||||
// } else if (stringFields.contains(searchType)) {
|
||||
// predicate = cb.and(predicate,
|
||||
// cb.like(cb.lower(root.get(searchType)), "%" + keyword.toLowerCase() + "%"));
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return predicate;
|
||||
// };
|
||||
// }
|
||||
//}
|
||||
|
||||
|
||||
package kr.re.etri.autoflow.specification;
|
||||
|
||||
import jakarta.annotation.PostConstruct;
|
||||
import jakarta.persistence.EntityManager;
|
||||
import jakarta.persistence.metamodel.Attribute;
|
||||
import jakarta.persistence.metamodel.EntityType;
|
||||
import jakarta.persistence.metamodel.Metamodel;
|
||||
import kr.re.etri.autoflow.entity.ProjectEntity;
|
||||
import kr.re.etri.autoflow.entity.KubeflowRunEntity;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.springframework.data.jpa.domain.Specification;
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
import jakarta.persistence.criteria.Predicate;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Set;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@Slf4j
|
||||
@Component
|
||||
public class KubeflowRunSpecification {
|
||||
|
||||
private final EntityManager entityManager;
|
||||
|
||||
private Set<String> stringFields;
|
||||
|
||||
public KubeflowRunSpecification(EntityManager entityManager) {
|
||||
this.entityManager = entityManager;
|
||||
}
|
||||
|
||||
// 스프링 빈 초기화 후 실행
|
||||
@PostConstruct
|
||||
public void init() {
|
||||
Metamodel metamodel = entityManager.getMetamodel();
|
||||
EntityType<KubeflowRunEntity> entityType = metamodel.entity(KubeflowRunEntity.class);
|
||||
|
||||
// 문자열 타입 필드명만 추출
|
||||
stringFields = entityType.getAttributes().stream()
|
||||
.filter(attr -> attr.getJavaType().equals(String.class))
|
||||
.map(Attribute::getName)
|
||||
.collect(Collectors.toSet());
|
||||
|
||||
log.info("KubeflowRunEntity string fields: {}", stringFields);
|
||||
}
|
||||
|
||||
public Specification<KubeflowRunEntity> searchByConditions(
|
||||
String experimentId, String searchType, String keyword) {
|
||||
|
||||
return (root, query, cb) -> {
|
||||
Predicate predicate = cb.conjunction();
|
||||
|
||||
predicate = cb.and(predicate, cb.equal(root.get("experimentId"), experimentId));
|
||||
|
||||
if (keyword != null && !keyword.isEmpty()) {
|
||||
if (searchType == null || searchType.isEmpty() ||
|
||||
"전체".equalsIgnoreCase(searchType) || "all".equalsIgnoreCase(searchType)) {
|
||||
Predicate orPredicate = cb.disjunction();
|
||||
for (String field : stringFields) {
|
||||
orPredicate = cb.or(orPredicate,
|
||||
cb.like(cb.lower(root.get(field)), "%" + keyword.toLowerCase() + "%"));
|
||||
}
|
||||
predicate = cb.and(predicate, orPredicate);
|
||||
|
||||
} else if (stringFields.contains(searchType)) {
|
||||
predicate = cb.and(predicate,
|
||||
cb.like(cb.lower(root.get(searchType)), "%" + keyword.toLowerCase() + "%"));
|
||||
}
|
||||
}
|
||||
|
||||
return predicate;
|
||||
};
|
||||
}
|
||||
}
|
||||
Loading…
Reference in new issue