Skip to content

Commit 7e4a985

Browse files
authored
[Feat/#405] 오늘 기록 존재 여부 확인 api 구현 (#406)
* fix: 공개 경로 추가 * feat: 오늘 기록 존재 여부 확인 api 구현
1 parent 9095983 commit 7e4a985

9 files changed

Lines changed: 134 additions & 0 deletions

File tree

clokey-api/src/main/java/org/clokey/domain/history/controller/HistoryController.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.clokey.domain.history.dto.response.MonthlyHistoryResponse;
1717
import org.clokey.domain.history.dto.response.SituationListResponse;
1818
import org.clokey.domain.history.dto.response.StyleListResponse;
19+
import org.clokey.domain.history.dto.response.TodayHistoryExistenceResponse;
1920
import org.clokey.domain.history.service.HistoryService;
2021
import org.clokey.response.BaseResponse;
2122
import org.springframework.validation.annotation.Validated;
@@ -127,6 +128,16 @@ public BaseResponse<HistoryOwnershipCheckResponse> checkHistoryOwnership(
127128
return BaseResponse.onSuccess(GlobalBaseSuccessCode.OK, response);
128129
}
129130

131+
@GetMapping("/today/existence")
132+
@Operation(
133+
operationId = "History_checkTodayHistoryExistence",
134+
summary = "오늘 기록 존재 여부 확인",
135+
description = "현재 사용자가 오늘 작성한 기록이 존재하는지 확인합니다.")
136+
public BaseResponse<TodayHistoryExistenceResponse> checkTodayHistoryExistence() {
137+
TodayHistoryExistenceResponse response = historyService.checkTodayHistoryExistence();
138+
return BaseResponse.onSuccess(GlobalBaseSuccessCode.OK, response);
139+
}
140+
130141
@DeleteMapping("/{historyId}")
131142
@Operation(
132143
operationId = "History_deleteHistory",
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package org.clokey.domain.history.dto.response;
2+
3+
import io.swagger.v3.oas.annotations.media.Schema;
4+
5+
public record TodayHistoryExistenceResponse(
6+
@Schema(description = "오늘 기록 존재 여부", example = "true") boolean exists) {
7+
public static TodayHistoryExistenceResponse of(boolean exists) {
8+
return new TodayHistoryExistenceResponse(exists);
9+
}
10+
}

clokey-api/src/main/java/org/clokey/domain/history/repository/HistoryRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.clokey.domain.history.repository;
22

3+
import java.time.LocalDate;
34
import java.util.List;
45
import java.util.Optional;
56
import java.util.Set;
@@ -13,6 +14,8 @@ public interface HistoryRepository extends JpaRepository<History, Long> {
1314

1415
boolean existsByMemberId(Long memberId);
1516

17+
boolean existsByMemberIdAndHistoryDate(Long memberId, LocalDate historyDate);
18+
1619
@Query(
1720
"""
1821
select h from History h

clokey-api/src/main/java/org/clokey/domain/history/service/HistoryService.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,8 @@ public interface HistoryService {
2222

2323
HistoryOwnershipCheckResponse checkHistoryOwnership(Long historyId);
2424

25+
TodayHistoryExistenceResponse checkTodayHistoryExistence();
26+
2527
void deleteHistory(Long historyId);
2628

2729
HistoryImagesPresignedUrlResponse getHistoryUploadPresignedUrls(

clokey-api/src/main/java/org/clokey/domain/history/service/HistoryServiceImpl.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import org.clokey.domain.history.dto.response.MonthlyHistoryResponse;
2222
import org.clokey.domain.history.dto.response.SituationListResponse;
2323
import org.clokey.domain.history.dto.response.StyleListResponse;
24+
import org.clokey.domain.history.dto.response.TodayHistoryExistenceResponse;
2425
import org.clokey.domain.history.exception.HistoryErrorCode;
2526
import org.clokey.domain.history.exception.SituationErrorCode;
2627
import org.clokey.domain.history.exception.StyleErrorCode;
@@ -350,6 +351,16 @@ public HistoryOwnershipCheckResponse checkHistoryOwnership(Long historyId) {
350351
return HistoryOwnershipCheckResponse.of(isOwner);
351352
}
352353

354+
@Override
355+
public TodayHistoryExistenceResponse checkTodayHistoryExistence() {
356+
final Member currentMember = memberUtil.getCurrentMember();
357+
boolean exists =
358+
historyRepository.existsByMemberIdAndHistoryDate(
359+
currentMember.getId(), LocalDate.now(KST));
360+
361+
return TodayHistoryExistenceResponse.of(exists);
362+
}
363+
353364
@Override
354365
@Transactional
355366
public void deleteHistory(Long historyId) {

clokey-api/src/main/java/org/clokey/global/config/security/SecurityConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -66,6 +66,8 @@ public SecurityFilterChain apiFilterChain(
6666
http.authorizeHttpRequests(
6767
auth ->
6868
auth.requestMatchers("/public/**")
69+
.permitAll()
70+
.requestMatchers("/auth/reissue-token")
6971
.permitAll()
7072
.requestMatchers("/oauth2/**", "/login/oauth2/**")
7173
.permitAll()

clokey-api/src/test/java/org/clokey/domain/history/controller/HistoryControllerTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@
1919
import org.clokey.domain.history.dto.response.MonthlyHistoryResponse;
2020
import org.clokey.domain.history.dto.response.SituationListResponse;
2121
import org.clokey.domain.history.dto.response.StyleListResponse;
22+
import org.clokey.domain.history.dto.response.TodayHistoryExistenceResponse;
2223
import org.clokey.domain.history.service.HistoryService;
2324
import org.clokey.enums.FileExtension;
2425
import org.junit.jupiter.api.Nested;
@@ -922,4 +923,27 @@ class 기록_삭제_요청_시 {
922923
.andExpect(jsonPath("$.code").value("COMMON204"));
923924
}
924925
}
926+
927+
@Nested
928+
class 오늘_기록_존재_여부_확인_요청_시 {
929+
930+
@Test
931+
void 유효한_요청이면_오늘_기록_존재_여부를_반환한다() throws Exception {
932+
// given
933+
TodayHistoryExistenceResponse response = TodayHistoryExistenceResponse.of(true);
934+
935+
given(historyService.checkTodayHistoryExistence()).willReturn(response);
936+
937+
// when & then
938+
ResultActions perform =
939+
mockMvc.perform(
940+
get("/histories/today/existence")
941+
.contentType(MediaType.APPLICATION_JSON));
942+
943+
perform.andExpect(status().isOk())
944+
.andExpect(jsonPath("$.isSuccess").value(true))
945+
.andExpect(jsonPath("$.code").value("COMMON200"))
946+
.andExpect(jsonPath("$.result.exists").value(true));
947+
}
948+
}
925949
}

clokey-api/src/test/java/org/clokey/domain/history/service/HistoryServiceImplTest.java

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
import org.clokey.domain.history.dto.response.MonthlyHistoryResponse;
3232
import org.clokey.domain.history.dto.response.SituationListResponse;
3333
import org.clokey.domain.history.dto.response.StyleListResponse;
34+
import org.clokey.domain.history.dto.response.TodayHistoryExistenceResponse;
3435
import org.clokey.domain.history.exception.HistoryErrorCode;
3536
import org.clokey.domain.history.exception.SituationErrorCode;
3637
import org.clokey.domain.history.exception.StyleErrorCode;
@@ -1275,4 +1276,58 @@ void setUp() {
12751276
.hasMessage(HistoryErrorCode.LIMITED_AUTHORITY.getMessage());
12761277
}
12771278
}
1279+
1280+
@Nested
1281+
class 오늘_기록_존재_여부를_확인할_때 {
1282+
1283+
@BeforeEach
1284+
void setUp() {
1285+
Member member1 =
1286+
Member.createMember(
1287+
"testEmail1",
1288+
"testNickName1",
1289+
OauthInfo.createOauthInfo("testOauthId1", OauthProvider.KAKAO));
1290+
Member member2 =
1291+
Member.createMember(
1292+
"testEmail2",
1293+
"testNickName2",
1294+
OauthInfo.createOauthInfo("testOauthId2", OauthProvider.KAKAO));
1295+
1296+
memberRepository.saveAll(List.of(member1, member2));
1297+
given(memberUtil.getCurrentMember()).willReturn(member1);
1298+
1299+
Situation situation = Situation.createSituation("testSituation");
1300+
situationRepository.save(situation);
1301+
1302+
History todayHistory =
1303+
History.createHistory(LocalDate.now(), "testContent", member1, situation);
1304+
History otherHistory =
1305+
History.createHistory(
1306+
LocalDate.now().minusDays(1), "oldContent", member2, situation);
1307+
historyRepository.saveAll(List.of(todayHistory, otherHistory));
1308+
}
1309+
1310+
@Test
1311+
void 오늘_기록이_존재하면_true를_반환한다() {
1312+
// when
1313+
TodayHistoryExistenceResponse response = historyService.checkTodayHistoryExistence();
1314+
1315+
// then
1316+
assertThat(response.exists()).isTrue();
1317+
}
1318+
1319+
@Test
1320+
void 오늘_기록이_존재하지_않으면_false를_반환한다() {
1321+
// given
1322+
Member otherMember =
1323+
transactionUtil.getResult(() -> memberRepository.findById(2L).orElseThrow());
1324+
given(memberUtil.getCurrentMember()).willReturn(otherMember);
1325+
1326+
// when
1327+
TodayHistoryExistenceResponse response = historyService.checkTodayHistoryExistence();
1328+
1329+
// then
1330+
assertThat(response.exists()).isFalse();
1331+
}
1332+
}
12781333
}

clokey-api/src/test/java/org/clokey/global/config/security/SwaggerSecurityIntegrationTest.java

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,14 +2,17 @@
22

33
import static org.hamcrest.Matchers.containsString;
44
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get;
5+
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
56
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.header;
7+
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
68
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;
79

810
import com.google.firebase.messaging.FirebaseMessaging;
911
import org.junit.jupiter.api.Test;
1012
import org.springframework.beans.factory.annotation.Autowired;
1113
import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc;
1214
import org.springframework.boot.test.context.SpringBootTest;
15+
import org.springframework.http.MediaType;
1316
import org.springframework.test.context.ActiveProfiles;
1417
import org.springframework.test.context.TestPropertySource;
1518
import org.springframework.test.context.bean.override.mockito.MockitoBean;
@@ -62,4 +65,17 @@ void swaggerConfigWithoutCredentialsDoesNotRedirectToLogin() throws Exception {
6265
.andExpect(status().isUnauthorized())
6366
.andExpect(header().string("WWW-Authenticate", containsString("Basic")));
6467
}
68+
69+
@Test
70+
void reissueTokenWithoutAuthenticationReturnsJsonValidationError() throws Exception {
71+
mockMvc.perform(
72+
post("/auth/reissue-token")
73+
.contentType(MediaType.APPLICATION_JSON)
74+
.content("{\"refreshToken\":\"\"}"))
75+
.andExpect(status().isBadRequest())
76+
.andExpect(header().doesNotExist("Location"))
77+
.andExpect(jsonPath("$.isSuccess").value(false))
78+
.andExpect(jsonPath("$.code").value("COMMON400"))
79+
.andExpect(jsonPath("$.result.refreshToken").exists());
80+
}
6581
}

0 commit comments

Comments
 (0)