diff --git a/src/lombok.config b/src/lombok.config new file mode 100644 index 00000000..2c6905d7 --- /dev/null +++ b/src/lombok.config @@ -0,0 +1 @@ +lombok.copyableAnnotations += org.springframework.beans.factory.annotation.Value diff --git a/src/main/java/com/coniverse/dangjang/domain/intro/controller/IntroController.java b/src/main/java/com/coniverse/dangjang/domain/intro/controller/IntroController.java deleted file mode 100644 index ddb5185a..00000000 --- a/src/main/java/com/coniverse/dangjang/domain/intro/controller/IntroController.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.coniverse.dangjang.domain.intro.controller; - -import org.springframework.http.ResponseEntity; - -import com.coniverse.dangjang.domain.intro.dto.IntroResponse; -import com.coniverse.dangjang.global.dto.SuccessSingleResponse; - -/** - * client에서 앱을 처음 실행할 때, 앱의 버전을 확인하고, 최신 버전이 아니라면 업데이트를 유도하는 역할을 한다. - * - * @author TEO - * @since 1.0.0 - */ -public interface IntroController { - ResponseEntity>> getIntro(); -} diff --git a/src/main/java/com/coniverse/dangjang/domain/intro/controller/ProdIntroController.java b/src/main/java/com/coniverse/dangjang/domain/intro/controller/ProdIntroController.java deleted file mode 100644 index 7c6afa99..00000000 --- a/src/main/java/com/coniverse/dangjang/domain/intro/controller/ProdIntroController.java +++ /dev/null @@ -1,28 +0,0 @@ -package com.coniverse.dangjang.domain.intro.controller; - -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import com.coniverse.dangjang.domain.intro.dto.IntroResponse; -import com.coniverse.dangjang.domain.intro.service.IntroService; -import com.coniverse.dangjang.global.dto.SuccessSingleResponse; - -import lombok.RequiredArgsConstructor; - -@RestController -@RequestMapping("/api/intro") -@RequiredArgsConstructor -public class ProdIntroController implements IntroController { - private final IntroService introService; - - // TODO - @Override - @GetMapping("/prod") - public ResponseEntity>> getIntro() { - IntroResponse introResponse = introService.getProdIntroResponse(); - return ResponseEntity.ok().body(new SuccessSingleResponse<>(HttpStatus.OK.getReasonPhrase(), introResponse)); - } -} diff --git a/src/main/java/com/coniverse/dangjang/domain/intro/controller/TestIntroController.java b/src/main/java/com/coniverse/dangjang/domain/intro/controller/TestIntroController.java deleted file mode 100644 index 0dcb48bc..00000000 --- a/src/main/java/com/coniverse/dangjang/domain/intro/controller/TestIntroController.java +++ /dev/null @@ -1,29 +0,0 @@ -package com.coniverse.dangjang.domain.intro.controller; - -import org.springframework.context.annotation.Profile; -import org.springframework.http.HttpStatus; -import org.springframework.http.ResponseEntity; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import com.coniverse.dangjang.domain.intro.dto.IntroResponse; -import com.coniverse.dangjang.domain.intro.service.IntroService; -import com.coniverse.dangjang.global.dto.SuccessSingleResponse; - -import lombok.RequiredArgsConstructor; - -@RestController -@RequestMapping("/api/intro") -@RequiredArgsConstructor -@Profile({"dev", "local", "test"}) -public class TestIntroController implements IntroController { - private final IntroService introService; - - @Override - @GetMapping("/test") - public ResponseEntity>> getIntro() { - IntroResponse introResponse = introService.getTestIntroResponse(); - return ResponseEntity.ok().body(new SuccessSingleResponse<>(HttpStatus.OK.getReasonPhrase(), introResponse)); - } -} diff --git a/src/main/java/com/coniverse/dangjang/domain/intro/dto/IntroResponse.java b/src/main/java/com/coniverse/dangjang/domain/intro/dto/IntroResponse.java deleted file mode 100644 index 7e1afbd0..00000000 --- a/src/main/java/com/coniverse/dangjang/domain/intro/dto/IntroResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.coniverse.dangjang.domain.intro.dto; - -import java.util.List; - -import com.fasterxml.jackson.annotation.JsonInclude; - -/** - * splash screen time때 load할 버전 정보와 데이터를 담는 dto이다. - * - * @author TEO - * @since 1.0.0 - */ -public record IntroResponse(String minVersion, String latestVersion, @JsonInclude(JsonInclude.Include.NON_NULL) List loadData) { -} diff --git a/src/main/java/com/coniverse/dangjang/domain/intro/dto/Version.java b/src/main/java/com/coniverse/dangjang/domain/intro/dto/Version.java deleted file mode 100644 index 9e849e7c..00000000 --- a/src/main/java/com/coniverse/dangjang/domain/intro/dto/Version.java +++ /dev/null @@ -1,13 +0,0 @@ -package com.coniverse.dangjang.domain.intro.dto; - -import lombok.AllArgsConstructor; -import lombok.Getter; - -@AllArgsConstructor -public enum Version { - MINIMUM("1.0.7"), - LATEST("1.0.7"); - - @Getter - private final String version; -} diff --git a/src/main/java/com/coniverse/dangjang/domain/intro/service/IntroService.java b/src/main/java/com/coniverse/dangjang/domain/intro/service/IntroService.java deleted file mode 100644 index 0861db3a..00000000 --- a/src/main/java/com/coniverse/dangjang/domain/intro/service/IntroService.java +++ /dev/null @@ -1,48 +0,0 @@ -package com.coniverse.dangjang.domain.intro.service; - -import org.springframework.stereotype.Service; - -import com.coniverse.dangjang.domain.intro.dto.IntroResponse; -import com.coniverse.dangjang.domain.intro.dto.Version; -import com.coniverse.dangjang.global.exception.BadRequestException; - -import lombok.RequiredArgsConstructor; - -/** - * @author TEO - * @since 1.0.0 - */ -@Service -@RequiredArgsConstructor -public class IntroService { - private static final String MIN_VERSION = Version.MINIMUM.getVersion(); - private static final String LATEST_VERSION = Version.LATEST.getVersion(); - private int error = -1; - - /** - * response를 성공적으로 반환 또는 예외 발생을 번갈아가며 실행한다. - * - * @return IntroResponse - * @since 1.0.0 - */ - public IntroResponse getTestIntroResponse() { - IntroResponse introResponse = new IntroResponse<>(MIN_VERSION, LATEST_VERSION, null); - error += 1; - if (error % 2 == 0) { - return introResponse; - } - throw new BadRequestException(); - } - - /** - * TODO load data - * - * @return IntroResponse - * @since 1.0.0 - */ - public IntroResponse getProdIntroResponse() { - IntroResponse introResponse = new IntroResponse<>(MIN_VERSION, LATEST_VERSION, null); - - return introResponse; - } -} diff --git a/src/main/java/com/coniverse/dangjang/domain/version/controller/VersionController.java b/src/main/java/com/coniverse/dangjang/domain/version/controller/VersionController.java new file mode 100644 index 00000000..fb54a77f --- /dev/null +++ b/src/main/java/com/coniverse/dangjang/domain/version/controller/VersionController.java @@ -0,0 +1,52 @@ +package com.coniverse.dangjang.domain.version.controller; + +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +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; + +import com.coniverse.dangjang.domain.version.dto.request.VersionRequest; +import com.coniverse.dangjang.domain.version.dto.response.VersionResponse; +import com.coniverse.dangjang.domain.version.service.VersionService; +import com.coniverse.dangjang.global.dto.SuccessSingleResponse; + +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; + +/** + * 앱 버전 관련 controller + * + * @author TEO + * @since 1.3.0 + */ +@RestController +@RequestMapping("/api/version") +@RequiredArgsConstructor +public class VersionController { + private final VersionService versionService; + + /** + * 버전 정보를 GET 요청한다. + * + * @since 1.3.0 + */ + @GetMapping("/intro") + public ResponseEntity>> getIntro() { + VersionResponse versionResponse = versionService.getVersionResponse(); + return ResponseEntity.ok().body(new SuccessSingleResponse<>(HttpStatus.OK.getReasonPhrase(), versionResponse)); + } + + /** + * 버전 정보를 POST 요청한다. + * + * @since 1.3.0 + */ + @PostMapping + public ResponseEntity>> postVersion(@Valid @RequestBody VersionRequest request) { + VersionResponse versionResponse = versionService.saveVersion(request); + return ResponseEntity.ok().body(new SuccessSingleResponse<>(HttpStatus.OK.getReasonPhrase(), versionResponse)); + } +} diff --git a/src/main/java/com/coniverse/dangjang/domain/version/dto/request/VersionRequest.java b/src/main/java/com/coniverse/dangjang/domain/version/dto/request/VersionRequest.java new file mode 100644 index 00000000..9b711bb5 --- /dev/null +++ b/src/main/java/com/coniverse/dangjang/domain/version/dto/request/VersionRequest.java @@ -0,0 +1,14 @@ +package com.coniverse.dangjang.domain.version.dto.request; + +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.Pattern; + +/** + * 앱 버전 post dto + * + * @author TEO + * @since 1.3.0 + */ +public record VersionRequest(@NotBlank @Pattern(regexp = "^\\d\\.\\d\\.\\d$") String minVersion, + @NotBlank @Pattern(regexp = "^\\d\\.\\d\\.\\d$") String latestVersion, @NotBlank String key) { +} diff --git a/src/main/java/com/coniverse/dangjang/domain/version/dto/response/VersionResponse.java b/src/main/java/com/coniverse/dangjang/domain/version/dto/response/VersionResponse.java new file mode 100644 index 00000000..e2a60ce0 --- /dev/null +++ b/src/main/java/com/coniverse/dangjang/domain/version/dto/response/VersionResponse.java @@ -0,0 +1,18 @@ +package com.coniverse.dangjang.domain.version.dto.response; + +import java.util.List; + +import com.coniverse.dangjang.domain.version.entity.Version; +import com.fasterxml.jackson.annotation.JsonInclude; + +/** + * 버전 정보를 반환하는 dto이다. + * + * @author TEO + * @since 1.3.0 + */ +public record VersionResponse(String minVersion, String latestVersion, @JsonInclude(JsonInclude.Include.NON_NULL) List loadData) { + public VersionResponse(Version version, List loadData) { + this(version.getMinVersion(), version.getLatestVersion(), loadData); + } +} diff --git a/src/main/java/com/coniverse/dangjang/domain/version/entity/Version.java b/src/main/java/com/coniverse/dangjang/domain/version/entity/Version.java new file mode 100644 index 00000000..0adae6a0 --- /dev/null +++ b/src/main/java/com/coniverse/dangjang/domain/version/entity/Version.java @@ -0,0 +1,38 @@ +package com.coniverse.dangjang.domain.version.entity; + +import java.time.LocalDateTime; + +import org.springframework.data.annotation.CreatedDate; +import org.springframework.data.jpa.domain.support.AuditingEntityListener; + +import jakarta.persistence.Entity; +import jakarta.persistence.EntityListeners; +import jakarta.persistence.Id; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; + +/** + * app version entity + * + * @author TEO + * @since 1.3.0 + */ +@Entity +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@EntityListeners(AuditingEntityListener.class) +public class Version { + @Id + @CreatedDate + private LocalDateTime createdAt; + private String minVersion; + private String latestVersion; + + @Builder + private Version(String minVersion, String latestVersion) { + this.minVersion = minVersion; + this.latestVersion = latestVersion; + } +} diff --git a/src/main/java/com/coniverse/dangjang/domain/version/repository/VersionRepository.java b/src/main/java/com/coniverse/dangjang/domain/version/repository/VersionRepository.java new file mode 100644 index 00000000..577e97c8 --- /dev/null +++ b/src/main/java/com/coniverse/dangjang/domain/version/repository/VersionRepository.java @@ -0,0 +1,22 @@ +package com.coniverse.dangjang.domain.version.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.coniverse.dangjang.domain.version.entity.Version; + +/** + * app version repository + * + * @author TEO + * @since 1.3.0 + */ +public interface VersionRepository extends JpaRepository { + + /** + * 가장 최근의 버전 정보를 반환한다. + * + * @return Version + * @since 1.3.0 + */ + Version findFirstByOrderByCreatedAtDesc(); +} diff --git a/src/main/java/com/coniverse/dangjang/domain/version/service/VersionService.java b/src/main/java/com/coniverse/dangjang/domain/version/service/VersionService.java new file mode 100644 index 00000000..73ffdad8 --- /dev/null +++ b/src/main/java/com/coniverse/dangjang/domain/version/service/VersionService.java @@ -0,0 +1,69 @@ +package com.coniverse.dangjang.domain.version.service; + +import org.springframework.beans.factory.annotation.Value; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +import com.coniverse.dangjang.domain.version.dto.request.VersionRequest; +import com.coniverse.dangjang.domain.version.dto.response.VersionResponse; +import com.coniverse.dangjang.domain.version.entity.Version; +import com.coniverse.dangjang.domain.version.repository.VersionRepository; + +import lombok.RequiredArgsConstructor; + +/** + * 앱 버전 관련 service + * + * @author TEO + * @since 1.3.0 + */ +@Service +@RequiredArgsConstructor +@Transactional +public class VersionService { + private final VersionRepository versionRepository; + @Value("${version.key}") + private final String serverKey; + + /** + * 가장 최근의 버전 정보를 반환한다. + * + * @return VersionResponse + * @since 1.0.0 + */ + @Transactional(readOnly = true) + public VersionResponse getVersionResponse() { + Version version = versionRepository.findFirstByOrderByCreatedAtDesc(); + return new VersionResponse<>(version, null); + } + + /** + * 버전 정보를 저장한다. + * + * @param request 버전 정보 + * @return VersionResponse + * @since 1.3.0 + */ + public VersionResponse saveVersion(VersionRequest request) { + validateKey(request.key()); + Version version = Version.builder() + .minVersion(request.minVersion()) + .latestVersion(request.latestVersion()) + .build(); + versionRepository.save(version); + return new VersionResponse<>(version, null); + } + + /** + * 버전 정보의 키 값을 확인한다. + * + * @param key 버전 키 + * @throws IllegalArgumentException 서버 키가 아닐 경우 + * @since 1.3.0 + */ + private void validateKey(String key) { + if (!key.equals(serverKey)) { + throw new IllegalArgumentException("올바르지 않은 키 값입니다."); + } + } +} diff --git a/src/main/resources/config b/src/main/resources/config index 64b96d83..f027d62e 160000 --- a/src/main/resources/config +++ b/src/main/resources/config @@ -1 +1 @@ -Subproject commit 64b96d83da52275f908d1f7599812ca34dd16787 +Subproject commit f027d62e9388d1c8a0bb681a61c3ba1d0b76e2ae diff --git a/src/main/resources/data-dev.sql b/src/main/resources/data-dev.sql index 2ac9cccc..8fc7fe73 100644 --- a/src/main/resources/data-dev.sql +++ b/src/main/resources/data-dev.sql @@ -44,3 +44,6 @@ VALUES ("11111111", "fcmToken1", '2023-10-08'), -- ("체중 기록!", "오늘 체중을 기록해보세요 ~ ", '2023-10-08', "기록", "22222222", true), -- ("접속해주세요!", "오늘은 접속 안하셨네요 ~ 접속해서 포인트 받아가세요", '2023-10-09', "접속", "22222222", false), -- ("운동 기록 !", "오늘 운동을 기록해보세요 ~ ", '2023-10-10', "기록", "11111111", false); + +INSERT INTO version(created_at, min_version, latest_version) +VALUES (now(), "1.0.0", "1.0.0"); \ No newline at end of file diff --git a/src/main/resources/schema-dev.sql b/src/main/resources/schema-dev.sql index 50f2d073..6d5e2fd6 100644 --- a/src/main/resources/schema-dev.sql +++ b/src/main/resources/schema-dev.sql @@ -12,6 +12,7 @@ DROP TABLE IF EXISTS POINT_PRODUCT; DROP TABLE IF EXISTS USER_POINT; DROP TABLE IF EXISTS USERS; DROP TABLE IF EXISTS SHEDLOCK; +DROP TABLE IF EXISTS VERSION; CREATE TABLE `USERS` ( @@ -119,7 +120,7 @@ CREATE TABLE `PURCHASE_HISTORY` `CREATED_AT` dateTime NOT NULL, `PHONE` varchar(15) NOT NULL, `NAME` varchar(20) NOT NULL, - `COMMENT` varchar(255), + `COMMENT` varchar(255), `COMPLETED` boolean, PRIMARY KEY (`OAUTH_ID`, `PRODUCT_NAME`, `CREATED_AT`), FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) ON DELETE CASCADE, @@ -131,7 +132,7 @@ CREATE TABLE `USER_FCM_TOKEN` ( `OAUTH_ID` varchar(50) NOT NULL, `FCM_TOKEN` varchar(255) NOT NULL, - `CREATED_AT` dateTime NOT NULL, + `CREATED_AT` date NOT NULL, PRIMARY KEY (`FCM_TOKEN`), FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) ON DELETE CASCADE ); @@ -144,7 +145,7 @@ CREATE TABLE `NOTIFICATION_TYPE` CREATE TABLE `NOTIFICATION` ( - `NOTIFICATION_ID` INT NOT NULL AUTO_INCREMENT, + `NOTIFICATION_ID` bigint NOT NULL AUTO_INCREMENT, `TITLE` varchar(255) NOT NULL, `CONTENT` varchar(255) NOT NULL, `OAUTH_ID` varchar(50) NOT NULL, @@ -156,10 +157,19 @@ CREATE TABLE `NOTIFICATION` FOREIGN KEY (`TYPE`) REFERENCES NOTIFICATION_TYPE (`TYPE`) ); -CREATE TABLE `SHEDLOCK` ( - `NAME` VARCHAR(64), - `LOCK_UNTIL` TIMESTAMP(3) NULL, - `LOCKED_AT` TIMESTAMP(3) NULL, - `LOCKED_BY` VARCHAR(255), - PRIMARY KEY (NAME) -) +CREATE TABLE `SHEDLOCK` +( + `NAME` varchar(64) NOT NULL, + `LOCK_UNTIL` timestamp(3) NOT NULL, + `LOCKED_AT` timestamp(3) NOT NULL, + `LOCKED_BY` varchar(255) NOT NULL, + PRIMARY KEY (NAME) +); + +CREATE TABLE `VERSION` +( + `CREATED_AT` datetime NOT NULL, + `MIN_VERSION` varchar(10) NOT NULL, + `LATEST_VERSION` varchar(10) NOT NULL, + PRIMARY KEY (`CREATED_AT`) +); diff --git a/src/test/java/com/coniverse/dangjang/domain/intro/controller/IntroControllerTest.java b/src/test/java/com/coniverse/dangjang/domain/intro/controller/IntroControllerTest.java deleted file mode 100644 index c54b1586..00000000 --- a/src/test/java/com/coniverse/dangjang/domain/intro/controller/IntroControllerTest.java +++ /dev/null @@ -1,87 +0,0 @@ -package com.coniverse.dangjang.domain.intro.controller; - -import static com.coniverse.dangjang.support.SimpleMockMvc.*; -import static org.mockito.BDDMockito.*; -import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.test.web.servlet.ResultActions; - -import com.coniverse.dangjang.domain.intro.dto.IntroResponse; -import com.coniverse.dangjang.domain.intro.dto.Version; -import com.coniverse.dangjang.domain.intro.service.IntroService; -import com.coniverse.dangjang.global.exception.BadRequestException; -import com.coniverse.dangjang.support.ControllerTest; - -/** - * @author TEO - * @since 1.0.0 - */ -class IntroControllerTest extends ControllerTest { - private static final String URI = "/api/intro"; - private static final String MIN_VERSION = Version.MINIMUM.getVersion(); - private static final String LATEST_VERSION = Version.LATEST.getVersion(); - @Autowired - private IntroService introService; - - @Nested - class Test_Intro_URL에_접근시 { - @Test - void 성공한_응답을_반환한다() throws Exception { - // given - IntroResponse introResponse = new IntroResponse<>(MIN_VERSION, LATEST_VERSION, null); - given(introService.getTestIntroResponse()).willReturn(introResponse); - - // when - ResultActions resultActions = get(mockMvc, URI + "/test"); - - // then - resultActions.andExpectAll( - status().isOk(), - jsonPath("$.message").value(HttpStatus.OK.getReasonPhrase()), - jsonPath("$.data.minVersion").value(introResponse.minVersion()), - jsonPath("$.data.latestVersion").value(introResponse.latestVersion()) - ); - } - - @Test - void 실패한_응답을_반환한다() throws Exception { - // given - given(introService.getTestIntroResponse()).willThrow(new BadRequestException()); - - // when - ResultActions resultActions = get(mockMvc, URI + "/test"); - - // then - resultActions.andExpectAll( - status().isBadRequest(), - jsonPath("$.errorCode").value(400), - jsonPath("$.message").value("잘못된 요청입니다.") - ); - } - } - - @Nested - class Prod_Intro_URL에_접근시 { - @Test - void 성공한_응답을_반환한다() throws Exception { - // given - IntroResponse introResponse = new IntroResponse<>(MIN_VERSION, LATEST_VERSION, null); - given(introService.getProdIntroResponse()).willReturn(introResponse); - - // when - ResultActions resultActions = get(mockMvc, URI + "/prod"); - - // then - resultActions.andExpectAll( - status().isOk(), - jsonPath("$.message").value(HttpStatus.OK.getReasonPhrase()), - jsonPath("$.data.minVersion").value(introResponse.minVersion()), - jsonPath("$.data.latestVersion").value(introResponse.latestVersion()) - ); - } - } -} diff --git a/src/test/java/com/coniverse/dangjang/domain/intro/service/IntroServiceTest.java b/src/test/java/com/coniverse/dangjang/domain/intro/service/IntroServiceTest.java deleted file mode 100644 index fb8733f7..00000000 --- a/src/test/java/com/coniverse/dangjang/domain/intro/service/IntroServiceTest.java +++ /dev/null @@ -1,54 +0,0 @@ -package com.coniverse.dangjang.domain.intro.service; - -import static org.assertj.core.api.Assertions.*; - -import org.junit.jupiter.api.Nested; -import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.boot.test.context.SpringBootTest; - -import com.coniverse.dangjang.domain.intro.dto.IntroResponse; -import com.coniverse.dangjang.global.exception.BadRequestException; - -/** - * @author TEO - * @since 1.0.0 - */ -@SpringBootTest -class IntroServiceTest { - @Autowired - private IntroService introService; - - @Nested - class TestIntroResponse를 { - @Test - void 한_번_호출하면_IntroResponse가_반환된다() { - // when - IntroResponse introResponse = introService.getTestIntroResponse(); - - // then - assertThat(introResponse).isNotNull(); - } - - @Test - void 두_번_연속_호출하면_BadRequestException이_발생한다() { - // given - introService.getTestIntroResponse(); - - // when & then - assertThatThrownBy(() -> introService.getTestIntroResponse()).isInstanceOf(BadRequestException.class); - } - } - - @Nested - class ProdIntroResponse를 { - @Test - void 호출하면_IntroResponse가_반환된다() { - // when - IntroResponse introResponse = introService.getProdIntroResponse(); - - // then - assertThat(introResponse).isNotNull(); - } - } -} diff --git a/src/test/java/com/coniverse/dangjang/domain/version/controller/VersionControllerTest.java b/src/test/java/com/coniverse/dangjang/domain/version/controller/VersionControllerTest.java new file mode 100644 index 00000000..3aee4994 --- /dev/null +++ b/src/test/java/com/coniverse/dangjang/domain/version/controller/VersionControllerTest.java @@ -0,0 +1,124 @@ +package com.coniverse.dangjang.domain.version.controller; + +import static com.coniverse.dangjang.fixture.VersionFixture.*; +import static com.coniverse.dangjang.support.SimpleMockMvc.*; +import static org.mockito.BDDMockito.*; +import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.*; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.test.web.servlet.ResultActions; + +import com.coniverse.dangjang.domain.version.dto.request.VersionRequest; +import com.coniverse.dangjang.domain.version.dto.response.VersionResponse; +import com.coniverse.dangjang.domain.version.service.VersionService; +import com.coniverse.dangjang.support.ControllerTest; + +/** + * @author TEO + * @since 1.3.0 + */ +class VersionControllerTest extends ControllerTest { + private static final String URI = "/api/version"; + + @Autowired + private VersionService versionService; + + @Test + void 앱_버전을_GET_요청하면_성공한_응답을_반환한다() throws Exception { + // given + VersionResponse versionResponse = 버전_응답(); + doReturn(versionResponse).when(versionService).getVersionResponse(); + + // when + ResultActions resultActions = get(mockMvc, URI + "/intro"); + + // then + resultActions.andExpectAll( + status().isOk(), + jsonPath("$.message").value(HttpStatus.OK.getReasonPhrase()), + jsonPath("$.data.minVersion").value(versionResponse.minVersion()), + jsonPath("$.data.latestVersion").value(versionResponse.latestVersion()) + ); + } + + @Test + void 앱_버전을_POST_요청하면_성공한_응답을_반환한다() throws Exception { + // given + VersionRequest versionRequest = 버전_요청(); + String content = objectMapper.writeValueAsString(versionRequest); + VersionResponse versionResponse = 버전_응답(); + doReturn(versionResponse).when(versionService).saveVersion(any()); + + // when + ResultActions resultActions = post(mockMvc, URI, content); + + // then + resultActions.andExpectAll( + status().isOk(), + jsonPath("$.message").value(HttpStatus.OK.getReasonPhrase()), + jsonPath("$.data.minVersion").value(versionResponse.minVersion()), + jsonPath("$.data.latestVersion").value(versionResponse.latestVersion()) + ); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "1/1/1", "1.1.0 ", " 1.0.1"}) + void RequestBody의_minVersion이_잘못됐을_경우_실패한_응답을_반환한다(String minVersion) throws Exception { + // given + VersionRequest versionRequest = 버전_요청(minVersion, "1.0.1", "1234"); + String content = objectMapper.writeValueAsString(versionRequest); + + // when + ResultActions resultActions = post(mockMvc, URI, content); + + // then + resultActions.andExpectAll( + status().isBadRequest(), + jsonPath("$.errorCode").value(400), + jsonPath("$.fieldErrors[0].field").value("minVersion"), + jsonPath("$.fieldErrors[0].rejectedValue").value(minVersion) + ); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "1/1/1", "1.1.0 ", " 1.0.1"}) + void RequestBody의_latestVersion이_잘못됐을_경우_실패한_응답을_반환한다(String latestVersion) throws Exception { + // given + VersionRequest versionRequest = 버전_요청("1.0.1", latestVersion, "1234"); + String content = objectMapper.writeValueAsString(versionRequest); + + // when + ResultActions resultActions = post(mockMvc, URI, content); + + // then + resultActions.andExpectAll( + status().isBadRequest(), + jsonPath("$.errorCode").value(400), + jsonPath("$.fieldErrors[0].field").value("latestVersion"), + jsonPath("$.fieldErrors[0].rejectedValue").value(latestVersion) + ); + } + + @ParameterizedTest + @ValueSource(strings = {"", " ", "\n"}) + void RequestBody의_key가_잘못됐을_경우_실패한_응답을_반환한다(String key) throws Exception { + // given + VersionRequest versionRequest = 버전_요청("1.0.1", "1.0.1", key); + String content = objectMapper.writeValueAsString(versionRequest); + + // when + ResultActions resultActions = post(mockMvc, URI, content); + + // then + resultActions.andExpectAll( + status().isBadRequest(), + jsonPath("$.errorCode").value(400), + jsonPath("$.fieldErrors[0].field").value("key"), + jsonPath("$.fieldErrors[0].rejectedValue").value(key) + ); + } +} diff --git a/src/test/java/com/coniverse/dangjang/domain/version/repository/VersionRepositoryTest.java b/src/test/java/com/coniverse/dangjang/domain/version/repository/VersionRepositoryTest.java new file mode 100644 index 00000000..4b3cf24c --- /dev/null +++ b/src/test/java/com/coniverse/dangjang/domain/version/repository/VersionRepositoryTest.java @@ -0,0 +1,43 @@ +package com.coniverse.dangjang.domain.version.repository; + +import static com.coniverse.dangjang.fixture.VersionFixture.*; +import static org.assertj.core.api.Assertions.*; +import static org.junit.jupiter.api.Assertions.*; + +import java.util.List; +import java.util.stream.IntStream; + +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; + +import com.coniverse.dangjang.domain.version.entity.Version; +import com.coniverse.dangjang.support.annotation.JpaRepositoryTest; + +/** + * @author TEO + * @since 1.3.0 + */ +@JpaRepositoryTest +class VersionRepositoryTest { + @Autowired + private VersionRepository versionRepository; + + @Test + void 가장_최근의_버전_정보를_반환한다() { + // given + int count = 30; + List versions = IntStream.rangeClosed(1, count) + .mapToObj(i -> 버전_엔티티("1.0." + i, "1.0." + i)) + .toList(); + versionRepository.saveAll(versions); + + // when + Version version = versionRepository.findFirstByOrderByCreatedAtDesc(); + + // then + assertAll( + () -> assertThat(version.getMinVersion()).isEqualTo("1.0." + count), + () -> assertThat(version.getLatestVersion()).isEqualTo("1.0." + count) + ); + } +} diff --git a/src/test/java/com/coniverse/dangjang/domain/version/service/VersionServiceTest.java b/src/test/java/com/coniverse/dangjang/domain/version/service/VersionServiceTest.java new file mode 100644 index 00000000..940fb3f3 --- /dev/null +++ b/src/test/java/com/coniverse/dangjang/domain/version/service/VersionServiceTest.java @@ -0,0 +1,72 @@ +package com.coniverse.dangjang.domain.version.service; + +import static com.coniverse.dangjang.fixture.VersionFixture.*; +import static org.assertj.core.api.Assertions.*; +import static org.mockito.Mockito.*; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.Test; +import org.mockito.Mockito; + +import com.coniverse.dangjang.domain.version.dto.request.VersionRequest; +import com.coniverse.dangjang.domain.version.dto.response.VersionResponse; +import com.coniverse.dangjang.domain.version.entity.Version; +import com.coniverse.dangjang.domain.version.repository.VersionRepository; + +/** + * @author TEO + * @since 1.3.0 + */ +class VersionServiceTest { + private VersionService versionService; + private VersionRepository versionRepository; + private static final String KEY = "1234"; + private static final String MIN_VERSION = "1.3.1"; + private static final String LATEST_VERSION = "1.3.1"; + + @BeforeEach + void setUp() { + versionRepository = Mockito.mock(VersionRepository.class); + versionService = new VersionService(versionRepository, KEY); + } + + @Test + void 버전_응답을_반환한다() { + // given + Version version = 버전_엔티티(MIN_VERSION, LATEST_VERSION); + doReturn(version).when(versionRepository).findFirstByOrderByCreatedAtDesc(); + + // when + VersionResponse versionResponse = versionService.getVersionResponse(); + + // then + assertThat(versionResponse.minVersion()).isEqualTo(MIN_VERSION); + assertThat(versionResponse.latestVersion()).isEqualTo(LATEST_VERSION); + } + + @Test + void 키가_같으면_버전을_성공적으로_저장한다() { + // given + doReturn(null) + .when(versionRepository) + .save(any()); + VersionRequest versionRequest = 버전_요청(MIN_VERSION, LATEST_VERSION, KEY); + + // when + VersionResponse versionResponse = versionService.saveVersion(versionRequest); + + // then + assertThat(versionResponse.minVersion()).isEqualTo(MIN_VERSION); + assertThat(versionResponse.latestVersion()).isEqualTo(LATEST_VERSION); + } + + @Test + void 키가_다르면_버전을_저장할_때_예외가_발생한다() { + // given + VersionRequest versionRequest = 버전_요청(MIN_VERSION, LATEST_VERSION, "wrong key"); + + // when + assertThatThrownBy(() -> versionService.saveVersion(versionRequest)) + .isInstanceOf(IllegalArgumentException.class); + } +} diff --git a/src/test/java/com/coniverse/dangjang/fixture/VersionFixture.java b/src/test/java/com/coniverse/dangjang/fixture/VersionFixture.java new file mode 100644 index 00000000..f7b54072 --- /dev/null +++ b/src/test/java/com/coniverse/dangjang/fixture/VersionFixture.java @@ -0,0 +1,42 @@ +package com.coniverse.dangjang.fixture; + +import com.coniverse.dangjang.domain.version.dto.request.VersionRequest; +import com.coniverse.dangjang.domain.version.dto.response.VersionResponse; +import com.coniverse.dangjang.domain.version.entity.Version; + +/** + * @author TEO + * @since 1.3.0 + */ +public class VersionFixture { + public static Version 버전_엔티티() { + return getVersion("1.0.0", "1.0.0"); + } + + public static Version 버전_엔티티(String minVersion, String latestVersion) { + return getVersion(minVersion, latestVersion); + } + + private static Version getVersion(String minVersion, String latestVersion) { + return Version.builder() + .minVersion(minVersion) + .latestVersion(latestVersion) + .build(); + } + + public static VersionRequest 버전_요청() { + return getVersionRequest("1.0.0", "1.0.0", "1234"); + } + + public static VersionRequest 버전_요청(String minVersion, String latestVersion, String key) { + return getVersionRequest(minVersion, latestVersion, key); + } + + private static VersionRequest getVersionRequest(String minVersion, String latestVersion, String key) { + return new VersionRequest(minVersion, latestVersion, key); + } + + public static VersionResponse 버전_응답() { + return new VersionResponse<>("1.0.0", "1.0.0", null); + } +} diff --git a/src/test/java/com/coniverse/dangjang/support/ControllerTest.java b/src/test/java/com/coniverse/dangjang/support/ControllerTest.java index aa08052e..1b762aa6 100644 --- a/src/test/java/com/coniverse/dangjang/support/ControllerTest.java +++ b/src/test/java/com/coniverse/dangjang/support/ControllerTest.java @@ -25,8 +25,6 @@ import com.coniverse.dangjang.domain.healthmetric.service.HealthMetricChartSearchService; import com.coniverse.dangjang.domain.healthmetric.service.HealthMetricRegisterService; import com.coniverse.dangjang.domain.healthmetric.service.HealthMetricSearchService; -import com.coniverse.dangjang.domain.intro.controller.IntroController; -import com.coniverse.dangjang.domain.intro.service.IntroService; import com.coniverse.dangjang.domain.log.controller.AppLogController; import com.coniverse.dangjang.domain.log.service.LogService; import com.coniverse.dangjang.domain.notification.controller.NotificationController; @@ -39,6 +37,8 @@ import com.coniverse.dangjang.domain.user.service.MypageService; import com.coniverse.dangjang.domain.user.service.UserSignupService; import com.coniverse.dangjang.domain.user.service.UserWithdrawalService; +import com.coniverse.dangjang.domain.version.controller.VersionController; +import com.coniverse.dangjang.domain.version.service.VersionService; import com.fasterxml.jackson.databind.ObjectMapper; /** @@ -51,7 +51,7 @@ */ @WebMvcTest( controllers = { - IntroController.class, + VersionController.class, HealthMetricController.class, LoginController.class, SignupController.class, @@ -75,7 +75,7 @@ public class ControllerTest { @Autowired protected ObjectMapper objectMapper; @MockBean - private IntroService introService; + private VersionService versionService; @MockBean private OauthLoginService oAuthLoginService; @MockBean @@ -110,5 +110,4 @@ public class ControllerTest { private NotificationService notificationService; @MockBean private HealthMetricSearchService healthMetricSearchService; - } diff --git a/src/test/resources/config/application.yml b/src/test/resources/config/application.yml index d9714a2b..43d09e5a 100644 --- a/src/test/resources/config/application.yml +++ b/src/test/resources/config/application.yml @@ -47,9 +47,9 @@ jwt: exp: 3600000 fluentbit: - app-log-url: http://localhost:8888/app.log - server-log-url: http://localhost:8888/server.log - notification-url: http://localhost:8888/notification + app-log-url: http://localhost:1234/app.log + server-log-url: http://localhost:1234/server.log + notification-url: http://localhost:1234/notification cors: allowed-origins: "*" @@ -62,6 +62,9 @@ oauth: url: api: https://openapi.naver.com +version: + key: "1234" + logging: level: org: diff --git a/src/test/resources/schema.sql b/src/test/resources/schema.sql deleted file mode 100644 index 31ad0ca8..00000000 --- a/src/test/resources/schema.sql +++ /dev/null @@ -1,166 +0,0 @@ -DROP TABLE IF EXISTS DISEASE; -DROP TABLE IF EXISTS NOTIFICATION_SETTING; -DROP TABLE IF EXISTS NOTIFICATION; -DROP TABLE IF EXISTS USER_FCM_TOKEN; -DROP TABLE IF EXISTS NOTIFICATION_TYPE; -DROP TABLE IF EXISTS DEVICE; -DROP TABLE IF EXISTS HEALTH_METRIC; --- DROP TABLE IF EXISTS CODE; -DROP TABLE IF EXISTS DANGJANG_CLUB; -DROP TABLE IF EXISTS POINT_HISTORY; -DROP TABLE IF EXISTS PURCHASE_HISTORY; -DROP TABLE IF EXISTS POINT_PRODUCT; -DROP TABLE IF EXISTS USER_POINT; -DROP TABLE IF EXISTS USERS; -DROP TABLE IF EXISTS SHEDLOCK; - -CREATE TABLE `USERS` -( - `OAUTH_ID` varchar(50) NOT NULL, - `NICKNAME` varchar(8) NOT NULL, - `OAUTH_PROVIDER` varchar(10) NOT NULL, - `GENDER` varchar(1) NOT NULL, - `BIRTHDAY` date NOT NULL, - `ACTIVITY_AMOUNT` varchar(10) NOT NULL, - `HEIGHT` int NOT NULL, - `ROLE` varchar(10) NOT NULL, - `RECOMMENDED_CALORIE` int NOT NULL, - `STATUS` varchar(10) NOT NULL, - `CREATED_AT` datetime NOT NULL, - `UPDATED_AT` datetime NOT NULL, - `PROFILE_IMAGE_PATH` varchar(255), - `DIABETIC` boolean NOT NULL, - `DIABETES_YEAR` int, - `MEDICINE` boolean, - `INJECTION` boolean, - `ACCESSED_AT` date NOT NULL, - `HEALTH_CONNECT` varchar(50) NOT NULL, - `INACTIVATED_AT` date, - PRIMARY KEY (`OAUTH_ID`), - UNIQUE (`NICKNAME`) -); - -CREATE TABLE `DANGJANG_CLUB` -( - `OAUTH_ID` varchar(50) NOT NULL, - `START_DATE` date NOT NULL, - PRIMARY KEY (`OAUTH_ID`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) -); - -CREATE TABLE `HEALTH_METRIC` -( - `OAUTH_ID` varchar(50) NOT NULL, - `CREATED_AT` date NOT NULL, - `TYPE` varchar(20) NOT NULL, - `GROUP_CODE` varchar(20) NOT NULL, - `UNIT` varchar(20) NOT NULL, - PRIMARY KEY (`OAUTH_ID`, `CREATED_AT`, `TYPE`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) -); - -CREATE TABLE `DEVICE` -( - `OAUTH_ID` varchar(50) NOT NULL, - `CODE` varchar(20) NOT NULL, - PRIMARY KEY (`OAUTH_ID`, `CODE`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) -); - -CREATE TABLE `NOTIFICATION_SETTING` -( - `OAUTH_ID` varchar(50) NOT NULL, - `CODE` varchar(20) NOT NULL, - PRIMARY KEY (`OAUTH_ID`, `CODE`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) -); - -CREATE TABLE `DISEASE` -( - `OAUTH_ID` varchar(50) NOT NULL, - `CODE` varchar(20) NOT NULL, - PRIMARY KEY (`OAUTH_ID`, `CODE`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) -); - -CREATE TABLE `POINT_PRODUCT` -( - `PRODUCT_NAME` varchar(255) NOT NULL, - `POINT` int, - `TYPE` varchar(20) NOT NULL, - `DESCRIPTION` varchar(255), - PRIMARY KEY (`PRODUCT_NAME`) -); - -CREATE TABLE `POINT_HISTORY` -( - `OAUTH_ID` varchar(50) NOT NULL, - `CREATED_AT` dateTime NOT NULL, - `PRODUCT_NAME` varchar(255) NOT NULL, - `CHANGE_POINT` int, - `BALANCE_POINT` int, - PRIMARY KEY (`OAUTH_ID`, `CREATED_AT`, `PRODUCT_NAME`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`), - FOREIGN KEY (`PRODUCT_NAME`) REFERENCES POINT_PRODUCT (`PRODUCT_NAME`) -); - -CREATE TABLE `USER_POINT` -( - `OAUTH_ID` varchar(50) NOT NULL, - `POINT` int, - `VERSION` bigint, - PRIMARY KEY (`OAUTH_ID`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) -); - -CREATE TABLE `PURCHASE_HISTORY` -( - `OAUTH_ID` varchar(50) NOT NULL, - `PRODUCT_NAME` varchar(255) NOT NULL, - `CREATED_AT` dateTime NOT NULL, - `PHONE` varchar(20) NOT NULL, - `NAME` varchar(20) NOT NULL, - `COMMENT` varchar(255), - `COMPLETED` boolean, - PRIMARY KEY (`OAUTH_ID`, `PRODUCT_NAME`, `CREATED_AT`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`), - FOREIGN KEY (`PRODUCT_NAME`) REFERENCES POINT_PRODUCT (`PRODUCT_NAME`) -); - - - -CREATE TABLE `USER_FCM_TOKEN` -( - `OAUTH_ID` varchar(50) NOT NULL, - `FCM_TOKEN` varchar(255) NOT NULL, - `CREATED_AT` dateTime NOT NULL, - PRIMARY KEY (`FCM_TOKEN`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`) -); - -CREATE TABLE `NOTIFICATION_TYPE` -( - `TYPE` varchar(20) NOT NULL, - PRIMARY KEY (`TYPE`) -); - -CREATE TABLE `NOTIFICATION` -( - `NOTIFICATION_ID` INT NOT NULL AUTO_INCREMENT, - `TITLE` varchar(255) NOT NULL, - `CONTENT` varchar(255) NOT NULL, - `OAUTH_ID` varchar(50) NOT NULL, - `TYPE` varchar(20) NOT NULL, - `CREATED_AT` dateTime NOT NULL, - `IS_READ` boolean NOT NULL, - PRIMARY KEY (`NOTIFICATION_ID`), - FOREIGN KEY (`OAUTH_ID`) REFERENCES USERS (`OAUTH_ID`), - FOREIGN KEY (`TYPE`) REFERENCES NOTIFICATION_TYPE (`TYPE`) -); -CREATE TABLE `SHEDLOCK` ( - `NAME` VARCHAR(64), - `LOCK_UNTIL` TIMESTAMP(3) NULL, - `LOCKED_AT` TIMESTAMP(3) NULL, - `LOCKED_BY` VARCHAR(255), - PRIMARY KEY (NAME) -) \ No newline at end of file diff --git a/src/test/resources/schema.sql b/src/test/resources/schema.sql new file mode 120000 index 00000000..7e7f2325 --- /dev/null +++ b/src/test/resources/schema.sql @@ -0,0 +1 @@ +../../main/resources/schema-dev.sql \ No newline at end of file