토큰 현황 목록 조회에 최근 발급 이력 1건 포함
- 목록에서 issueHistory 가 null 로만 나와 "값이 없는 것"과 "안 실린 것"이 구분되지 않던 문제 - 목록은 최근 1건, 단건 조회는 보관 중인 전체(최대 10건)를 담는다 - 이력이 없으면 null 대신 빈 목록을 반환하고, serverName 도 함께 담는다 - 단위테스트 2건 추가
This commit is contained in:
@@ -90,10 +90,10 @@ public class OAuthTokenStatusDTO {
|
||||
String serverName;
|
||||
|
||||
/**
|
||||
* 토큰 발급 이력 (최근 순, 최대 10건).
|
||||
* 토큰 발급 이력 (최근 순).
|
||||
*
|
||||
* 이 노드에서 일어난 발급만 담기며 재기동 시 사라진다.
|
||||
* 목록 조회에는 담지 않고 어댑터그룹 단건 조회에서만 채운다.
|
||||
* 단건 조회는 보관 중인 전체(최대 10건), 목록 조회는 최근 1건만 담는다.
|
||||
*/
|
||||
List<TokenIssueHistory> issueHistory;
|
||||
}
|
||||
|
||||
@@ -19,6 +19,7 @@ import com.eactive.eai.adapter.AdapterPropManager;
|
||||
import com.eactive.eai.adapter.AdapterVO;
|
||||
import com.eactive.eai.authoutbound.OutboundOAuthCredentialVo;
|
||||
import com.eactive.eai.common.authoutbound.AccessTokenManagerByDB;
|
||||
import com.eactive.eai.common.authoutbound.TokenIssueHistory;
|
||||
import com.eactive.eai.common.server.EAIServerManager;
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
import com.openbanking.eai.common.token.AccessTokenVO;
|
||||
@@ -45,6 +46,9 @@ public class OAuthTokenStatusService {
|
||||
/** accessToken 중 노출할 앞자리 수 */
|
||||
private static final int UNMASKED_LENGTH = 8;
|
||||
|
||||
/** 목록 조회에 담을 발급 이력 건수 */
|
||||
private static final int LIST_HISTORY_SIZE = 1;
|
||||
|
||||
/**
|
||||
* 등록된 모든 어댑터그룹의 토큰 현황을 반환한다.
|
||||
*
|
||||
@@ -53,39 +57,38 @@ public class OAuthTokenStatusService {
|
||||
public List<OAuthTokenStatusDTO> getStatusList() {
|
||||
List<OAuthTokenStatusDTO> result = new ArrayList<OAuthTokenStatusDTO>();
|
||||
for (String adapterGroupName : AccessTokenManagerByDB.getInstance().getRegisteredAdapterGroupNames()) {
|
||||
// 목록에는 발급 이력을 담지 않는다. 그룹이 많으면 응답이 지나치게 커진다.
|
||||
result.add(getStatus(adapterGroupName, false));
|
||||
// 목록에는 최근 1건만 담는다. 전체를 담으면 그룹 수만큼 응답이 커진다.
|
||||
result.add(getStatus(adapterGroupName, LIST_HISTORY_SIZE));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* 어댑터그룹 하나의 토큰 현황을 발급 이력과 함께 반환한다.
|
||||
* 어댑터그룹 하나의 토큰 현황을 발급 이력 전체와 함께 반환한다.
|
||||
*
|
||||
* @param adapterGroupName 어댑터그룹명
|
||||
* @return 토큰 현황. 등록돼 있지 않으면 message 만 채워서 반환한다.
|
||||
*/
|
||||
public OAuthTokenStatusDTO getStatus(String adapterGroupName) {
|
||||
return getStatus(adapterGroupName, true);
|
||||
return getStatus(adapterGroupName, Integer.MAX_VALUE);
|
||||
}
|
||||
|
||||
/**
|
||||
* 어댑터그룹 하나의 토큰 현황을 반환한다.
|
||||
*
|
||||
* @param adapterGroupName 어댑터그룹명
|
||||
* @param includeHistory 토큰 발급 이력 포함 여부
|
||||
* @param historyLimit 담을 발급 이력 건수 (최근 순)
|
||||
* @return 토큰 현황. 등록돼 있지 않으면 message 만 채워서 반환한다.
|
||||
*/
|
||||
private OAuthTokenStatusDTO getStatus(String adapterGroupName, boolean includeHistory) {
|
||||
private OAuthTokenStatusDTO getStatus(String adapterGroupName, int historyLimit) {
|
||||
OAuthTokenStatusDTO dto = new OAuthTokenStatusDTO();
|
||||
dto.setAdapterGroupName(adapterGroupName);
|
||||
|
||||
AccessTokenManagerByDB manager = AccessTokenManagerByDB.getInstance();
|
||||
|
||||
if (includeHistory) {
|
||||
dto.setServerName(findLocalServerName());
|
||||
dto.setIssueHistory(manager.getIssueHistories(adapterGroupName));
|
||||
}
|
||||
// 이력은 노드 로컬이므로 어느 노드가 응답했는지 함께 담는다.
|
||||
dto.setServerName(findLocalServerName());
|
||||
dto.setIssueHistory(latest(manager.getIssueHistories(adapterGroupName), historyLimit));
|
||||
OutboundOAuthCredentialVo credential = manager.getOutboundOAuthCredentialVo(adapterGroupName);
|
||||
|
||||
if (credential == null) {
|
||||
@@ -175,6 +178,20 @@ public class OAuthTokenStatusService {
|
||||
return dto;
|
||||
}
|
||||
|
||||
/**
|
||||
* 발급 이력에서 최근 건만 잘라낸다. 이력은 이미 최근 순으로 정렬돼 있다.
|
||||
*
|
||||
* @param histories 발급 이력
|
||||
* @param limit 담을 건수
|
||||
* @return 최근 limit 건
|
||||
*/
|
||||
private List<TokenIssueHistory> latest(List<TokenIssueHistory> histories, int limit) {
|
||||
if (histories.size() <= limit) {
|
||||
return histories;
|
||||
}
|
||||
return new ArrayList<TokenIssueHistory>(histories.subList(0, limit));
|
||||
}
|
||||
|
||||
/**
|
||||
* 이 응답을 만든 서버명을 찾는다. 발급 이력이 노드 로컬이라 함께 알려준다.
|
||||
*
|
||||
|
||||
@@ -8,6 +8,7 @@ import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
import static org.mockito.ArgumentMatchers.anyString;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
@@ -23,6 +24,7 @@ import org.springframework.context.ApplicationContext;
|
||||
|
||||
import com.eactive.eai.authoutbound.OutboundOAuthCredentialVo;
|
||||
import com.eactive.eai.common.authoutbound.AccessTokenManagerByDB;
|
||||
import com.eactive.eai.common.authoutbound.TokenIssueHistory;
|
||||
import com.eactive.eai.common.util.ApplicationContextProvider;
|
||||
import com.openbanking.eai.common.token.OAuth2AccessTokenVO;
|
||||
|
||||
@@ -64,6 +66,21 @@ class OAuthTokenStatusServiceTest {
|
||||
return credential;
|
||||
}
|
||||
|
||||
/**
|
||||
* 최근 순으로 정렬된 발급 이력 count 건 (svc-0 이 최신).
|
||||
*
|
||||
* TokenIssueHistory 생성자는 package-private 이라 mock 으로 만든다.
|
||||
*/
|
||||
private List<TokenIssueHistory> histories(int count) {
|
||||
List<TokenIssueHistory> result = new ArrayList<>();
|
||||
for (int i = 0; i < count; i++) {
|
||||
TokenIssueHistory history = Mockito.mock(TokenIssueHistory.class);
|
||||
Mockito.when(history.getServiceClass()).thenReturn("svc-" + i);
|
||||
result.add(history);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/** 만료까지 expiresInSec 남은 토큰 */
|
||||
private OAuth2AccessTokenVO token(String accessToken, long expiresInSec) {
|
||||
OAuth2AccessTokenVO vo = new OAuth2AccessTokenVO();
|
||||
@@ -277,20 +294,35 @@ class OAuthTokenStatusServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("9-1. 단건 조회에는 발급 이력을 담고 목록에는 담지 않는다")
|
||||
@DisplayName("9-1. 단건 조회는 이력 전체, 목록은 최근 1건만 담는다")
|
||||
void 이력포함범위() {
|
||||
Set<String> groups = new LinkedHashSet<>(Arrays.asList(GROUP));
|
||||
Mockito.when(mockManager.getRegisteredAdapterGroupNames()).thenReturn(groups);
|
||||
registerCredential(GROUP);
|
||||
Mockito.when(mockManager.peekAccessTokenVO(GROUP)).thenReturn(token("abcdefghijkl", 600));
|
||||
Mockito.when(mockManager.getIssueHistories(GROUP)).thenReturn(Collections.emptyList());
|
||||
// histories() 안에서 mock 을 만들므로 when(...) 인자에 직접 넣으면 스터빙이 중첩된다.
|
||||
List<TokenIssueHistory> histories = histories(3);
|
||||
Mockito.when(mockManager.getIssueHistories(GROUP)).thenReturn(histories);
|
||||
|
||||
OAuthTokenStatusDTO single = service.getStatus(GROUP);
|
||||
OAuthTokenStatusDTO fromList = service.getStatusList().get(0);
|
||||
|
||||
assertNotNull(single.getIssueHistory(), "단건 조회에는 이력이 있어야 한다");
|
||||
assertNull(fromList.getIssueHistory(), "목록에는 이력을 담지 않는다");
|
||||
Mockito.verify(mockManager, Mockito.times(1)).getIssueHistories(GROUP);
|
||||
assertEquals(3, single.getIssueHistory().size(), "단건 조회는 보관 중인 이력 전체");
|
||||
assertEquals(1, fromList.getIssueHistory().size(), "목록은 최근 1건만");
|
||||
assertEquals("svc-0", fromList.getIssueHistory().get(0).getServiceClass(), "최신 건이어야 한다");
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("9-2. 이력이 없으면 빈 목록 (null 아님)")
|
||||
void 이력없으면_빈목록() {
|
||||
registerCredential(GROUP);
|
||||
Mockito.when(mockManager.peekAccessTokenVO(GROUP)).thenReturn(token("abcdefghijkl", 600));
|
||||
Mockito.when(mockManager.getIssueHistories(GROUP)).thenReturn(Collections.emptyList());
|
||||
|
||||
OAuthTokenStatusDTO dto = service.getStatus(GROUP);
|
||||
|
||||
assertNotNull(dto.getIssueHistory());
|
||||
assertTrue(dto.getIssueHistory().isEmpty());
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
Reference in New Issue
Block a user