Skip to content

Commit

Permalink
Feat/#116 캐시 제거 API 추가 (#117)
Browse files Browse the repository at this point in the history
* feat: AppCache 삭제 API 구현

- AppCache 삭제를 진행하는 API를 구현했습니다.

* feat: AppService .evictAppCache 메서드 구현

- 앱 캐시 삭제 메서드를 구현했습니다.
  - 앱이 존재하는 경우 예외를 발생시킵니다.

* refactor: API 전달 형식 변경

- Body -> Path Parameter

* refactor: 앱 삭제시 캐시 삭제 API 호출 로직 추가

- 앱 삭제 요청시 API 호출을 통한 캐시 삭제 로직을 추가했습니다.

* remove: 사용하지 않는 테스트 삭제
  • Loading branch information
tidavid1 authored Aug 27, 2024
1 parent 0323c1b commit f2f0922
Show file tree
Hide file tree
Showing 8 changed files with 127 additions and 114 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@
public class AppService {

private static final String APP_NOT_FOUND_MESSAGE = "앱을 찾을 수 없습니다.";

public static final String APP_EXISTS_MESSAGE = "앱이 존재합니다.";

private final AppRepository appRepository;

@Cacheable(key = "#token")
Expand All @@ -24,7 +25,8 @@ public Long getAppIdByToken(String token) {

@CacheEvict(key = "#token")
public void evictAppCache(String token) {
// TODO: Implement cache eviction

appRepository.getAppIdByToken(token).ifPresent(id -> {
throw new IllegalStateException(APP_EXISTS_MESSAGE);
});
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package info.logbat.domain.project.presentation;

import info.logbat.domain.project.application.AppService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;


@RestController
@RequestMapping("/apps")
@RequiredArgsConstructor
public class AppController {

private final AppService appService;

@DeleteMapping("/{token}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void removeAppCache(@PathVariable String token) {
appService.evictAppCache(token);
}

}
Original file line number Diff line number Diff line change
@@ -1,24 +1,22 @@
package info.logbat.domain.project.repository;

import java.util.Optional;
import lombok.RequiredArgsConstructor;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;

import java.util.Optional;

@Repository
@RequiredArgsConstructor
public class AppRepository {

private final JdbcTemplate jdbcTemplate;

public Optional<Long> getAppIdByToken(String token) {
String selectQuery = "SELECT id FROM apps WHERE app_key = UNHEX(REPLACE(?, '-', ''))";
try {
String sql = "SELECT id FROM apps WHERE app_key = UNHEX(REPLACE(?, '-', ''))";
Long id = jdbcTemplate.queryForObject(sql, Long.class, token);
Long id = jdbcTemplate.queryForObject(selectQuery, Long.class, token);
return Optional.ofNullable(id);
}
catch (Exception e) {
} catch (Exception e) {
return Optional.empty();
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,15 @@
import info.logbat_meta.domain.project.presentation.payload.response.AppCommonResponse;
import info.logbat_meta.domain.project.repository.AppJpaRepository;
import info.logbat_meta.domain.project.repository.ProjectJpaRepository;
import java.util.List;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@Transactional
@RequiredArgsConstructor
@CacheConfig(cacheNames = {"app"})
public class AppService {

private static final String APP_NOT_FOUND_MESSAGE = "앱을 찾을 수 없습니다.";
Expand All @@ -44,11 +41,11 @@ public List<AppCommonResponse> getAppsByProjectId(Long projectId) {
return apps.stream().map(AppCommonResponse::from).toList();
}

public Long deleteApp(Long projectId, Long appId) {
public UUID deleteApp(Long projectId, Long appId) {
App app = appRepository.findByProject_IdAndId(projectId, appId)
.orElseThrow(() -> new IllegalArgumentException(APP_NOT_FOUND_MESSAGE));
appRepository.delete(app);
return app.getId();
return app.getAppKey();
}

private Project getProject(Long id) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
package info.logbat_meta.domain.project.presentation;

import static org.springframework.http.HttpStatus.NO_CONTENT;

import info.logbat_meta.common.payload.ApiCommonResponse;
import info.logbat_meta.domain.project.application.AppService;
import info.logbat_meta.domain.project.presentation.payload.request.AppCreateRequest;
import info.logbat_meta.domain.project.presentation.payload.response.AppCommonResponse;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import org.springframework.web.client.RestClient;

@RestController
@RequestMapping("/v1/projects/{projectId}/apps")
Expand All @@ -17,7 +21,8 @@ public class AppController {
private final AppService appService;

@GetMapping
public ApiCommonResponse<List<AppCommonResponse>> getAppsByProjectId(@PathVariable Long projectId) {
public ApiCommonResponse<List<AppCommonResponse>> getAppsByProjectId(
@PathVariable Long projectId) {
List<AppCommonResponse> apps = appService.getAppsByProjectId(projectId);

return ApiCommonResponse.createSuccessResponse(apps);
Expand All @@ -32,21 +37,32 @@ public ApiCommonResponse<AppCommonResponse> getAppById(@PathVariable Long appId)

@PostMapping
public ApiCommonResponse<AppCommonResponse> createApp(
@PathVariable Long projectId,
@RequestBody AppCreateRequest appCreateRequest) {
AppCommonResponse app = appService.createApp(projectId, appCreateRequest.name(), appCreateRequest.appType());
@PathVariable Long projectId,
@RequestBody AppCreateRequest appCreateRequest) {
AppCommonResponse app = appService.createApp(projectId, appCreateRequest.name(),
appCreateRequest.appType());

return ApiCommonResponse.createSuccessResponse(app);
}

@DeleteMapping("/{appId}")
public ApiCommonResponse<Long> deleteApp(
@PathVariable Long projectId,
@PathVariable Long appId
public ApiCommonResponse<Void> deleteApp(
@PathVariable Long projectId,
@PathVariable Long appId
) {
Long deletedId = appService.deleteApp(projectId, appId);
UUID deletedAppToken = appService.deleteApp(projectId, appId);
sendAPIRequest(deletedAppToken.toString());
return ApiCommonResponse.createSuccessResponse();
}

return ApiCommonResponse.createSuccessResponse(deletedId);
private void sendAPIRequest(String uuid) {
RestClient restClient = RestClient.create("https://api.logbat.info/");
restClient.delete().uri("/apps/" + uuid)
.retrieve()
.onStatus(status -> status != NO_CONTENT, (request1, response) -> {
throw new IllegalArgumentException("API 요청 실패");
})
.toEntity(Void.class);
}

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import info.logbat_meta.domain.project.presentation.payload.response.AppCommonResponse;
import info.logbat_meta.domain.project.repository.AppJpaRepository;
import info.logbat_meta.domain.project.repository.ProjectJpaRepository;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
import org.junit.jupiter.api.Test;
Expand Down Expand Up @@ -58,7 +59,8 @@ void willThrowExceptionWhenProjectNotFound() {
Long notExistProjectId = 2L;
given(projectRepository.findById(notExistProjectId)).willReturn(Optional.empty());
// Act & Assert
assertThatThrownBy(() -> appService.createApp(notExistProjectId, expectedProjectName, expectedAppType))
assertThatThrownBy(
() -> appService.createApp(notExistProjectId, expectedProjectName, expectedAppType))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("프로젝트를 찾을 수 없습니다.");
}
Expand All @@ -71,7 +73,8 @@ void willThrowExceptionWhenAppTypeIsInvalid() {
given(projectRepository.findById(expectedProjectId)).willReturn(
Optional.of(expectedProject));
// Act & Assert
assertThatThrownBy(() -> appService.createApp(expectedProjectId, expectedProjectName, invalidAppType))
assertThatThrownBy(
() -> appService.createApp(expectedProjectId, expectedProjectName, invalidAppType))
.isInstanceOf(IllegalArgumentException.class)
.hasMessage("잘못된 앱 타입 요청입니다.");
}
Expand Down Expand Up @@ -107,7 +110,9 @@ void willCreateNewApp() {
@Nested
@DisplayName("App을 조회할 때")
class whenGetApp {
private final App expectedApp = spy(App.of(expectedProject, expectedProjectName, AppType.JAVA));

private final App expectedApp = spy(
App.of(expectedProject, expectedProjectName, AppType.JAVA));

@Test
@DisplayName("ID로 조회할 수 있다.")
Expand Down Expand Up @@ -156,13 +161,14 @@ class whenDeleteApp {
@DisplayName("프로젝트와 앱 ID로 삭제할 수 있다.")
void canDeleteAppByProjectIdAndAppId() {
// Arrange
UUID expectedAppKey = UUID.randomUUID();
given(appRepository.findByProject_IdAndId(expectedProjectId, expectedAppId)).willReturn(
Optional.of(expectedApp));
given(expectedApp.getId()).willReturn(expectedAppId);
given(expectedApp.getAppKey()).willReturn(expectedAppKey);
// Act
Long actualResult = appService.deleteApp(expectedProjectId, expectedAppId);
UUID actualResult = appService.deleteApp(expectedProjectId, expectedAppId);
// Assert
assertThat(actualResult).isEqualTo(expectedAppId);
assertThat(actualResult).isEqualTo(expectedAppKey);
}

@Test
Expand Down
Loading

0 comments on commit f2f0922

Please sign in to comment.