diff --git a/logbat/build.gradle b/logbat/build.gradle index 910dacc..1bf2591 100644 --- a/logbat/build.gradle +++ b/logbat/build.gradle @@ -27,14 +27,16 @@ repositories { dependencies { // SpringBoot Web implementation 'org.springframework.boot:spring-boot-starter-web' - // SpringBoot JPA - implementation 'org.springframework.boot:spring-boot-starter-data-jpa' // SpringBoot Validation implementation 'org.springframework.boot:spring-boot-starter-validation' // Caffeine implementation 'com.github.ben-manes.caffeine:caffeine:3.1.8' // SpringBoot Cache implementation 'org.springframework.boot:spring-boot-starter-cache' + // Spring AOP + implementation 'org.springframework.boot:spring-boot-starter-aop' + // JDBC + implementation 'org.springframework.boot:spring-boot-starter-jdbc' // Lombok compileOnly 'org.projectlombok:lombok' annotationProcessor 'org.projectlombok:lombok' diff --git a/logbat/src/main/java/info/logbat/common/config/JpaConfiguration.java b/logbat/src/main/java/info/logbat/common/config/JpaConfiguration.java deleted file mode 100644 index 983e700..0000000 --- a/logbat/src/main/java/info/logbat/common/config/JpaConfiguration.java +++ /dev/null @@ -1,10 +0,0 @@ -package info.logbat.common.config; - -import org.springframework.context.annotation.Configuration; -import org.springframework.data.jpa.repository.config.EnableJpaAuditing; - -@Configuration -@EnableJpaAuditing -public class JpaConfiguration { - -} diff --git a/logbat/src/main/java/info/logbat/domain/log/application/LogService.java b/logbat/src/main/java/info/logbat/domain/log/application/LogService.java index 8bf32e9..624dfbb 100644 --- a/logbat/src/main/java/info/logbat/domain/log/application/LogService.java +++ b/logbat/src/main/java/info/logbat/domain/log/application/LogService.java @@ -20,7 +20,7 @@ public long saveLog(CreateLogServiceRequest request) { String data = request.data(); LocalDateTime timestamp = request.timestamp(); - Long appId = appService.getAppByToken(request.appKey()).id(); + Long appId = appService.getAppIdByToken(request.appKey()); Log log = Log.of(appId, level, data, timestamp); diff --git a/logbat/src/main/java/info/logbat/domain/log/repository/AsyncLogProcessor.java b/logbat/src/main/java/info/logbat/domain/log/repository/AsyncLogProcessor.java index 50f8a25..200ec12 100644 --- a/logbat/src/main/java/info/logbat/domain/log/repository/AsyncLogProcessor.java +++ b/logbat/src/main/java/info/logbat/domain/log/repository/AsyncLogProcessor.java @@ -46,9 +46,10 @@ public AsyncLogProcessor(@Value("${jdbc.async.timeout}") Long timeout, throw new IllegalArgumentException("DataSource is null"); } int poolSize = ((HikariDataSource) dataSource).getMaximumPoolSize(); + log.debug("Creating AsyncLogProcessor with pool size: {}", poolSize); // use 50% of the pool size for the follower thread pool - followerExecutor = Executors.newFixedThreadPool(poolSize * 5 / 10); + this.followerExecutor = Executors.newFixedThreadPool(poolSize * 5 / 10); this.defaultTimeout = Objects.requireNonNullElse(timeout, 2000L); this.defaultBulkSize = Objects.requireNonNullElse(bulkSize, 100); } diff --git a/logbat/src/main/java/info/logbat/domain/project/application/AppService.java b/logbat/src/main/java/info/logbat/domain/project/application/AppService.java index d124176..cd991b8 100644 --- a/logbat/src/main/java/info/logbat/domain/project/application/AppService.java +++ b/logbat/src/main/java/info/logbat/domain/project/application/AppService.java @@ -1,73 +1,30 @@ package info.logbat.domain.project.application; -import info.logbat.domain.project.domain.App; -import info.logbat.domain.project.domain.Project; -import info.logbat.domain.project.domain.enums.AppType; -import info.logbat.domain.project.presentation.payload.response.AppCommonResponse; -import info.logbat.domain.project.repository.AppJpaRepository; -import info.logbat.domain.project.repository.ProjectJpaRepository; -import java.util.List; -import java.util.UUID; +import info.logbat.domain.project.repository.AppRepository; import lombok.RequiredArgsConstructor; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; @Service -@Transactional @RequiredArgsConstructor @CacheConfig(cacheNames = {"app"}) public class AppService { private static final String APP_NOT_FOUND_MESSAGE = "앱을 찾을 수 없습니다."; - private final AppJpaRepository appRepository; - private final ProjectJpaRepository projectRepository; + private final AppRepository appRepository; - public AppCommonResponse createApp(Long projectId, String appTypeStr) { - Project project = getProject(projectId); - AppType appType = AppType.from(appTypeStr); - return AppCommonResponse.from(appRepository.save(App.of(project, appType))); - } - - @Transactional(readOnly = true) @Cacheable(key = "#token") - public AppCommonResponse getAppByToken(String token) { - UUID tokenUUID = UUID.fromString(token); - App app = appRepository.findByAppKey(tokenUUID) - .orElseThrow(() -> new IllegalArgumentException(APP_NOT_FOUND_MESSAGE)); - return AppCommonResponse.from(app); - } - - @Transactional(readOnly = true) - public AppCommonResponse getAppById(Long id) { - App app = appRepository.findById(id) + public Long getAppIdByToken(String token) { + return appRepository.getAppIdByToken(token) .orElseThrow(() -> new IllegalArgumentException(APP_NOT_FOUND_MESSAGE)); - return AppCommonResponse.from(app); - } - - @Transactional(readOnly = true) - public List getAppsByProjectId(Long projectId) { - List apps = appRepository.findByProject_Id(projectId); - return apps.stream().map(AppCommonResponse::from).toList(); - } - - public Long deleteApp(Long projectId, Long appId) { - App app = appRepository.findByProject_IdAndId(projectId, appId) - .orElseThrow(() -> new IllegalArgumentException(APP_NOT_FOUND_MESSAGE)); - appRepository.delete(app); - evictAppCache(app.getAppKey().toString()); - return app.getId(); } @CacheEvict(key = "#token") public void evictAppCache(String token) { - } + // TODO: Implement cache eviction - private Project getProject(Long id) { - return projectRepository.findById(id) - .orElseThrow(() -> new IllegalArgumentException("프로젝트를 찾을 수 없습니다.")); } } diff --git a/logbat/src/main/java/info/logbat/domain/project/presentation/AppController.java b/logbat/src/main/java/info/logbat/domain/project/presentation/AppController.java deleted file mode 100644 index e8cd7f9..0000000 --- a/logbat/src/main/java/info/logbat/domain/project/presentation/AppController.java +++ /dev/null @@ -1,48 +0,0 @@ -package info.logbat.domain.project.presentation; - -import info.logbat.common.payload.ApiCommonResponse; -import info.logbat.domain.project.application.AppService; -import info.logbat.domain.project.presentation.payload.request.AppCreateRequest; -import info.logbat.domain.project.presentation.payload.response.AppCommonResponse; -import java.util.List; -import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/v1/projects/apps") -@RequiredArgsConstructor -public class AppController { - - private final AppService appService; - - @GetMapping("/{projectId}") - public ApiCommonResponse> getAppsByProjectId( - @PathVariable Long projectId) { - return ApiCommonResponse.createSuccessResponse(appService.getAppsByProjectId(projectId)); - } - - @GetMapping("/info/{id}") - public ApiCommonResponse getAppById(@PathVariable Long id) { - return ApiCommonResponse.createSuccessResponse(appService.getAppById(id)); - } - - @PostMapping - public ApiCommonResponse createApp( - @RequestBody AppCreateRequest appCreateRequest) { - return ApiCommonResponse.createSuccessResponse( - appService.createApp(appCreateRequest.projectId(), appCreateRequest.appType())); - } - - @DeleteMapping("/{projectId}/{appId}") - public ApiCommonResponse deleteApp(@PathVariable Long projectId, - @PathVariable Long appId) { - return ApiCommonResponse.createSuccessResponse(appService.deleteApp(projectId, appId)); - } - -} diff --git a/logbat/src/main/java/info/logbat/domain/project/presentation/ProjectController.java b/logbat/src/main/java/info/logbat/domain/project/presentation/ProjectController.java deleted file mode 100644 index f5e08ba..0000000 --- a/logbat/src/main/java/info/logbat/domain/project/presentation/ProjectController.java +++ /dev/null @@ -1,53 +0,0 @@ -package info.logbat.domain.project.presentation; - -import info.logbat.common.payload.ApiCommonResponse; -import info.logbat.domain.project.application.ProjectService; -import info.logbat.domain.project.presentation.payload.request.ProjectCreateRequest; -import info.logbat.domain.project.presentation.payload.request.ProjectUpdateRequest; -import info.logbat.domain.project.presentation.payload.response.ProjectCommonResponse; -import lombok.RequiredArgsConstructor; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.DeleteMapping; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.PutMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -@RestController -@RequestMapping("/v1/projects") -@RequiredArgsConstructor -public class ProjectController { - - private final ProjectService projectService; - - @GetMapping("/{name}") - public ApiCommonResponse get(@PathVariable String name) { - return ApiCommonResponse.createSuccessResponse(projectService.getProjectByName(name)); - } - - @PostMapping - public ResponseEntity> create( - @RequestBody ProjectCreateRequest request) { - ApiCommonResponse response = ApiCommonResponse.createApiResponse( - HttpStatus.CREATED, "Success", projectService.createProject(request.name())); - return ResponseEntity.status(HttpStatus.CREATED).body(response); - } - - @PutMapping("/{id}") - public ApiCommonResponse update(@PathVariable Long id, - @RequestBody ProjectUpdateRequest request) { - return ApiCommonResponse.createSuccessResponse( - projectService.updateProjectValues(id, request.name())); - } - - @DeleteMapping("/{id}") - public ApiCommonResponse delete(@PathVariable Long id) { - Long expectedId = projectService.deleteProject(id); - return ApiCommonResponse.createSuccessResponse(expectedId); - } - -} diff --git a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/AppCreateRequest.java b/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/AppCreateRequest.java deleted file mode 100644 index 12ff276..0000000 --- a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/AppCreateRequest.java +++ /dev/null @@ -1,5 +0,0 @@ -package info.logbat.domain.project.presentation.payload.request; - -public record AppCreateRequest(Long projectId, String appType) { - -} diff --git a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/ProjectCreateRequest.java b/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/ProjectCreateRequest.java deleted file mode 100644 index dad036c..0000000 --- a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/ProjectCreateRequest.java +++ /dev/null @@ -1,5 +0,0 @@ -package info.logbat.domain.project.presentation.payload.request; - -public record ProjectCreateRequest(String name) { - -} diff --git a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/ProjectUpdateRequest.java b/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/ProjectUpdateRequest.java deleted file mode 100644 index 6261346..0000000 --- a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/request/ProjectUpdateRequest.java +++ /dev/null @@ -1,5 +0,0 @@ -package info.logbat.domain.project.presentation.payload.request; - -public record ProjectUpdateRequest(String name) { - -} diff --git a/logbat/src/main/java/info/logbat/domain/project/repository/AppRepository.java b/logbat/src/main/java/info/logbat/domain/project/repository/AppRepository.java new file mode 100644 index 0000000..a74179d --- /dev/null +++ b/logbat/src/main/java/info/logbat/domain/project/repository/AppRepository.java @@ -0,0 +1,25 @@ +package info.logbat.domain.project.repository; + +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 getAppIdByToken(String token) { + try { + String sql = "SELECT id FROM apps WHERE app_key = UNHEX(REPLACE(?, '-', ''))"; + Long id = jdbcTemplate.queryForObject(sql, Long.class, token); + return Optional.ofNullable(id); + } + catch (Exception e) { + return Optional.empty(); + } + } +} diff --git a/logbat/src/main/resources/application.yml b/logbat/src/main/resources/application.yml index 9b16de0..a2613f6 100644 --- a/logbat/src/main/resources/application.yml +++ b/logbat/src/main/resources/application.yml @@ -19,10 +19,8 @@ spring: username: root password: driver-class-name: com.mysql.cj.jdbc.Driver - - jpa: - hibernate: - ddl-auto: create + hikari: + maximum-pool-size: 10 --- spring: @@ -35,7 +33,5 @@ spring: username: root password: driver-class-name: com.mysql.cj.jdbc.Driver - - jpa: - hibernate: - ddl-auto: none + hikari: + maximum-pool-size: 10 \ No newline at end of file diff --git a/logbat/src/test/java/info/logbat/domain/common/ControllerTestSupport.java b/logbat/src/test/java/info/logbat/domain/common/ControllerTestSupport.java index 5ea3c37..a40630d 100644 --- a/logbat/src/test/java/info/logbat/domain/common/ControllerTestSupport.java +++ b/logbat/src/test/java/info/logbat/domain/common/ControllerTestSupport.java @@ -6,9 +6,6 @@ import info.logbat.domain.log.application.LogService; import info.logbat.domain.log.presentation.LogController; import info.logbat.domain.project.application.AppService; -import info.logbat.domain.project.application.ProjectService; -import info.logbat.domain.project.presentation.AppController; -import info.logbat.domain.project.presentation.ProjectController; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -18,8 +15,7 @@ /* * 이후 테스트 하려면 Controller에 대해 controllers에 추가하고, ControllerTestSupport를 상속받아 테스트를 진행하시면 됩니다. */ -@WebMvcTest(controllers = {LogController.class, ProjectController.class, AppController.class, - CountTestController.class}) +@WebMvcTest(controllers = {LogController.class, CountTestController.class}) @ActiveProfiles("test") public abstract class ControllerTestSupport { @@ -32,9 +28,6 @@ public abstract class ControllerTestSupport { @MockBean protected LogService logService; - @MockBean - protected ProjectService projectService; - @MockBean protected AppService appService; diff --git a/logbat/src/test/java/info/logbat/domain/log/application/LogServiceTest.java b/logbat/src/test/java/info/logbat/domain/log/application/LogServiceTest.java index a717f42..599a98d 100644 --- a/logbat/src/test/java/info/logbat/domain/log/application/LogServiceTest.java +++ b/logbat/src/test/java/info/logbat/domain/log/application/LogServiceTest.java @@ -1,17 +1,12 @@ package info.logbat.domain.log.application; -import static org.assertj.core.api.Assertions.assertThatCode; import static org.assertj.core.api.Assertions.assertThatThrownBy; import info.logbat.domain.log.application.payload.request.CreateLogServiceRequest; -import info.logbat.domain.log.repository.LogRepository; -import info.logbat.domain.project.domain.App; -import info.logbat.domain.project.domain.Project; -import info.logbat.domain.project.domain.enums.AppType; -import info.logbat.domain.project.repository.AppJpaRepository; -import info.logbat.domain.project.repository.ProjectJpaRepository; import java.time.LocalDateTime; import java.util.UUID; + +import org.junit.jupiter.api.Disabled; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.springframework.beans.factory.annotation.Autowired; @@ -28,18 +23,12 @@ class LogServiceTest { @Autowired private LogService logService; - @Autowired - private LogRepository logRepository; - - @Autowired - private ProjectJpaRepository projectJpaRepository; - - @Autowired - private AppJpaRepository appJpaRepository; - @DisplayName("Log를 저장할 수 있다.") + @Disabled("APP, PROJECT domain이 없어서 테스트가 불가능합니다.") @Test void saveLog() { + throw new UnsupportedOperationException("아직 구현되지 않았습니다."); + /* // given String 프로젝트_이름 = "테스트_프로젝트"; Project 프로젝트 = Project.from(프로젝트_이름); @@ -63,6 +52,7 @@ void saveLog() { // when & then assertThatCode(() -> logService.saveLog(요청_DTO)) .doesNotThrowAnyException(); + */ } @DisplayName("존재하지 않는 Application Key로 Log를 저장할 수 없다.") diff --git a/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/LogTest.java b/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/LogTest.java deleted file mode 100644 index a12a881..0000000 --- a/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/LogTest.java +++ /dev/null @@ -1,154 +0,0 @@ -package info.logbat.domain.project.domain.log.domain; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import info.logbat.domain.log.domain.Log; -import info.logbat.domain.log.domain.enums.Level; -import java.time.LocalDateTime; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; - -@DisplayName("Log 도메인 테스트") -class LogTest { - - - @Nested - @DisplayName("새로운 Log 도메인 객체 생성") - class CreateNewLog { - - @Test - @DisplayName("새로 만든 Log 도메인 객체를 만들 수 있다.") - void createLog() { - // given - Long 앱_ID = 1L; - String 로그_데이터 = "테스트_로그_데이터"; - String 로그_레벨 = "INFO"; - LocalDateTime 로그_타임스탬프 = LocalDateTime.of(2021, 1, 1, 0, 0, 0); - - // when - Log log = new Log(앱_ID, 로그_레벨, 로그_데이터, 로그_타임스탬프); - - // then - assertThat(log) - .extracting("id", "appId", "level", "data.value", "timestamp") - .containsExactly(null, 앱_ID, Level.INFO, 로그_데이터, 로그_타임스탬프); - } - - @Test - @DisplayName("appId가 null이면 예외가 발생한다.") - void createLogWithNullApplicationId() { - // given - String 로그_데이터 = "테스트_로그_데이터"; - String 로그_레벨 = "INFO"; - LocalDateTime 로그_타임스탬프 = LocalDateTime.of(2021, 1, 1, 0, 0, 0); - - // when & then - assertThatThrownBy(() -> new Log(null, 로그_레벨, 로그_데이터, 로그_타임스탬프)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("appId는 null일 수 없고 0보다 커야 합니다."); - } - - @Test - @DisplayName("appId가 0이하면 예외가 발생한다.") - void createLogWithEmptyApplicationId() { - // given - Long 앱_ID = 0L; - String 로그_데이터 = "테스트_로그_데이터"; - String 로그_레벨 = "INFO"; - LocalDateTime 로그_타임스탬프 = LocalDateTime.of(2021, 1, 1, 0, 0, 0); - - // when & then - assertThatThrownBy(() -> new Log(앱_ID, 로그_레벨, 로그_데이터, 로그_타임스탬프)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("appId는 null일 수 없고 0보다 커야 합니다."); - } - - @Test - @DisplayName("timestamp가 null이면 예외가 발생한다.") - void createLogWithNullTimestamp() { - // given - Long 앱_ID = 1L; - String 로그_데이터 = "테스트_로그_데이터"; - String 로그_레벨 = "INFO"; - LocalDateTime 로그_타임스탬프 = null; - - // when & then - assertThatThrownBy(() -> new Log(앱_ID, 로그_레벨, 로그_데이터, 로그_타임스탬프)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("timestamp는 null이 될 수 없습니다."); - } - } - - @Nested - @DisplayName("DB에 저장된 Log 도메인 객체 생성") - class CreateExistingLog { - - @Test - @DisplayName("DB에 저장된 Log 도메인 객체를 만들 수 있다.") - void createLogWithLogId() { - // given - Long 앱_ID = 1L; - Long 로그_ID = 1L; - String 로그_데이터 = "테스트_로그_데이터"; - String 로그_레벨 = "INFO"; - LocalDateTime 로그_타임스탬프 = LocalDateTime.of(2021, 1, 1, 0, 0, 0); - - // when - Log log = new Log(로그_ID, 앱_ID, 로그_레벨, 로그_데이터, 로그_타임스탬프); - - // then - assertThat(log) - .extracting("id", "appId", "level", "data.value", "timestamp") - .containsExactly(로그_ID, 앱_ID, Level.INFO, 로그_데이터, 로그_타임스탬프); - } - - @Test - @DisplayName("appId가 null이면 예외가 발생한다.") - void createLogWithNullApplicationIdWithLogId() { - // given - Long 로그_ID = 1L; - String 로그_데이터 = "테스트_로그_데이터"; - String 로그_레벨 = "INFO"; - LocalDateTime 로그_타임스탬프 = LocalDateTime.of(2021, 1, 1, 0, 0, 0); - - // when & then - assertThatThrownBy(() -> new Log(로그_ID, null, 로그_레벨, 로그_데이터, 로그_타임스탬프)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("appId는 null일 수 없고 0보다 커야 합니다."); - } - - @Test - @DisplayName("appId가 0이하면 예외가 발생한다.") - void createLogWithEmptyApplicationIdWithLogId() { - // given - Long 로그_ID = 1L; - Long 앱_ID = 0L; - String 로그_데이터 = "테스트_로그_데이터"; - String 로그_레벨 = "INFO"; - LocalDateTime 로그_타임스탬프 = LocalDateTime.of(2021, 1, 1, 0, 0, 0); - - // when & then - assertThatThrownBy(() -> new Log(로그_ID, 앱_ID, 로그_레벨, 로그_데이터, 로그_타임스탬프)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("appId는 null일 수 없고 0보다 커야 합니다."); - } - - @Test - @DisplayName("timestamp가 null이면 예외가 발생한다.") - void createLogWithNullTimestampWithLogId() { - // given - Long 로그_ID = 1L; - Long 앱_ID = 1L; - String 로그_데이터 = "테스트_로그_데이터"; - String 로그_레벨 = "INFO"; - LocalDateTime 로그_타임스탬프 = null; - - // when & then - assertThatThrownBy(() -> new Log(로그_ID, 앱_ID, 로그_레벨, 로그_데이터, 로그_타임스탬프)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("timestamp는 null이 될 수 없습니다."); - } - } -} \ No newline at end of file diff --git a/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/enums/LevelTest.java b/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/enums/LevelTest.java deleted file mode 100644 index bf2f53e..0000000 --- a/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/enums/LevelTest.java +++ /dev/null @@ -1,98 +0,0 @@ -package info.logbat.domain.project.domain.log.domain.enums; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.util.stream.Stream; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.params.ParameterizedTest; -import org.junit.jupiter.params.provider.Arguments; -import org.junit.jupiter.params.provider.MethodSource; - -import info.logbat.domain.log.domain.enums.Level; - -@DisplayName("로그 Level enum 테스트") -class LevelTest { - - @DisplayName("빈 문자열의 로그 레벨 문자열을 enum으로 변환하면 예외가 발생한다.") - @Test - void createLevelWithBlank() { - // given - String 빈_로그_레벨 = ""; - - // when & then - assertThatThrownBy(() -> Level.from(빈_로그_레벨)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("level은 null이거나 빈 문자열일 수 없습니다."); - } - - @DisplayName("null의 로그 레벨 문자열을 enum으로 변환하면 예외가 발생한다.") - @Test - void createLevelWithNull() { - // given - String null_로그_레벨 = null; - - // when & then - assertThatThrownBy(() -> Level.from(null_로그_레벨)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("level은 null이거나 빈 문자열일 수 없습니다."); - } - - @DisplayName("로그 레벨 문자열이 앞 뒤에 공백을 포함하더라도 enum으로 변환할 수 있다.") - @Test - void createLevelWithBlankSpace() { - // given - String 공백_포함_로그_레벨 = " INFO "; - - // when - Level 로그_레벨 = Level.from(공백_포함_로그_레벨); - - // then - assertThat(로그_레벨).isEqualTo(Level.INFO); - } - - @DisplayName("올바르지 않은 로그 레벨 문자열을 enum으로 변환하면 예외가 발생한다.") - @ParameterizedTest(name = "{index}. 잘못된 입력값: {0}") - @MethodSource("invalidLogLevelProvider") - void createLevelWithInvalidLevel(String 잘못된_로그_레벨) { - // when & then - assertThatThrownBy(() -> Level.from(잘못된_로그_레벨)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("level이 올바르지 않습니다."); - } - - private static Stream invalidLogLevelProvider() { - return Stream.of( - Arguments.of("infos"), - Arguments.of("infor"), - Arguments.of("eror"), - Arguments.of("errr"), - Arguments.of("err"), - Arguments.of("errorr")) - ; - } - - @DisplayName("다양한 형식의 로그 레벨 문자열을 enum으로 변환할 수 있다.") - @ParameterizedTest(name = "{index}. 입력값: {0}, 기대값: {1} enum") - @MethodSource("logLevelProvider") - void createLevel(String inputLevel, Level expectedLevel) { - // when - Level 로그_레벨 = Level.from(inputLevel); - - // then - assertThat(로그_레벨).isEqualTo(expectedLevel); - } - - private static Stream logLevelProvider() { - return Stream.of( - Arguments.of("info", Level.INFO), - Arguments.of("iNfO", Level.INFO), - Arguments.of("Info", Level.INFO), - Arguments.of("INFO", Level.INFO), - Arguments.of("error", Level.ERROR), - Arguments.of("ErRoR", Level.ERROR), - Arguments.of("ERROR", Level.ERROR) - ); - } -} \ No newline at end of file diff --git a/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/values/LogDataTest.java b/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/values/LogDataTest.java deleted file mode 100644 index 8cf424b..0000000 --- a/logbat/src/test/java/info/logbat/domain/project/domain/log/domain/values/LogDataTest.java +++ /dev/null @@ -1,52 +0,0 @@ -package info.logbat.domain.project.domain.log.domain.values; - -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; - -import info.logbat.domain.log.domain.values.LogData; - -@DisplayName("LogData VO 테스트") -class LogDataTest { - - @DisplayName("정상적으로 LogData를 만들 수 있다.") - @Test - void createLogData() { - // given - String 로그_데이터_문자열 = "테스트_로그_데이터"; - - // when - LogData 로그_데이터 = LogData.from(로그_데이터_문자열); - - // then - assertThat(로그_데이터) - .extracting("value") - .isEqualTo(로그_데이터_문자열); - } - - @DisplayName("빈 문자열로 LogData를 만들면 예외가 발생한다.") - @Test - void createLogDataWithBlank() { - // given - String 빈_로그_데이터 = ""; - - // when & then - assertThatThrownBy(() -> LogData.from(빈_로그_데이터)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("log data는 null이거나 빈 문자열일 수 없습니다."); - } - - @DisplayName("null로 LogData를 만들면 예외가 발생한다.") - @Test - void createLogDataWithNull() { - // given - String null_로그_데이터 = null; - - // when & then - assertThatThrownBy(() -> LogData.from(null_로그_데이터)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("log data는 null이거나 빈 문자열일 수 없습니다."); - } -} \ No newline at end of file diff --git a/logbat_meta/.gitignore b/logbat_meta/.gitignore new file mode 100644 index 0000000..c2065bc --- /dev/null +++ b/logbat_meta/.gitignore @@ -0,0 +1,37 @@ +HELP.md +.gradle +build/ +!gradle/wrapper/gradle-wrapper.jar +!**/src/main/**/build/ +!**/src/test/**/build/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache +bin/ +!**/src/main/**/bin/ +!**/src/test/**/bin/ + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr +out/ +!**/src/main/**/out/ +!**/src/test/**/out/ + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ + +### VS Code ### +.vscode/ diff --git a/logbat_meta/build.gradle b/logbat_meta/build.gradle new file mode 100644 index 0000000..1d54c52 --- /dev/null +++ b/logbat_meta/build.gradle @@ -0,0 +1,46 @@ +plugins { + id 'java' + id 'org.springframework.boot' version '3.3.2' + id 'io.spring.dependency-management' version '1.1.6' +} + +group = 'info' +version = '0.0.1-SNAPSHOT' + +java { + toolchain { + languageVersion = JavaLanguageVersion.of(17) + } +} + +configurations { + compileOnly { + extendsFrom annotationProcessor + } +} + +repositories { + mavenCentral() +} + +dependencies { + // SpringBoot Web + implementation 'org.springframework.boot:spring-boot-starter-web' + // SpringBoot JPA + implementation 'org.springframework.boot:spring-boot-starter-data-jpa' + // SpringBoot Validation + implementation 'org.springframework.boot:spring-boot-starter-validation' + // Lombok + compileOnly 'org.projectlombok:lombok' + annotationProcessor 'org.projectlombok:lombok' + // MySQL Connector + runtimeOnly 'com.mysql:mysql-connector-j' + + // SpringBoot Test + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testRuntimeOnly 'org.junit.platform:junit-platform-launcher' +} + +tasks.named('test') { + useJUnitPlatform() +} diff --git a/logbat_meta/gradle/wrapper/gradle-wrapper.jar b/logbat_meta/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..e644113 Binary files /dev/null and b/logbat_meta/gradle/wrapper/gradle-wrapper.jar differ diff --git a/logbat_meta/gradle/wrapper/gradle-wrapper.properties b/logbat_meta/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..a441313 --- /dev/null +++ b/logbat_meta/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-8.8-bin.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/logbat_meta/gradlew b/logbat_meta/gradlew new file mode 100755 index 0000000..b740cf1 --- /dev/null +++ b/logbat_meta/gradlew @@ -0,0 +1,249 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd "${APP_HOME:-./}" > /dev/null && pwd -P ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/logbat_meta/gradlew.bat b/logbat_meta/gradlew.bat new file mode 100644 index 0000000..25da30d --- /dev/null +++ b/logbat_meta/gradlew.bat @@ -0,0 +1,92 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/logbat_meta/settings.gradle b/logbat_meta/settings.gradle new file mode 100644 index 0000000..27b5d8d --- /dev/null +++ b/logbat_meta/settings.gradle @@ -0,0 +1 @@ +rootProject.name = 'logbat_meta' diff --git a/logbat_meta/src/main/java/info/logbat_meta/LogbatMetaApplication.java b/logbat_meta/src/main/java/info/logbat_meta/LogbatMetaApplication.java new file mode 100644 index 0000000..c480534 --- /dev/null +++ b/logbat_meta/src/main/java/info/logbat_meta/LogbatMetaApplication.java @@ -0,0 +1,13 @@ +package info.logbat_meta; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@SpringBootApplication +public class LogbatMetaApplication { + + public static void main(String[] args) { + SpringApplication.run(LogbatMetaApplication.class, args); + } + +} diff --git a/logbat_meta/src/main/java/info/logbat_meta/common/payload/ApiCommonResponse.java b/logbat_meta/src/main/java/info/logbat_meta/common/payload/ApiCommonResponse.java new file mode 100644 index 0000000..a566cdd --- /dev/null +++ b/logbat_meta/src/main/java/info/logbat_meta/common/payload/ApiCommonResponse.java @@ -0,0 +1,48 @@ +package info.logbat_meta.common.payload; + +import lombok.AccessLevel; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.springframework.http.HttpStatus; + +@Getter +@NoArgsConstructor(access = AccessLevel.PRIVATE) +public class ApiCommonResponse { + + private int statusCode; + private String message; + private T data; + + private ApiCommonResponse(int status, String message, T data) { + this.statusCode = status; + this.message = message; + this.data = data; + } + + private ApiCommonResponse(int statusCode, String message) { + this.statusCode = statusCode; + this.message = message; + } + + private ApiCommonResponse(int statusCode) { + this.statusCode = statusCode; + } + + public static ApiCommonResponse createApiResponse(HttpStatus httpStatus, String message, + T data) { + return new ApiCommonResponse<>(httpStatus.value(), message, data); + } + + public static ApiCommonResponse createFailResponse(HttpStatus httpStatus, + String message) { + return new ApiCommonResponse<>(httpStatus.value(), message); + } + + public static ApiCommonResponse createSuccessResponse() { + return new ApiCommonResponse<>(HttpStatus.OK.value()); + } + + public static ApiCommonResponse createSuccessResponse(T data) { + return new ApiCommonResponse<>(HttpStatus.OK.value(), "Success", data); + } +} diff --git a/logbat_meta/src/main/java/info/logbat_meta/domain/project/application/AppService.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/application/AppService.java new file mode 100644 index 0000000..93ccb75 --- /dev/null +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/application/AppService.java @@ -0,0 +1,58 @@ +package info.logbat_meta.domain.project.application; + +import info.logbat_meta.domain.project.domain.App; +import info.logbat_meta.domain.project.domain.Project; +import info.logbat_meta.domain.project.domain.enums.AppType; +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 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 = "앱을 찾을 수 없습니다."; + + private final AppJpaRepository appRepository; + private final ProjectJpaRepository projectRepository; + + public AppCommonResponse createApp(Long projectId, String appTypeStr) { + Project project = getProject(projectId); + AppType appType = AppType.from(appTypeStr); + return AppCommonResponse.from(appRepository.save(App.of(project, appType))); + } + + @Transactional(readOnly = true) + public AppCommonResponse getAppById(Long id) { + App app = appRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException(APP_NOT_FOUND_MESSAGE)); + return AppCommonResponse.from(app); + } + + @Transactional(readOnly = true) + public List getAppsByProjectId(Long projectId) { + List apps = appRepository.findByProject_Id(projectId); + return apps.stream().map(AppCommonResponse::from).toList(); + } + + public Long 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(); + } + + private Project getProject(Long id) { + return projectRepository.findById(id) + .orElseThrow(() -> new IllegalArgumentException("프로젝트를 찾을 수 없습니다.")); + } +} diff --git a/logbat/src/main/java/info/logbat/domain/project/application/ProjectService.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/application/ProjectService.java similarity index 74% rename from logbat/src/main/java/info/logbat/domain/project/application/ProjectService.java rename to logbat_meta/src/main/java/info/logbat_meta/domain/project/application/ProjectService.java index d4597cf..db98231 100644 --- a/logbat/src/main/java/info/logbat/domain/project/application/ProjectService.java +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/application/ProjectService.java @@ -1,8 +1,8 @@ -package info.logbat.domain.project.application; +package info.logbat_meta.domain.project.application; -import info.logbat.domain.project.domain.Project; -import info.logbat.domain.project.presentation.payload.response.ProjectCommonResponse; -import info.logbat.domain.project.repository.ProjectJpaRepository; +import info.logbat_meta.domain.project.domain.Project; +import info.logbat_meta.domain.project.presentation.payload.response.ProjectCommonResponse; +import info.logbat_meta.domain.project.repository.ProjectJpaRepository; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -26,6 +26,12 @@ public ProjectCommonResponse getProjectByName(String name) { return ProjectCommonResponse.from(project); } + @Transactional(readOnly = true) + public ProjectCommonResponse getProjectById(Long id) { + Project project = getProject(id); + return ProjectCommonResponse.from(project); + } + public ProjectCommonResponse updateProjectValues(Long id, String name) { Project project = getProject(id); project.updateName(name); diff --git a/logbat/src/main/java/info/logbat/domain/project/domain/App.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/App.java similarity index 72% rename from logbat/src/main/java/info/logbat/domain/project/domain/App.java rename to logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/App.java index f5203a8..a4a4b1e 100644 --- a/logbat/src/main/java/info/logbat/domain/project/domain/App.java +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/App.java @@ -1,26 +1,17 @@ -package info.logbat.domain.project.domain; - -import info.logbat.domain.project.domain.enums.AppType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Index; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import java.time.LocalDateTime; -import java.util.Objects; -import java.util.UUID; +package info.logbat_meta.domain.project.domain; + +import info.logbat_meta.domain.project.domain.enums.AppType; +import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; import org.hibernate.annotations.CreationTimestamp; import org.hibernate.annotations.SoftDelete; +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.UUID; + @Entity @Getter @Table(name = "apps", indexes = { diff --git a/logbat/src/main/java/info/logbat/domain/project/domain/Project.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/Project.java similarity index 82% rename from logbat/src/main/java/info/logbat/domain/project/domain/Project.java rename to logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/Project.java index 904d38e..7e0f46a 100644 --- a/logbat/src/main/java/info/logbat/domain/project/domain/Project.java +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/Project.java @@ -1,14 +1,6 @@ -package info.logbat.domain.project.domain; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.Index; -import jakarta.persistence.Table; -import java.nio.charset.StandardCharsets; -import java.time.LocalDateTime; +package info.logbat_meta.domain.project.domain; + +import jakarta.persistence.*; import lombok.AccessLevel; import lombok.Getter; import lombok.NoArgsConstructor; @@ -16,6 +8,9 @@ import org.springframework.data.annotation.CreatedDate; import org.springframework.data.annotation.LastModifiedDate; +import java.nio.charset.StandardCharsets; +import java.time.LocalDateTime; + @Entity @Getter @SoftDelete diff --git a/logbat/src/main/java/info/logbat/domain/project/domain/enums/AppType.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/enums/AppType.java similarity index 86% rename from logbat/src/main/java/info/logbat/domain/project/domain/enums/AppType.java rename to logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/enums/AppType.java index 0351807..09c3794 100644 --- a/logbat/src/main/java/info/logbat/domain/project/domain/enums/AppType.java +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/domain/enums/AppType.java @@ -1,4 +1,4 @@ -package info.logbat.domain.project.domain.enums; +package info.logbat_meta.domain.project.domain.enums; public enum AppType { JS, JAVA; diff --git a/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/AppController.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/AppController.java new file mode 100644 index 0000000..feeff9e --- /dev/null +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/AppController.java @@ -0,0 +1,52 @@ +package info.logbat_meta.domain.project.presentation; + +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 lombok.RequiredArgsConstructor; +import org.springframework.web.bind.annotation.*; + +import java.util.List; + +@RestController +@RequestMapping("/v1/projects/{projectId}/apps") +@RequiredArgsConstructor +public class AppController { + + private final AppService appService; + + @GetMapping + public ApiCommonResponse> getAppsByProjectId(@PathVariable Long projectId) { + List apps = appService.getAppsByProjectId(projectId); + + return ApiCommonResponse.createSuccessResponse(apps); + } + + @GetMapping("/{appId}") + public ApiCommonResponse getAppById(@PathVariable Long appId) { + AppCommonResponse app = appService.getAppById(appId); + + return ApiCommonResponse.createSuccessResponse(app); + } + + @PostMapping + public ApiCommonResponse createApp( + @PathVariable Long projectId, + @RequestBody AppCreateRequest appCreateRequest) { + AppCommonResponse app = appService.createApp(projectId, appCreateRequest.appType()); + + return ApiCommonResponse.createSuccessResponse(app); + } + + @DeleteMapping("/{appId}") + public ApiCommonResponse deleteApp( + @PathVariable Long projectId, + @PathVariable Long appId + ) { + Long deletedId = appService.deleteApp(projectId, appId); + + return ApiCommonResponse.createSuccessResponse(deletedId); + } + +} diff --git a/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/ProjectController.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/ProjectController.java new file mode 100644 index 0000000..698411e --- /dev/null +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/ProjectController.java @@ -0,0 +1,50 @@ +package info.logbat_meta.domain.project.presentation; + +import info.logbat_meta.common.payload.ApiCommonResponse; +import info.logbat_meta.domain.project.application.ProjectService; +import info.logbat_meta.domain.project.presentation.payload.request.ProjectCreateRequest; +import info.logbat_meta.domain.project.presentation.payload.request.ProjectUpdateRequest; +import info.logbat_meta.domain.project.presentation.payload.response.ProjectCommonResponse; +import lombok.RequiredArgsConstructor; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; + +@RestController +@RequestMapping("/v1/projects") +@RequiredArgsConstructor +public class ProjectController { + + private final ProjectService projectService; + + @GetMapping("/{projectId}") + public ApiCommonResponse get(@PathVariable Long projectId) { + ProjectCommonResponse project = projectService.getProjectById(projectId); + + return ApiCommonResponse.createSuccessResponse(project); + } + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public ApiCommonResponse create(@RequestBody ProjectCreateRequest request) { + ProjectCommonResponse project = projectService.createProject(request.name()); + + return ApiCommonResponse.createApiResponse(HttpStatus.CREATED, "Success", project); + } + + @PutMapping("/{projectId}") + public ApiCommonResponse update( + @PathVariable Long projectId, + @RequestBody ProjectUpdateRequest request + ) { + ProjectCommonResponse data = projectService.updateProjectValues(projectId, request.name()); + + return ApiCommonResponse.createSuccessResponse(data); + } + + @DeleteMapping("/{projectId}") + public ApiCommonResponse delete(@PathVariable Long projectId) { + Long deletedId = projectService.deleteProject(projectId); + return ApiCommonResponse.createSuccessResponse(deletedId); + } + +} diff --git a/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/AppCreateRequest.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/AppCreateRequest.java new file mode 100644 index 0000000..f255e8c --- /dev/null +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/AppCreateRequest.java @@ -0,0 +1,5 @@ +package info.logbat_meta.domain.project.presentation.payload.request; + +public record AppCreateRequest(String appType) { + +} diff --git a/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/ProjectCreateRequest.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/ProjectCreateRequest.java new file mode 100644 index 0000000..733d9d8 --- /dev/null +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/ProjectCreateRequest.java @@ -0,0 +1,5 @@ +package info.logbat_meta.domain.project.presentation.payload.request; + +public record ProjectCreateRequest(String name) { + +} diff --git a/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/ProjectUpdateRequest.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/ProjectUpdateRequest.java new file mode 100644 index 0000000..48f5137 --- /dev/null +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/request/ProjectUpdateRequest.java @@ -0,0 +1,5 @@ +package info.logbat_meta.domain.project.presentation.payload.request; + +public record ProjectUpdateRequest(String name) { + +} diff --git a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/response/AppCommonResponse.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/response/AppCommonResponse.java similarity index 76% rename from logbat/src/main/java/info/logbat/domain/project/presentation/payload/response/AppCommonResponse.java rename to logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/response/AppCommonResponse.java index 822fc17..a61892d 100644 --- a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/response/AppCommonResponse.java +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/response/AppCommonResponse.java @@ -1,6 +1,7 @@ -package info.logbat.domain.project.presentation.payload.response; +package info.logbat_meta.domain.project.presentation.payload.response; + +import info.logbat_meta.domain.project.domain.App; -import info.logbat.domain.project.domain.App; import java.time.LocalDateTime; public record AppCommonResponse(Long id, Long projectId, String appType, String token, diff --git a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/response/ProjectCommonResponse.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/response/ProjectCommonResponse.java similarity index 75% rename from logbat/src/main/java/info/logbat/domain/project/presentation/payload/response/ProjectCommonResponse.java rename to logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/response/ProjectCommonResponse.java index 639b814..8e661b1 100644 --- a/logbat/src/main/java/info/logbat/domain/project/presentation/payload/response/ProjectCommonResponse.java +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/presentation/payload/response/ProjectCommonResponse.java @@ -1,6 +1,7 @@ -package info.logbat.domain.project.presentation.payload.response; +package info.logbat_meta.domain.project.presentation.payload.response; + +import info.logbat_meta.domain.project.domain.Project; -import info.logbat.domain.project.domain.Project; import java.time.LocalDateTime; public record ProjectCommonResponse(Long id, String name, LocalDateTime createdAt, diff --git a/logbat/src/main/java/info/logbat/domain/project/repository/AppJpaRepository.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/repository/AppJpaRepository.java similarity index 82% rename from logbat/src/main/java/info/logbat/domain/project/repository/AppJpaRepository.java rename to logbat_meta/src/main/java/info/logbat_meta/domain/project/repository/AppJpaRepository.java index dc7e68c..73b6259 100644 --- a/logbat/src/main/java/info/logbat/domain/project/repository/AppJpaRepository.java +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/repository/AppJpaRepository.java @@ -1,13 +1,14 @@ -package info.logbat.domain.project.repository; +package info.logbat_meta.domain.project.repository; -import info.logbat.domain.project.domain.App; -import java.util.List; -import java.util.Optional; -import java.util.UUID; +import info.logbat_meta.domain.project.domain.App; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.lang.NonNull; import org.springframework.stereotype.Repository; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + @Repository public interface AppJpaRepository extends JpaRepository { diff --git a/logbat/src/main/java/info/logbat/domain/project/repository/ProjectJpaRepository.java b/logbat_meta/src/main/java/info/logbat_meta/domain/project/repository/ProjectJpaRepository.java similarity index 75% rename from logbat/src/main/java/info/logbat/domain/project/repository/ProjectJpaRepository.java rename to logbat_meta/src/main/java/info/logbat_meta/domain/project/repository/ProjectJpaRepository.java index cba27cf..4d391a0 100644 --- a/logbat/src/main/java/info/logbat/domain/project/repository/ProjectJpaRepository.java +++ b/logbat_meta/src/main/java/info/logbat_meta/domain/project/repository/ProjectJpaRepository.java @@ -1,11 +1,12 @@ -package info.logbat.domain.project.repository; +package info.logbat_meta.domain.project.repository; -import info.logbat.domain.project.domain.Project; -import java.util.Optional; +import info.logbat_meta.domain.project.domain.Project; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.lang.NonNull; import org.springframework.stereotype.Repository; +import java.util.Optional; + @Repository public interface ProjectJpaRepository extends JpaRepository { diff --git a/logbat_meta/src/main/resources/application.yml b/logbat_meta/src/main/resources/application.yml new file mode 100644 index 0000000..8e0c16b --- /dev/null +++ b/logbat_meta/src/main/resources/application.yml @@ -0,0 +1,42 @@ +spring: + application: + name: logbat_meta + + profiles: + active: local + +jdbc: + async: + timeout: 2000 + bulk-size: 100 +--- +spring: + config: + activate: + on-profile: test + + datasource: + url: jdbc:mysql://localhost:3306/logbat + username: root + password: + driver-class-name: com.mysql.cj.jdbc.Driver + + jpa: + hibernate: + ddl-auto: create + +--- +spring: + config: + activate: + on-profile: local + + datasource: + url: jdbc:mysql://localhost:3306/logbat + username: root + password: + driver-class-name: com.mysql.cj.jdbc.Driver + + jpa: + hibernate: + ddl-auto: none diff --git a/logbat_meta/src/test/java/info/logbat_meta/LogbatMetaApplicationTests.java b/logbat_meta/src/test/java/info/logbat_meta/LogbatMetaApplicationTests.java new file mode 100644 index 0000000..f68dd33 --- /dev/null +++ b/logbat_meta/src/test/java/info/logbat_meta/LogbatMetaApplicationTests.java @@ -0,0 +1,13 @@ +package info.logbat_meta; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class LogbatMetaApplicationTests { + + @Test + void contextLoads() { + } + +} diff --git a/logbat_meta/src/test/java/info/logbat_meta/domain/common/ControllerTestSupport.java b/logbat_meta/src/test/java/info/logbat_meta/domain/common/ControllerTestSupport.java new file mode 100644 index 0000000..7159f6d --- /dev/null +++ b/logbat_meta/src/test/java/info/logbat_meta/domain/common/ControllerTestSupport.java @@ -0,0 +1,37 @@ +package info.logbat_meta.domain.common; + +import com.fasterxml.jackson.databind.ObjectMapper; +import info.logbat_meta.domain.project.application.AppService; +import info.logbat_meta.domain.project.application.ProjectService; +import info.logbat_meta.domain.project.presentation.AppController; +import info.logbat_meta.domain.project.presentation.ProjectController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.context.ActiveProfiles; +import org.springframework.test.web.servlet.MockMvc; + +/* + * 이후 테스트 하려면 Controller에 대해 controllers에 추가하고, ControllerTestSupport를 상속받아 테스트를 진행하시면 됩니다. + */ +@WebMvcTest(controllers = {ProjectController.class, AppController.class}) +@ActiveProfiles("test") +public abstract class ControllerTestSupport { + + @Autowired + protected MockMvc mockMvc; + + @Autowired + protected ObjectMapper objectMapper; + + @MockBean + protected ProjectService projectService; + + @MockBean + protected AppService appService; + + /* + * 이후 필요한 서비스에 대해 MockBean을 추가하여 테스트를 진행하시면 됩니다. + */ + +} \ No newline at end of file diff --git a/logbat/src/test/java/info/logbat/domain/project/application/AppServiceTest.java b/logbat_meta/src/test/java/info/logbat_meta/domain/project/application/AppServiceTest.java similarity index 79% rename from logbat/src/test/java/info/logbat/domain/project/application/AppServiceTest.java rename to logbat_meta/src/test/java/info/logbat_meta/domain/project/application/AppServiceTest.java index babdd44..f39414f 100644 --- a/logbat/src/test/java/info/logbat/domain/project/application/AppServiceTest.java +++ b/logbat_meta/src/test/java/info/logbat_meta/domain/project/application/AppServiceTest.java @@ -1,4 +1,22 @@ -package info.logbat.domain.project.application; +package info.logbat_meta.domain.project.application; + +import info.logbat_meta.domain.project.domain.App; +import info.logbat_meta.domain.project.domain.Project; +import info.logbat_meta.domain.project.domain.enums.AppType; +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 org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -8,24 +26,6 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.spy; -import info.logbat.domain.project.domain.App; -import info.logbat.domain.project.domain.Project; -import info.logbat.domain.project.domain.enums.AppType; -import info.logbat.domain.project.presentation.payload.response.AppCommonResponse; -import info.logbat.domain.project.repository.AppJpaRepository; -import info.logbat.domain.project.repository.ProjectJpaRepository; -import java.time.LocalDateTime; -import java.util.List; -import java.util.Optional; -import java.util.UUID; -import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; - @ExtendWith(MockitoExtension.class) @DisplayName("AppService는") class AppServiceTest { @@ -40,6 +40,7 @@ class AppServiceTest { private final Project expectedProject = mock(Project.class); private final App expectedApp = spy(App.of(expectedProject, AppType.JAVA)); + private final Long expectedProjectId = 1L; private final Long expectedAppId = 1L; @@ -104,25 +105,8 @@ void willCreateNewApp() { @Nested @DisplayName("App을 조회할 때") class whenGetApp { - - private final UUID expectedToken = UUID.randomUUID(); private final App expectedApp = spy(App.of(expectedProject, AppType.JAVA)); - @Test - @DisplayName("토큰으로 조회할 수 있다.") - void canGetAppByToken() { - // Arrange - String expectedTokenString = expectedToken.toString(); - given(appRepository.findByAppKey(expectedToken)).willReturn(Optional.of(expectedApp)); - given(expectedApp.getAppKey()).willReturn(expectedToken); - // Act - AppCommonResponse actualResult = appService.getAppByToken(expectedTokenString); - // Assert - assertThat(actualResult) - .extracting("token") - .isEqualTo(expectedTokenString); - } - @Test @DisplayName("ID로 조회할 수 있다.") void canGetAppById() { @@ -160,19 +144,6 @@ void willThrowExceptionWhenAppNotFoundById() { .isInstanceOf(IllegalArgumentException.class) .hasMessage("앱을 찾을 수 없습니다."); } - - @Test - @DisplayName("토큰 조회시 앱을 찾을 수 없으면 예외를 던진다.") - void willThrowExceptionWhenAppNotFoundByToken() { - // Arrange - String notExistToken = UUID.randomUUID().toString(); - // Arrange - given(appRepository.findByAppKey(any(UUID.class))).willReturn(Optional.empty()); - // Act & Assert - assertThatThrownBy(() -> appService.getAppByToken(notExistToken)) - .isInstanceOf(IllegalArgumentException.class) - .hasMessage("앱을 찾을 수 없습니다."); - } } @Nested diff --git a/logbat/src/test/java/info/logbat/domain/project/application/ProjectServiceTest.java b/logbat_meta/src/test/java/info/logbat_meta/domain/project/application/ProjectServiceTest.java similarity index 96% rename from logbat/src/test/java/info/logbat/domain/project/application/ProjectServiceTest.java rename to logbat_meta/src/test/java/info/logbat_meta/domain/project/application/ProjectServiceTest.java index 0ad2387..9e56ed8 100644 --- a/logbat/src/test/java/info/logbat/domain/project/application/ProjectServiceTest.java +++ b/logbat_meta/src/test/java/info/logbat_meta/domain/project/application/ProjectServiceTest.java @@ -1,17 +1,8 @@ -package info.logbat.domain.project.application; +package info.logbat_meta.domain.project.application; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.spy; - -import info.logbat.domain.project.domain.Project; -import info.logbat.domain.project.presentation.payload.response.ProjectCommonResponse; -import info.logbat.domain.project.repository.ProjectJpaRepository; -import java.time.LocalDateTime; -import java.util.Optional; +import info.logbat_meta.domain.project.domain.Project; +import info.logbat_meta.domain.project.presentation.payload.response.ProjectCommonResponse; +import info.logbat_meta.domain.project.repository.ProjectJpaRepository; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -20,6 +11,16 @@ import org.mockito.Mock; import org.mockito.junit.jupiter.MockitoExtension; +import java.time.LocalDateTime; +import java.util.Optional; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.spy; + @ExtendWith(MockitoExtension.class) @DisplayName("ProjectService는") class ProjectServiceTest { diff --git a/logbat/src/test/java/info/logbat/domain/project/domain/AppTest.java b/logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/AppTest.java similarity index 94% rename from logbat/src/test/java/info/logbat/domain/project/domain/AppTest.java rename to logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/AppTest.java index cb5b8eb..5e230cd 100644 --- a/logbat/src/test/java/info/logbat/domain/project/domain/AppTest.java +++ b/logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/AppTest.java @@ -1,11 +1,6 @@ -package info.logbat.domain.project.domain; +package info.logbat_meta.domain.project.domain; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; -import static org.junit.jupiter.api.Assertions.assertAll; - -import info.logbat.domain.project.domain.enums.AppType; -import java.util.stream.Stream; +import info.logbat_meta.domain.project.domain.enums.AppType; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -13,6 +8,12 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.junit.jupiter.api.Assertions.assertAll; + @DisplayName("App 도메인은") class AppTest { diff --git a/logbat/src/test/java/info/logbat/domain/project/domain/ProjectTest.java b/logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/ProjectTest.java similarity index 98% rename from logbat/src/test/java/info/logbat/domain/project/domain/ProjectTest.java rename to logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/ProjectTest.java index 88380e3..2a06863 100644 --- a/logbat/src/test/java/info/logbat/domain/project/domain/ProjectTest.java +++ b/logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/ProjectTest.java @@ -1,9 +1,5 @@ -package info.logbat.domain.project.domain; +package info.logbat_meta.domain.project.domain; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -11,6 +7,11 @@ import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + @DisplayName("Project 도메인은") class ProjectTest { diff --git a/logbat/src/test/java/info/logbat/domain/project/domain/enums/AppTypeTest.java b/logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/enums/AppTypeTest.java similarity index 96% rename from logbat/src/test/java/info/logbat/domain/project/domain/enums/AppTypeTest.java rename to logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/enums/AppTypeTest.java index 6a47e6e..5dae990 100644 --- a/logbat/src/test/java/info/logbat/domain/project/domain/enums/AppTypeTest.java +++ b/logbat_meta/src/test/java/info/logbat_meta/domain/project/domain/enums/AppTypeTest.java @@ -1,14 +1,15 @@ -package info.logbat.domain.project.domain.enums; +package info.logbat_meta.domain.project.domain.enums; -import static org.assertj.core.api.Assertions.assertThat; -import static org.assertj.core.api.Assertions.assertThatThrownBy; - -import java.util.stream.Stream; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.Arguments; import org.junit.jupiter.params.provider.MethodSource; +import java.util.stream.Stream; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + @DisplayName("AppType Enum은") class AppTypeTest { diff --git a/logbat/src/test/java/info/logbat/domain/project/presentation/AppControllerTest.java b/logbat_meta/src/test/java/info/logbat_meta/domain/project/presentation/AppControllerTest.java similarity index 86% rename from logbat/src/test/java/info/logbat/domain/project/presentation/AppControllerTest.java rename to logbat_meta/src/test/java/info/logbat_meta/domain/project/presentation/AppControllerTest.java index 3147d81..7769cfe 100644 --- a/logbat/src/test/java/info/logbat/domain/project/presentation/AppControllerTest.java +++ b/logbat_meta/src/test/java/info/logbat_meta/domain/project/presentation/AppControllerTest.java @@ -1,19 +1,8 @@ -package info.logbat.domain.project.presentation; +package info.logbat_meta.domain.project.presentation; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import info.logbat.domain.common.ControllerTestSupport; -import info.logbat.domain.project.domain.enums.AppType; -import info.logbat.domain.project.presentation.payload.response.AppCommonResponse; -import java.time.LocalDateTime; -import java.util.List; -import java.util.UUID; +import info.logbat_meta.domain.common.ControllerTestSupport; +import info.logbat_meta.domain.project.domain.enums.AppType; +import info.logbat_meta.domain.project.presentation.payload.response.AppCommonResponse; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -22,6 +11,16 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; + +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + @ExtendWith(MockitoExtension.class) @DisplayName("AppController는") class AppControllerTest extends ControllerTestSupport { @@ -47,12 +46,12 @@ void init() { class describeGetApps { @Test - @DisplayName("/v1/projects/apps/{projectId} 요청시 프로젝트 ID로 앱 목록을 조회할 수 있다.") + @DisplayName("/v1/projects/{projectId}/apps 요청시 프로젝트 ID로 앱 목록을 조회할 수 있다.") void willReturnAppList() throws Exception { // Arrange given(appService.getAppsByProjectId(expectedProjectId)).willReturn( List.of(expectedAppCommonResponse)); - MockHttpServletRequestBuilder get = get("/v1/projects/apps/{projectId}", + MockHttpServletRequestBuilder get = get("/v1/projects/{projectId}/apps", expectedProjectId); // Act & Assert mockMvc.perform(get) @@ -67,11 +66,11 @@ void willReturnAppList() throws Exception { } @Test - @DisplayName("/v1/projects/apps/info/{id} 요청시 앱 ID로 앱 정보를 조회할 수 있다.") + @DisplayName("/v1/projects/{projectId}/apps/{id} 요청시 앱 ID로 앱 정보를 조회할 수 있다.") void willReturnAppInformation() throws Exception { // Arrange given(appService.getAppById(expectedId)).willReturn(expectedAppCommonResponse); - MockHttpServletRequestBuilder get = get("/v1/projects/apps/info/{id}", expectedId); + MockHttpServletRequestBuilder get = get("/v1/projects/{projectId}/apps/{id}", expectedProjectId,expectedId); // Act & Assert mockMvc.perform(get) .andExpect(status().isOk()) @@ -91,16 +90,15 @@ void willReturnAppInformation() throws Exception { class describePostApp { @Test - @DisplayName("/v1/projects/apps 요청시 앱을 생성할 수 있다.") + @DisplayName("/v1/projects/{projectId}/apps 요청시 앱을 생성할 수 있다.") void willCreateApp() throws Exception { // Arrange given(appService.createApp(expectedProjectId, expectedAppType.name())) .willReturn(expectedAppCommonResponse); - MockHttpServletRequestBuilder post = post("/v1/projects/apps") + MockHttpServletRequestBuilder post = post("/v1/projects/{projectId}/apps", expectedProjectId) .contentType("application/json") .content(""" { - "projectId": 1, "appType": "JAVA" } """); @@ -123,11 +121,11 @@ void willCreateApp() throws Exception { class describeDeleteApp { @Test - @DisplayName("/v1/projects/apps/{projectId}/{appId} 요청시 프로젝트 ID와 앱 ID로 앱을 삭제할 수 있다.") + @DisplayName("/v1/projects/{projectId}/apps/{appId} 요청시 프로젝트 ID와 앱 ID로 앱을 삭제할 수 있다.") void willDeleteApp() throws Exception { // Arrange given(appService.deleteApp(expectedProjectId, expectedId)).willReturn(expectedId); - MockHttpServletRequestBuilder delete = delete("/v1/projects/apps/{projectId}/{appId}", + MockHttpServletRequestBuilder delete = delete("/v1/projects/{projectId}/apps/{appId}", expectedProjectId, expectedId); // Act & Assert mockMvc.perform(delete) diff --git a/logbat/src/test/java/info/logbat/domain/project/presentation/ProjectControllerTest.java b/logbat_meta/src/test/java/info/logbat_meta/domain/project/presentation/ProjectControllerTest.java similarity index 88% rename from logbat/src/test/java/info/logbat/domain/project/presentation/ProjectControllerTest.java rename to logbat_meta/src/test/java/info/logbat_meta/domain/project/presentation/ProjectControllerTest.java index 0c5fb98..4895dfa 100644 --- a/logbat/src/test/java/info/logbat/domain/project/presentation/ProjectControllerTest.java +++ b/logbat_meta/src/test/java/info/logbat_meta/domain/project/presentation/ProjectControllerTest.java @@ -1,18 +1,7 @@ -package info.logbat.domain.project.presentation; +package info.logbat_meta.domain.project.presentation; -import static org.mockito.ArgumentMatchers.any; -import static org.mockito.BDDMockito.given; -import static org.mockito.Mockito.mock; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.delete; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post; -import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.put; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; - -import info.logbat.domain.common.ControllerTestSupport; -import info.logbat.domain.project.presentation.payload.response.ProjectCommonResponse; -import java.time.LocalDateTime; +import info.logbat_meta.domain.common.ControllerTestSupport; +import info.logbat_meta.domain.project.presentation.payload.response.ProjectCommonResponse; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; @@ -20,6 +9,15 @@ import org.mockito.junit.jupiter.MockitoExtension; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; +import java.time.LocalDateTime; + +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; +import static org.mockito.Mockito.mock; +import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; + @ExtendWith(MockitoExtension.class) @DisplayName("ProjectController는") class ProjectControllerTest extends ControllerTestSupport { @@ -30,19 +28,19 @@ class ProjectControllerTest extends ControllerTestSupport { private final LocalDateTime expectedCreatedAt = LocalDateTime.of(2024, 8, 15, 12, 1, 2, 3); @Nested - @DisplayName("GET /v1/projects/{name}에 대해") + @DisplayName("GET /v1/projects/{id}에 대해") class describeGet { @Test - @DisplayName("프로젝트 이름으로 프로젝트를 조회할 수 있다.") + @DisplayName("프로젝트 아이디로 프로젝트를 조회할 수 있다.") void willReturnProjectInformation() throws Exception { // Arrange - given(projectService.getProjectByName("projectName")).willReturn(projectCommonResponse); + given(projectService.getProjectById(expectedId)).willReturn(projectCommonResponse); given(projectCommonResponse.id()).willReturn(expectedId); given(projectCommonResponse.name()).willReturn(expectedName); given(projectCommonResponse.createdAt()).willReturn(expectedCreatedAt); // Act - MockHttpServletRequestBuilder get = get("/v1/projects/{name}", "projectName"); + MockHttpServletRequestBuilder get = get("/v1/projects/{id}", expectedId); // Assert mockMvc.perform(get) .andExpect(status().isOk()) diff --git a/nginx/sites-available/default b/nginx/sites-available/default index 9531149..f4b03df 100644 --- a/nginx/sites-available/default +++ b/nginx/sites-available/default @@ -14,11 +14,14 @@ server { # managed by Certbot include /etc/nginx/snippets/managed-by-Certbot.conf; - root /var/www/html; - index index.html index.htm index.nginx-debian.html; + include /etc/nginx/snippets/private-ip.conf; location / { - try_files $uri $uri/ =404; + proxy_pass $logbat_server; + proxy_set_header Host $host; + proxy_set_header X-Real-IP $remote_addr; + proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; + proxy_set_header X-Forwarded-Proto $scheme; } } @@ -36,7 +39,7 @@ server { error_log /var/log/nginx/api_error.log; # api.logbat.info 전용 에러 로그 파일 location /logs { - proxy_pass $logbat_server; + proxy_pass $logbat_api_server; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; @@ -61,7 +64,7 @@ server { } location / { - proxy_pass $logbat_server; + proxy_pass $logbat_api_server; proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;