Merge branch 'master' of https://git.eactive.synology.me:8090/eapim/djbank/eapim-admin.git
eapim-admin CI / build (push) Has been cancelled
eapim-admin CI / build (push) Has been cancelled
This commit is contained in:
@@ -72,13 +72,22 @@
|
||||
});
|
||||
|
||||
}
|
||||
function detail(){
|
||||
// 체크박스는 name 을 search* 로 두면 getSearchForJqgrid 가 해제 상태에서도 value 를 담아버린다
|
||||
// (input[type!=radio][name^=search] 를 checked 여부와 무관하게 수집) → 여기서 직접 붙인다
|
||||
function getPopupSearchData() {
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
postData.searchApiSpecYn = $("#chkApiSpec").is(":checked") ? "Y" : "";
|
||||
postData.searchPortalDisplayYn = $("#chkPortalDisplay").is(":checked") ? "Y" : "";
|
||||
return postData;
|
||||
}
|
||||
|
||||
function detail(){
|
||||
var postData = getPopupSearchData();
|
||||
$("#grid").setGridParam({ url:url,postData: postData }).trigger("reloadGrid");
|
||||
}
|
||||
|
||||
function search(){
|
||||
var postData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
var postData = getPopupSearchData();
|
||||
$("#grid").setGridParam({ url:url,postData: postData ,page:1 }).trigger("reloadGrid");
|
||||
}
|
||||
function ioFormatter(cellvalue,options,rowObject){
|
||||
@@ -97,7 +106,7 @@
|
||||
|
||||
function list(){
|
||||
detail()
|
||||
var gridPostData = getSearchForJqgrid("cmd","LIST"); //jqgrid에서는 object 로
|
||||
var gridPostData = getPopupSearchData();
|
||||
// gridPostData.searchApiEnableYn = 'Y';
|
||||
var urlParams = new URLSearchParams(window.location.search);
|
||||
var selectedApiIds = urlParams.get("selectedApiIds");
|
||||
@@ -189,6 +198,9 @@
|
||||
$("select[name=searchEaiBzwkDstcd]").change(function() {
|
||||
search();
|
||||
});
|
||||
$("#chkApiSpec, #chkPortalDisplay").change(function() {
|
||||
search();
|
||||
});
|
||||
$("#btn_search").click(function(){
|
||||
search();
|
||||
});
|
||||
@@ -247,6 +259,18 @@
|
||||
<input type="text" name="searchApiFullPath" value="${param.searchApiFullPath}">
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width:180px;">포탈 노출</th>
|
||||
<td colspan="3">
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" id="chkApiSpec" <c:if test="${param.searchApiSpecYn eq 'Y'}">checked</c:if>> OpenAPI Spec 등록
|
||||
</label>
|
||||
|
||||
<label class="checkbox-inline">
|
||||
<input type="checkbox" id="chkPortalDisplay" <c:if test="${param.searchPortalDisplayYn eq 'Y'}">checked</c:if>> 포탈 게시
|
||||
</label>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<table id="grid" ></table>
|
||||
|
||||
@@ -189,8 +189,11 @@
|
||||
// AuthorizeInterceptor 가 정상적으로 권한 인식. window.open 직접 호출 시 권한 부족 발생.
|
||||
// 본 페이지는 summernote 본문 에디터 등 다른 iframe 이 함께 존재할 수 있어
|
||||
// frames[0] 가정은 위험 → DOM 에서 .ui-dialog 내 iframe 중 src=apiGroupMan 인 것을 식별.
|
||||
// 공지 영향 API 는 포탈에 실제 노출되는 것만 고르면 되므로 두 조건을 켠 채로 연다.
|
||||
// (타 화면에서 여는 팝업은 파라미터가 없어 해제 상태 — 팝업 기본값은 건드리지 않는다)
|
||||
var selectedIds = affectedApis.map(function(a) { return a.apiId; }).join(',');
|
||||
var url2 = apiPopupUrl + '?cmd=POPUP&selectedApiIds=' + encodeURIComponent(selectedIds);
|
||||
var url2 = apiPopupUrl + '?cmd=POPUP&searchApiSpecYn=Y&searchPortalDisplayYn=Y'
|
||||
+ '&selectedApiIds=' + encodeURIComponent(selectedIds);
|
||||
var args = {};
|
||||
showModal(url2, args, 1280, 860, function () {
|
||||
var doc = args.self && args.self.document;
|
||||
|
||||
@@ -13,7 +13,10 @@ import org.springframework.data.domain.Pageable;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.eactive.apim.portal.apispec.repository.ApiSpecInfoRepository;
|
||||
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;
|
||||
import com.eactive.eai.data.entity.onl.authserver.QScopeEntity;
|
||||
import com.eactive.eai.data.entity.onl.logger.QStatLog;
|
||||
import com.eactive.eai.data.entity.onl.message.EAIMessageEntity;
|
||||
@@ -31,6 +34,9 @@ import com.querydsl.core.types.OrderSpecifier;
|
||||
import com.querydsl.core.types.Projections;
|
||||
import com.querydsl.core.types.dsl.BooleanExpression;
|
||||
import com.querydsl.core.types.dsl.Expressions;
|
||||
import com.querydsl.core.types.dsl.StringPath;
|
||||
import com.querydsl.jpa.JPAExpressions;
|
||||
import com.querydsl.jpa.JPQLQuery;
|
||||
import com.querydsl.jpa.impl.JPAQuery;
|
||||
|
||||
@Service
|
||||
@@ -38,8 +44,14 @@ import com.querydsl.jpa.impl.JPAQuery;
|
||||
@RequiredArgsConstructor
|
||||
public class EAIMessageService extends AbstractDataService<EAIMessageEntity, String, EAIMessageRepository> {
|
||||
|
||||
/** Oracle IN 절 표현식 개수 상한. 넘기면 ORA-01795 라 나눠 OR 로 잇는다 */
|
||||
private static final int ORACLE_IN_LIMIT = 1000;
|
||||
|
||||
private final UserBusinessService userBusinessService;
|
||||
|
||||
/** EMSAPP(PTL_API_SPEC_INFO) 조회용. 다른 스키마라 EAI 메시지 쿼리에 조인할 수 없다 */
|
||||
private final ApiSpecInfoRepository apiSpecInfoRepository;
|
||||
|
||||
public Page<Tuple> selectList(EAIMessageUISearch eaiMessageUISearch, Pageable pageable, String sortname, String sortorder) {
|
||||
|
||||
QEAIMessageEntity qeaiMessageEntity = QEAIMessageEntity.eAIMessageEntity;
|
||||
@@ -189,9 +201,51 @@ public class EAIMessageService extends AbstractDataService<EAIMessageEntity, Str
|
||||
.and(qStdInfo.apifullpath.contains(eaiMessageUISearch.getSearchApiFullPath()));
|
||||
}
|
||||
|
||||
if ("Y".equals(eaiMessageUISearch.getSearchApiSpecYn())) {
|
||||
predicate.and(apiSpecRegisteredExpression(qeaiMessageEntity.eaisvcname));
|
||||
}
|
||||
|
||||
if ("Y".equals(eaiMessageUISearch.getSearchPortalDisplayYn())) {
|
||||
predicate.and(qeaiMessageEntity.eaisvcname.in(displayedGroupApiIdSubQuery()));
|
||||
}
|
||||
|
||||
return predicate;
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenAPI Spec 등록 여부 조건.
|
||||
*
|
||||
* <p>PTL_API_SPEC_INFO 는 EMSAPP 스키마라 TSEAIHE01 쿼리에 조인하면 ORA-00942 다. 스펙이 있는
|
||||
* API ID 를 EMS 커넥션에서 따로 읽어 IN 절로 넘긴다.</p>
|
||||
*/
|
||||
private BooleanExpression apiSpecRegisteredExpression(StringPath eaiSvcNamePath) {
|
||||
List<String> specApiIds = apiSpecInfoRepository.findAllApiIds();
|
||||
if (specApiIds.isEmpty()) {
|
||||
// in(빈 리스트) 는 JPQL 이 깨지므로 항상 거짓인 조건으로 대체한다
|
||||
return Expressions.ONE.eq(Expressions.TWO);
|
||||
}
|
||||
|
||||
BooleanExpression expression = null;
|
||||
for (int from = 0; from < specApiIds.size(); from += ORACLE_IN_LIMIT) {
|
||||
int to = Math.min(from + ORACLE_IN_LIMIT, specApiIds.size());
|
||||
BooleanExpression chunk = eaiSvcNamePath.in(specApiIds.subList(from, to));
|
||||
expression = (expression == null) ? chunk : expression.or(chunk);
|
||||
}
|
||||
return expression;
|
||||
}
|
||||
|
||||
/** 노출 중인(display_yn='1') API 그룹에 편성된 API ID. API_GROUP 은 EAI 메시지와 같은 스키마다 */
|
||||
private JPQLQuery<String> displayedGroupApiIdSubQuery() {
|
||||
QApiGroupApi qApiGroupApi = QApiGroupApi.apiGroupApi;
|
||||
QApiGroup qApiGroup = QApiGroup.apiGroup;
|
||||
|
||||
return JPAExpressions
|
||||
.select(qApiGroupApi.id.apiId)
|
||||
.from(qApiGroupApi)
|
||||
.join(qApiGroupApi.apiGroup, qApiGroup)
|
||||
.where(qApiGroup.displayYn.eq("1"));
|
||||
}
|
||||
|
||||
public Page<RoutingEaiMsgDto> findAll(Pageable pageable, RoutingEaiMsgSearch routingEaiMsgSearch) {
|
||||
|
||||
List<RoutingEaiMsgDto> result = routingEaiMsgDtoList(pageable, routingEaiMsgSearch);
|
||||
|
||||
@@ -20,4 +20,10 @@ public class EAIMessageUISearch {
|
||||
|
||||
private String searchApiFullPath;
|
||||
|
||||
/** "Y" 면 PTL_API_SPEC_INFO 에 OpenAPI Spec 이 등록된 API 만 조회 */
|
||||
private String searchApiSpecYn;
|
||||
|
||||
/** "Y" 면 노출 중인(display_yn='1') API 그룹에 편성된 API 만 조회 */
|
||||
private String searchPortalDisplayYn;
|
||||
|
||||
}
|
||||
|
||||
@@ -22,6 +22,7 @@ import org.springframework.dao.DataIntegrityViolationException;
|
||||
import org.springframework.stereotype.Service;
|
||||
import org.springframework.transaction.annotation.Transactional;
|
||||
|
||||
import com.eactive.apim.portal.apispec.repository.ApiSpecInfoRepository;
|
||||
import com.eactive.apim.portal.common.entity.Auditable;
|
||||
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncident;
|
||||
import com.eactive.apim.portal.djb.apistatus.incident.entity.DjbApistatusIncidentApi;
|
||||
@@ -110,6 +111,10 @@ public class ApiStatusDetectionService {
|
||||
@Autowired
|
||||
private ApiStatusRepository apiStatusRepository;
|
||||
|
||||
/** EMSAPP(PTL_API_SPEC_INFO) 조회용. AGWAPP 커넥션으로 도는 apiStatusRepository 로는 못 읽는다 */
|
||||
@Autowired
|
||||
private ApiSpecInfoRepository apiSpecInfoRepository;
|
||||
|
||||
@Autowired
|
||||
private PortalPropertyService portalPropertyService;
|
||||
|
||||
@@ -534,14 +539,23 @@ public class ApiStatusDetectionService {
|
||||
}
|
||||
}
|
||||
|
||||
/** 게시 인터페이스 판정. 조회에 실패하면 전부 비게시로 보아 ID·명칭 노출을 막는다. */
|
||||
/**
|
||||
* 게시 인터페이스 판정. 조회에 실패하면 전부 비게시로 보아 ID·명칭 노출을 막는다.
|
||||
*
|
||||
* <p>판정 조건 두 개가 서로 다른 스키마에 있어 조회를 나눈다 - API 그룹 편성은 AGWAPP,
|
||||
* 스펙 존재는 EMSAPP(PTL_API_SPEC_INFO). 한 쿼리로 조인하면 tenant=APIGW 커넥션이
|
||||
* EMSAPP 를 못 봐서 ORA-00942 가 난다.</p>
|
||||
*/
|
||||
private ApiSplit splitPublished(List<String> apiIds) {
|
||||
if (apiIds == null || apiIds.isEmpty()) {
|
||||
return new ApiSplit(Collections.emptyList(), 0);
|
||||
}
|
||||
Set<String> publishedIds;
|
||||
try {
|
||||
publishedIds = new HashSet<>(apiStatusRepository.findPublishedApiIds(apiIds));
|
||||
List<String> groupedIds = apiStatusRepository.findDisplayedGroupApiIds(apiIds);
|
||||
publishedIds = groupedIds.isEmpty()
|
||||
? Collections.emptySet()
|
||||
: new HashSet<>(apiSpecInfoRepository.findApiIdsIn(groupedIds));
|
||||
} catch (Exception e) {
|
||||
// 실패 시 노출하는 쪽으로 기울면 내부 인터페이스가 고객 화면에 새어 나간다
|
||||
log.warn("게시 API 판정 실패 - 전부 GW 인터페이스로 묶어 표기", e);
|
||||
|
||||
@@ -147,22 +147,25 @@ public interface ApiStatusRepository extends BaseRepository<ApiStatus, String> {
|
||||
List<ApiNameRow> findApiNames(@Param("apiIds") Collection<String> apiIds);
|
||||
|
||||
/**
|
||||
* 개발자포탈에 게시되는 인터페이스(= API) 만 추린다.
|
||||
* 노출 중인 API 그룹에 편성된 인터페이스 ID.
|
||||
*
|
||||
* <p>GW 인터페이스는 전부 탐지 대상이지만 개발자포탈에 노출되는 것은 그 일부다.
|
||||
* 자동 초안(제목·공지 본문·타임라인)에 게시되지 않은 인터페이스 ID 가 그대로 박히면
|
||||
* 고객 화면에 내부 인터페이스가 드러나므로, 여기서 걸러 나머지는 건수로만 표기한다.</p>
|
||||
*
|
||||
* <p>판정 기준은 포털 "오픈 API" 목록과 같은 두 조건이다 - 노출 중인 API 그룹에 편성돼 있고
|
||||
* PTL_API_SPEC_INFO 에 스펙이 있을 것. 역할·소속에 따른 사용자별 공개 범위는 admin 에
|
||||
* 사용자 컨텍스트가 없어 적용하지 않는다(= 포털 화면이 한 번 더 좁힌다).</p>
|
||||
* <p>게시 판정은 포털 "오픈 API" 목록과 같은 두 조건이다 - 노출 중인 API 그룹에 편성돼 있고
|
||||
* PTL_API_SPEC_INFO 에 스펙이 있을 것. 이 쿼리는 앞 조건(AGWAPP)만 본다. 뒤 조건은
|
||||
* EMSAPP 스키마라 {@code ApiSpecInfoRepository.findApiIdsIn} 으로 따로 조회해 교집합을 잡는다
|
||||
* - 이 리포지토리는 tenant=APIGW(AGWAPP) 커넥션으로 도는 탓에 한 쿼리로 조인하면 ORA-00942 다.</p>
|
||||
*
|
||||
* <p>역할·소속에 따른 사용자별 공개 범위는 admin 에 사용자 컨텍스트가 없어 적용하지 않는다
|
||||
* (= 포털 화면이 한 번 더 좁힌다).</p>
|
||||
*/
|
||||
@Query(nativeQuery = true, value =
|
||||
" SELECT DISTINCT GA.API_ID"
|
||||
+ " FROM API_GROUP_API GA"
|
||||
+ " JOIN API_GROUP G ON G.ID = GA.API_GROUP_ID"
|
||||
+ " WHERE GA.API_ID IN (:apiIds)"
|
||||
+ " AND G.DISPLAY_YN = '1'"
|
||||
+ " AND EXISTS (SELECT 1 FROM EMSAPP.PTL_API_SPEC_INFO S WHERE S.API_ID = GA.API_ID)")
|
||||
List<String> findPublishedApiIds(@Param("apiIds") Collection<String> apiIds);
|
||||
+ " AND G.DISPLAY_YN = '1'")
|
||||
List<String> findDisplayedGroupApiIds(@Param("apiIds") Collection<String> apiIds);
|
||||
}
|
||||
Reference in New Issue
Block a user