API Spec 상태 조회 및 관리 기능 추가
- ApiSpecStatusRepository 신규 추가 - 스펙 상태 검색 조건/그룹 조회 및 UI 반영
This commit is contained in:
@@ -8,9 +8,12 @@ import com.eactive.eai.data.entity.onl.apim.apigroup.QApiGroupApi;
|
||||
import com.eactive.eai.data.jpa.AbstractDataService;
|
||||
import com.eactive.eai.rms.onl.apim.apigroup.ui.ApiGroupUISearch;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
import com.querydsl.core.Tuple;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
@@ -47,6 +50,24 @@ public class ApiGroupService extends AbstractDataService<ApiGroup, String, ApiGr
|
||||
});
|
||||
}
|
||||
|
||||
/** 검색 콤보용 그룹 ID/명칭. 게시 여부와 관계없이 모든 그룹을 제공한다. */
|
||||
public List<Map<String, String>> findGroupOptions() {
|
||||
QApiGroup group = QApiGroup.apiGroup;
|
||||
List<Tuple> rows = getJPAQueryFactory()
|
||||
.select(group.id, group.groupName)
|
||||
.from(group)
|
||||
.orderBy(group.displayOrder.asc().nullsLast(), group.groupName.asc(), group.id.asc())
|
||||
.fetch();
|
||||
List<Map<String, String>> options = new ArrayList<>();
|
||||
for (Tuple row : rows) {
|
||||
Map<String, String> option = new HashMap<>();
|
||||
option.put("id", row.get(group.id));
|
||||
option.put("groupName", StringUtils.defaultIfBlank(row.get(group.groupName), "그룹명 없음"));
|
||||
options.add(option);
|
||||
}
|
||||
return options;
|
||||
}
|
||||
|
||||
public void deleteApiGroupApiByApiId(String apiId) {
|
||||
List<ApiGroup> apiGroups = repository.findAll();
|
||||
for (ApiGroup apiGroup : apiGroups) {
|
||||
@@ -77,6 +98,28 @@ public class ApiGroupService extends AbstractDataService<ApiGroup, String, ApiGr
|
||||
.fetch();
|
||||
}
|
||||
|
||||
/** 목록 툴팁용 게시 그룹명. Oracle IN 절 제한에 맞춰 페이지의 API들을 일괄 조회한다. */
|
||||
public Map<String, List<String>> findPublishedGroupNamesByApiIds(List<String> apiIds) {
|
||||
Map<String, List<String>> groupNames = new HashMap<>();
|
||||
QApiGroup group = QApiGroup.apiGroup;
|
||||
QApiGroupApi membership = QApiGroupApi.apiGroupApi;
|
||||
for (int from = 0; from < apiIds.size(); from += 1000) {
|
||||
List<String> chunk = apiIds.subList(from, Math.min(from + 1000, apiIds.size()));
|
||||
List<Tuple> rows = getJPAQueryFactory()
|
||||
.select(membership.id.apiId, group.groupName)
|
||||
.from(membership)
|
||||
.join(membership.apiGroup, group)
|
||||
.where(membership.id.apiId.in(chunk), group.displayYn.eq("1"))
|
||||
.orderBy(group.displayOrder.asc().nullsLast(), group.groupName.asc(), group.id.asc())
|
||||
.fetch();
|
||||
for (Tuple row : rows) {
|
||||
groupNames.computeIfAbsent(row.get(membership.id.apiId), key -> new ArrayList<>())
|
||||
.add(StringUtils.defaultIfBlank(row.get(group.groupName), "그룹명 없음"));
|
||||
}
|
||||
}
|
||||
return groupNames;
|
||||
}
|
||||
|
||||
/** API 를 지정한 그룹 목록에만 소속시킨다(기존 소속 제거 후 재설정). */
|
||||
public void setApiGroupsForApi(String apiId, List<String> groupIds) {
|
||||
deleteApiGroupApiByApiId(apiId);
|
||||
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
package com.eactive.eai.rms.data.entity.onl.apim.apispec;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.List;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import com.eactive.eai.rms.data.EMSDataSource;
|
||||
import org.springframework.data.jpa.repository.Query;
|
||||
import org.springframework.data.repository.Repository;
|
||||
import org.springframework.data.repository.query.Param;
|
||||
|
||||
/** EAI 목록과 스키마가 다른 EMS 스펙의 ID/게시 여부만 조회한다. */
|
||||
@EMSDataSource
|
||||
public interface ApiSpecStatusRepository extends Repository<ApiSpecInfo, String> {
|
||||
|
||||
@Query("SELECT a.apiId FROM ApiSpecInfo a WHERE "
|
||||
+ "(:status = 'PUBLISHED' AND a.displayYn = 'Y') OR "
|
||||
+ "(:status = 'REGISTERED' AND (a.displayYn IS NULL OR a.displayYn <> 'Y'))")
|
||||
List<String> findApiIdsByStatus(@Param("status") String status);
|
||||
|
||||
@Query("SELECT a.apiId AS apiId, a.displayYn AS displayYn FROM ApiSpecInfo a WHERE a.apiId IN :apiIds")
|
||||
List<SpecStatus> findStatusesByApiIds(@Param("apiIds") Collection<String> apiIds);
|
||||
|
||||
interface SpecStatus {
|
||||
String getApiId();
|
||||
String getDisplayYn();
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package com.eactive.eai.rms.data.entity.onl.eaimsg;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
@@ -14,6 +15,7 @@ import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.eactive.apim.portal.apispec.repository.ApiSpecInfoRepository;
|
||||
import com.eactive.eai.rms.data.entity.onl.apim.apispec.ApiSpecStatusRepository;
|
||||
import com.eactive.eai.data.entity.onl.adapter.QAdapter;
|
||||
import com.eactive.eai.data.entity.onl.apim.apigroup.QApiGroup;
|
||||
import com.eactive.eai.data.entity.onl.apim.apigroup.QApiGroupApi;
|
||||
@@ -51,6 +53,7 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
||||
|
||||
/** EMSAPP(PTL_API_SPEC_INFO) 조회용. 다른 스키마라 EAI 메시지 쿼리에 조인할 수 없다 */
|
||||
private final ApiSpecInfoRepository apiSpecInfoRepository;
|
||||
private final ApiSpecStatusRepository apiSpecStatusRepository;
|
||||
|
||||
public Page<Tuple> selectList(EAIMessageUISearch eaiMessageUISearch, Pageable pageable, String sortname, String sortorder) {
|
||||
|
||||
@@ -201,6 +204,32 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
||||
predicate
|
||||
.and(qStdInfo.apifullpath.contains(eaiMessageUISearch.getSearchApiFullPath()));
|
||||
}
|
||||
if (StringUtils.isNotBlank(eaiMessageUISearch.getSearchInOutType())) {
|
||||
predicate.and(qeaiMessageEntity.eaisvcname.endsWith(eaiMessageUISearch.getSearchInOutType()));
|
||||
}
|
||||
|
||||
String specStatus = eaiMessageUISearch.getSearchSpecStatus();
|
||||
if ("UNREGISTERED".equals(specStatus)) {
|
||||
predicate.and(apiSpecRegisteredExpression(qeaiMessageEntity.eaisvcname).not());
|
||||
} else if ("REGISTERED".equals(specStatus) || "PUBLISHED".equals(specStatus)) {
|
||||
predicate.and(apiIdInExpression(qeaiMessageEntity.eaisvcname,
|
||||
apiSpecStatusRepository.findApiIdsByStatus(specStatus)));
|
||||
}
|
||||
|
||||
String apiGroupId = eaiMessageUISearch.getSearchApiGroupId();
|
||||
if (StringUtils.isNotBlank(apiGroupId)) {
|
||||
QApiGroupApi membership = QApiGroupApi.apiGroupApi;
|
||||
QApiGroup group = QApiGroup.apiGroup;
|
||||
JPQLQuery<Integer> groupQuery = JPAExpressions.selectOne()
|
||||
.from(membership)
|
||||
.join(membership.apiGroup, group)
|
||||
.where(membership.id.apiId.eq(qeaiMessageEntity.eaisvcname));
|
||||
if ("UNREGISTERED".equals(apiGroupId)) {
|
||||
predicate.and(groupQuery.notExists());
|
||||
} else {
|
||||
predicate.and(groupQuery.where(group.id.eq(apiGroupId)).exists());
|
||||
}
|
||||
}
|
||||
|
||||
if (StringUtils.isNotBlank(eaiMessageUISearch.getSearchStatusCode())) {
|
||||
String statusCode = eaiMessageUISearch.getSearchStatusCode();
|
||||
@@ -230,7 +259,10 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
||||
* API ID 를 EMS 커넥션에서 따로 읽어 IN 절로 넘긴다.</p>
|
||||
*/
|
||||
private BooleanExpression apiSpecRegisteredExpression(StringPath eaiSvcNamePath) {
|
||||
List<String> specApiIds = apiSpecInfoRepository.findAllApiIds();
|
||||
return apiIdInExpression(eaiSvcNamePath, apiSpecInfoRepository.findAllApiIds());
|
||||
}
|
||||
|
||||
private BooleanExpression apiIdInExpression(StringPath eaiSvcNamePath, List<String> specApiIds) {
|
||||
if (specApiIds.isEmpty()) {
|
||||
// in(빈 리스트) 는 JPQL 이 깨지므로 항상 거짓인 조건으로 대체한다
|
||||
return Expressions.ONE.eq(Expressions.TWO);
|
||||
@@ -245,6 +277,18 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
||||
return expression;
|
||||
}
|
||||
|
||||
/** 현재 페이지의 스펙 상태를 일괄 조회한다. Oracle IN 절은 1,000개씩 나눈다. */
|
||||
public Map<String, String> findApiSpecStatuses(List<String> apiIds) {
|
||||
Map<String, String> statuses = new HashMap<>();
|
||||
for (int from = 0; from < apiIds.size(); from += ORACLE_IN_LIMIT) {
|
||||
int to = Math.min(from + ORACLE_IN_LIMIT, apiIds.size());
|
||||
for (ApiSpecStatusRepository.SpecStatus spec : apiSpecStatusRepository.findStatusesByApiIds(apiIds.subList(from, to))) {
|
||||
statuses.put(spec.getApiId(), "Y".equals(spec.getDisplayYn()) ? "PUBLISHED" : "REGISTERED");
|
||||
}
|
||||
}
|
||||
return statuses;
|
||||
}
|
||||
|
||||
/** 노출 중인(display_yn='1') API 그룹에 편성된 API ID. API_GROUP 은 EAI 메시지와 같은 스키마다 */
|
||||
private JPQLQuery<String> displayedGroupApiIdSubQuery() {
|
||||
QApiGroupApi qApiGroupApi = QApiGroupApi.apiGroupApi;
|
||||
|
||||
@@ -19,6 +19,12 @@ public class EAIMessageUISearch {
|
||||
private String searchRefKey;
|
||||
|
||||
private String searchApiFullPath;
|
||||
/** API ID 마지막 자리 (1: 당발, 2: 타발) */
|
||||
private String searchInOutType;
|
||||
/** UNREGISTERED: 미등록, REGISTERED: 등록(비게시), PUBLISHED: 게시 */
|
||||
private String searchSpecStatus;
|
||||
/** 그룹 ID. UNREGISTERED이면 소속 그룹이 없는 API */
|
||||
private String searchApiGroupId;
|
||||
|
||||
/** API_STATUS.STATUS_CODE (E:장애, D:지연, C:점검). "N" 이면 정상(코드 없음)만 조회 */
|
||||
private String searchStatusCode;
|
||||
|
||||
@@ -135,6 +135,15 @@ public class ApiInterfaceService extends OnlBaseService {
|
||||
public Page<ApiInterfaceUI> selectList(EAIMessageUISearch eaiMessageUISearch, Pageable pageable,String sortname, String sortorder) {
|
||||
|
||||
Page<Tuple> tuples = eaiMessageQueryService.selectList(eaiMessageUISearch, pageable, sortname, sortorder );
|
||||
List<String> apiIds = tuples.getContent().stream()
|
||||
.map(tuple -> tuple.get(QEAIMessageEntity.eAIMessageEntity).getEaisvcname())
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
Map<String, String> specStatuses = eaiMessageQueryService.findApiSpecStatuses(apiIds);
|
||||
List<String> publishedApiIds = apiIds.stream()
|
||||
.filter(apiId -> "PUBLISHED".equals(specStatuses.get(apiId)))
|
||||
.collect(Collectors.toList());
|
||||
Map<String, List<String>> publishedGroupNames = apiGroupService.findPublishedGroupNamesByApiIds(publishedApiIds);
|
||||
|
||||
return tuples.map(tuple -> {
|
||||
EAIMessageEntity eaiMessageEntity = tuple.get(QEAIMessageEntity.eAIMessageEntity);
|
||||
@@ -153,6 +162,8 @@ public class ApiInterfaceService extends OnlBaseService {
|
||||
apiInterfaceUI.setApiFullPath(apiFullPath);
|
||||
apiInterfaceUI.setBzwksvckeyname(bzwksvckeyname);
|
||||
apiInterfaceUI.setStatusCode(statusCode);
|
||||
apiInterfaceUI.setSpecStatus(specStatuses.getOrDefault(eaiMessageEntity.getEaisvcname(), "UNREGISTERED"));
|
||||
apiInterfaceUI.setPublishedGroupNames(publishedGroupNames.getOrDefault(eaiMessageEntity.getEaisvcname(), Collections.emptyList()));
|
||||
|
||||
return apiInterfaceUI;
|
||||
});
|
||||
@@ -164,6 +175,7 @@ public class ApiInterfaceService extends OnlBaseService {
|
||||
// 전체 목록수 얻기
|
||||
HashMap map = new HashMap();
|
||||
map.put("bizList", voList);
|
||||
map.put("apiGroupList", apiGroupService.findGroupOptions());
|
||||
|
||||
return map;
|
||||
}
|
||||
|
||||
@@ -54,6 +54,8 @@ public class ApiInterfaceUI {
|
||||
private String apiFullPath;
|
||||
private String bzwksvckeyname;
|
||||
private String statusCode;
|
||||
private String specStatus;
|
||||
private List<String> publishedGroupNames;
|
||||
|
||||
//ASYNC-SYNC 용
|
||||
private String inboundResponseHttpMethod;
|
||||
|
||||
+84
@@ -0,0 +1,84 @@
|
||||
package com.eactive.eai.rms.data.entity.onl.apim.apispec;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.persistence.EntityManager;
|
||||
|
||||
import com.eactive.apim.portal.apispec.entity.ApiSpecInfo;
|
||||
import org.hibernate.SessionFactory;
|
||||
import org.hibernate.cfg.Configuration;
|
||||
import org.junit.jupiter.api.AfterAll;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
import org.junit.jupiter.api.Test;
|
||||
import org.springframework.data.jpa.repository.support.JpaRepositoryFactory;
|
||||
|
||||
class ApiSpecStatusRepositoryTest {
|
||||
private static SessionFactory sessionFactory;
|
||||
private static EntityManager entityManager;
|
||||
private static ApiSpecStatusRepository repository;
|
||||
|
||||
@BeforeAll
|
||||
static void setUp() {
|
||||
sessionFactory = new Configuration()
|
||||
.addAnnotatedClass(ApiSpecInfo.class)
|
||||
.setProperty("hibernate.dialect", "org.hibernate.dialect.H2Dialect")
|
||||
.setProperty("hibernate.connection.driver_class", "org.h2.Driver")
|
||||
.setProperty("hibernate.connection.url", "jdbc:h2:mem:apiSpecStatus;DB_CLOSE_DELAY=-1")
|
||||
.setProperty("hibernate.hbm2ddl.auto", "create-drop")
|
||||
.buildSessionFactory();
|
||||
entityManager = sessionFactory.createEntityManager();
|
||||
repository = new JpaRepositoryFactory(entityManager).getRepository(ApiSpecStatusRepository.class);
|
||||
entityManager.getTransaction().begin();
|
||||
save("PUBLISHED1", "Y");
|
||||
save("REGISTERED1", "N");
|
||||
save("LEGACY2", null);
|
||||
entityManager.getTransaction().commit();
|
||||
entityManager.clear();
|
||||
}
|
||||
|
||||
private static void save(String apiId, String displayYn) {
|
||||
ApiSpecInfo spec = new ApiSpecInfo();
|
||||
spec.setApiId(apiId);
|
||||
spec.setDisplayYn(displayYn);
|
||||
entityManager.persist(spec);
|
||||
}
|
||||
|
||||
@AfterAll
|
||||
static void tearDown() {
|
||||
if (entityManager != null) entityManager.close();
|
||||
if (sessionFactory != null) sessionFactory.close();
|
||||
}
|
||||
|
||||
@Test
|
||||
void publishedIncludesOnlyDisplayedSpecs() {
|
||||
assertEquals(Arrays.asList("PUBLISHED1"), repository.findApiIdsByStatus("PUBLISHED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void registeredIncludesNonDisplayedAndLegacyNullSpecs() {
|
||||
assertEquals(new HashSet<>(Arrays.asList("REGISTERED1", "LEGACY2")),
|
||||
new HashSet<>(repository.findApiIdsByStatus("REGISTERED")));
|
||||
}
|
||||
|
||||
@Test
|
||||
void statusProjectionIncludesOnlyRequestedExistingSpecs() {
|
||||
Map<String, String> statuses = repository.findStatusesByApiIds(
|
||||
Arrays.asList("PUBLISHED1", "LEGACY2", "MISSING2")).stream()
|
||||
.collect(Collectors.toMap(ApiSpecStatusRepository.SpecStatus::getApiId,
|
||||
spec -> "Y".equals(spec.getDisplayYn()) ? "PUBLISHED" : "REGISTERED"));
|
||||
assertEquals(2, statuses.size());
|
||||
assertEquals("PUBLISHED", statuses.get("PUBLISHED1"));
|
||||
assertEquals("REGISTERED", statuses.get("LEGACY2"));
|
||||
assertEquals("UNREGISTERED", statuses.getOrDefault("MISSING2", "UNREGISTERED"));
|
||||
}
|
||||
|
||||
@Test
|
||||
void missingApiHasNoSpecStatus() {
|
||||
assertTrue(repository.findStatusesByApiIds(Arrays.asList("MISSING2")).isEmpty());
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user