diff --git a/build.gradle b/build.gradle index dee30614..cff55d8a 100644 --- a/build.gradle +++ b/build.gradle @@ -77,18 +77,18 @@ dependencies { // Persistence Layer implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-data-redis' - implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.4' + implementation 'io.hypersistence:hypersistence-utils-hibernate-63:3.7.5' implementation "io.github.openfeign.querydsl:querydsl-jpa-spring:${queryDslVersion}" implementation "io.github.openfeign.querydsl:querydsl-collections:${queryDslVersion}" implementation 'com.github.gavlyukovskiy:p6spy-spring-boot-starter:1.9.1' implementation 'org.flywaydb:flyway-core' - implementation 'org.flywaydb:flyway-database-postgresql:10.11.1' + implementation 'org.flywaydb:flyway-database-postgresql:10.12.0' implementation 'org.postgresql:postgresql' // development implementation 'org.springframework.retry:spring-retry' implementation 'org.springframework:spring-aspects' - implementation 'com.github.f4b6a3:ulid-creator:5.2.2' + implementation 'com.github.f4b6a3:ulid-creator:5.2.3' implementation 'org.springframework.boot:spring-boot-starter-actuator' // @see https://github.com/awspring/spring-cloud-aws/issues/657 // developmentOnly 'org.springframework.boot:spring-boot-devtools' @@ -102,7 +102,7 @@ dependencies { implementation 'com.amazonaws.secretsmanager:aws-secretsmanager-jdbc:2.0.2' implementation 'software.amazon.awssdk:kms' implementation 'com.amazonaws:aws-encryption-sdk-java:3.0.0' - implementation 'software.amazon.cryptography:aws-cryptographic-material-providers:1.2.0' + implementation 'software.amazon.cryptography:aws-cryptographic-material-providers:1.3.0' implementation 'org.springframework.boot:spring-boot-starter-quartz' implementation 'org.springframework.cloud:spring-cloud-starter-openfeign:4.1.1' implementation 'com.slack.api:slack-app-backend:1.39.0' @@ -149,7 +149,7 @@ dependencies { testImplementation 'org.springframework.boot:spring-boot-starter-test' testImplementation 'org.springframework.restdocs:spring-restdocs-mockmvc' testImplementation 'org.springframework.security:spring-security-test' - testImplementation 'com.c4-soft.springaddons:spring-addons-oauth2-test:7.6.12' + testImplementation 'com.c4-soft.springaddons:spring-addons-oauth2-test:7.6.13' testImplementation 'org.springframework.kafka:spring-kafka-test' testImplementation 'org.apache.commons:commons-lang3:3.14.0' testImplementation 'com.tngtech.archunit:archunit-junit5:1.2.2' // Architecture Rule Test @@ -157,7 +157,7 @@ dependencies { testImplementation 'org.quickperf:quick-perf-junit5:1.1.0' // Performance Test testImplementation 'io.karatelabs:karate-junit5:1.5.0.RC3' // Both Test Pyramid testImplementation 'com.icegreen:greenmail-junit5:2.0.1' - testImplementation 'io.awspring.cloud:spring-cloud-aws-starter-secrets-manager:3.1.0' + testImplementation 'io.awspring.cloud:spring-cloud-aws-starter-secrets-manager' testImplementation 'org.awaitility:awaitility:4.2.1' testImplementation 'org.awaitility:awaitility-proxy:3.1.6' diff --git a/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/AccountJpaEntity.java b/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/AccountJpaEntity.java index 34fbdf71..8127d428 100644 --- a/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/AccountJpaEntity.java +++ b/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/AccountJpaEntity.java @@ -3,6 +3,7 @@ import com.tune_fun.v1.account.adapter.output.persistence.device.DeviceJpaEntity; import com.tune_fun.v1.account.adapter.output.persistence.oauth2.OAuth2AccountJpaEntity; import com.tune_fun.v1.common.entity.BaseEntity; +import io.hypersistence.utils.hibernate.id.Tsid; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -28,9 +29,9 @@ public class AccountJpaEntity extends BaseEntity implements UserDetails { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Tsid @Column(name = "id", nullable = false, updatable = false) - @Comment("Sequence") + @Comment("TSID") private Long id; @Size(max = 255) diff --git a/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/device/DeviceJpaEntity.java b/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/device/DeviceJpaEntity.java index 51123bcb..db509e4d 100644 --- a/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/device/DeviceJpaEntity.java +++ b/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/device/DeviceJpaEntity.java @@ -2,6 +2,7 @@ import com.tune_fun.v1.account.adapter.output.persistence.AccountJpaEntity; import com.tune_fun.v1.common.entity.BaseEntity; +import io.hypersistence.utils.hibernate.id.Tsid; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -23,9 +24,9 @@ public class DeviceJpaEntity extends BaseEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Tsid @Column(name = "id", nullable = false, updatable = false) - @Comment("Sequence") + @Comment("TSID") private Long id; @Size(max = 255) @@ -65,5 +66,5 @@ public final boolean equals(Object object) { public final int hashCode() { return this instanceof HibernateProxy ? ((HibernateProxy) this).getHibernateLazyInitializer().getPersistentClass().hashCode() : getClass().hashCode(); } - + } diff --git a/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/oauth2/OAuth2AccountJpaEntity.java b/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/oauth2/OAuth2AccountJpaEntity.java index ea656743..d127bfd5 100644 --- a/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/oauth2/OAuth2AccountJpaEntity.java +++ b/src/main/java/com/tune_fun/v1/account/adapter/output/persistence/oauth2/OAuth2AccountJpaEntity.java @@ -2,6 +2,7 @@ import com.tune_fun.v1.account.adapter.output.persistence.AccountJpaEntity; import com.tune_fun.v1.common.entity.BaseEntity; +import io.hypersistence.utils.hibernate.id.Tsid; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -24,9 +25,9 @@ public class OAuth2AccountJpaEntity extends BaseEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Tsid @Column(name = "id", nullable = false, updatable = false) - @Comment("Sequence") + @Comment("TSID") private Long id; @Size(max = 255) diff --git a/src/main/java/com/tune_fun/v1/common/response/BasePayload.java b/src/main/java/com/tune_fun/v1/common/response/BasePayload.java index c4e82979..95c4f8ef 100644 --- a/src/main/java/com/tune_fun/v1/common/response/BasePayload.java +++ b/src/main/java/com/tune_fun/v1/common/response/BasePayload.java @@ -1,10 +1,8 @@ package com.tune_fun.v1.common.response; -import com.fasterxml.jackson.annotation.JsonInclude; import com.fasterxml.jackson.databind.PropertyNamingStrategies; import com.fasterxml.jackson.databind.annotation.JsonNaming; @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -@JsonInclude(JsonInclude.Include.NON_NULL) public interface BasePayload { } diff --git a/src/main/java/com/tune_fun/v1/common/util/StringUtil.java b/src/main/java/com/tune_fun/v1/common/util/StringUtil.java index 12a9366b..87d1ea2f 100644 --- a/src/main/java/com/tune_fun/v1/common/util/StringUtil.java +++ b/src/main/java/com/tune_fun/v1/common/util/StringUtil.java @@ -1,5 +1,6 @@ package com.tune_fun.v1.common.util; +import com.github.f4b6a3.ulid.UlidCreator; import com.tune_fun.v1.common.constant.Constants; import lombok.experimental.UtilityClass; import org.apache.commons.lang3.RandomStringUtils; @@ -78,6 +79,10 @@ public static String uuid() { return UUID.randomUUID().toString(); } + public static String ulid() { + return UlidCreator.getMonotonicUlid().toString(); + } + public boolean hasText(String text) { return StringUtils.hasText(text); } diff --git a/src/main/java/com/tune_fun/v1/external/aws/s3/S3Template.java b/src/main/java/com/tune_fun/v1/external/aws/s3/S3Template.java index d89aa604..2f3e7596 100644 --- a/src/main/java/com/tune_fun/v1/external/aws/s3/S3Template.java +++ b/src/main/java/com/tune_fun/v1/external/aws/s3/S3Template.java @@ -74,8 +74,8 @@ public ResponseBytes getObject(String s3BucketName, String ke } @NotNull - public String getKey(String rootPath, MultipartFile multipartFile) throws NoSuchAlgorithmException { - String generatedFileName = StringUtil.randomAlphanumeric(40); + public String getKey(String rootPath, MultipartFile multipartFile) { + String generatedFileName = StringUtil.ulid(); log.info("generated file name is {}", generatedFileName); String extractedExtension = FilenameUtils.getExtension(multipartFile.getOriginalFilename()); diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperController.java b/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperController.java index adfc8e87..b76ad32f 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperController.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperController.java @@ -10,6 +10,7 @@ import com.tune_fun.v1.common.response.ResponseMapper; import com.tune_fun.v1.vote.application.port.input.command.VotePaperCommands; import com.tune_fun.v1.vote.application.port.input.usecase.*; +import com.tune_fun.v1.vote.domain.value.FullVotePaper; import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; import jakarta.validation.Valid; import jakarta.validation.constraints.NotNull; @@ -29,6 +30,8 @@ public class VotePaperController { private final ScrollVotePaperUseCase scrollVotePaperUseCase; + private final GetVotePaperUseCase getVotePaperUseCase; + private final RegisterVotePaperUseCase registerVotePaperUseCase; private final RegisterVoteChoiceUseCase registerVoteChoiceUseCase; @@ -45,6 +48,13 @@ public ResponseEntity> getVotePapers(@RequestP return responseMapper.ok(MessageCode.SUCCESS, new ScrollVotePaperResponse(scrollableVotePapers)); } + @GetMapping(value = Uris.VOTE_PAPER_ROOT + "/{votePaperId}") + public ResponseEntity> getVotePaper(@PathVariable("votePaperId") @NotNull(message = "{vote.paper.id.not_null}") final Long votePaperId, + @CurrentUser final User user) { + FullVotePaper votePaper = getVotePaperUseCase.getVotePaper(votePaperId); + return responseMapper.ok(MessageCode.SUCCESS, votePaper); + } + // TODO : Follower 로직 구현 후 테스트 재진행 예정 @PreAuthorize("hasRole('ARTIST')") @PostMapping(value = Uris.VOTE_PAPER_ROOT) diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteChoiceJpaEntity.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteChoiceJpaEntity.java index bad9d097..e1062280 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteChoiceJpaEntity.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteChoiceJpaEntity.java @@ -1,6 +1,7 @@ package com.tune_fun.v1.vote.adapter.output.persistence; import com.tune_fun.v1.common.entity.BaseEntity; +import io.hypersistence.utils.hibernate.id.Tsid; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -29,9 +30,9 @@ public class VoteChoiceJpaEntity extends BaseEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Tsid @Column(name = "id", nullable = false, updatable = false) - @Comment("Sequence") + @Comment("TSID") private Long id; @Size(max = 255) diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteJpaEntity.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteJpaEntity.java index 50e40802..16de15c7 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteJpaEntity.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VoteJpaEntity.java @@ -2,6 +2,7 @@ import com.tune_fun.v1.account.adapter.output.persistence.AccountJpaEntity; import com.tune_fun.v1.common.entity.BaseEntity; +import io.hypersistence.utils.hibernate.id.Tsid; import jakarta.persistence.*; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Size; @@ -23,9 +24,9 @@ public class VoteJpaEntity extends BaseEntity { @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) + @Tsid @Column(name = "id", nullable = false, updatable = false) - @Comment("Sequence") + @Comment("TSID") private Long id; @Size(max = 255) diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperJpaEntity.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperJpaEntity.java index c97a6571..1f38050b 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperJpaEntity.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperJpaEntity.java @@ -24,6 +24,7 @@ @Table(name = "vote_paper") public class VotePaperJpaEntity extends BaseEntity { + // TODO : TSID Generation으로 변경 필요하나 DummyService.initVotePaperBatch()에서 사용하므로 일단 미적용 @Id @GeneratedValue(strategy = GenerationType.IDENTITY) @Column(name = "id", nullable = false, updatable = false) diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperMapper.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperMapper.java index 2ffebe4c..5c54e548 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperMapper.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperMapper.java @@ -6,7 +6,6 @@ import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; import com.tune_fun.v1.vote.domain.value.ScrollableVotePaper; -import com.tune_fun.v1.vote.domain.value.VotePaperOption; import org.mapstruct.Mapper; import org.mapstruct.Mapping; import org.mapstruct.MappingTarget; diff --git a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperRepository.java b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperRepository.java index 06b9af71..affc3c87 100644 --- a/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperRepository.java +++ b/src/main/java/com/tune_fun/v1/vote/adapter/output/persistence/VotePaperRepository.java @@ -14,6 +14,7 @@ public interface VotePaperRepository extends JpaRepository findByVoteEndAtAfterAndAuthorUsernameAndEnabledTrue(LocalDateTime voteEndAt, String author); + @EntityGraph(attributePaths = {"author"}) Optional findByVoteEndAtAfterAndIdAndEnabledTrue(LocalDateTime voteEndAt, Long id); Optional findByVoteEndAtBeforeAndIdAndEnabledTrue(LocalDateTime voteEndAt, Long id); diff --git a/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/GetVotePaperUseCase.java b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/GetVotePaperUseCase.java new file mode 100644 index 00000000..3f57f91e --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/application/port/input/usecase/GetVotePaperUseCase.java @@ -0,0 +1,10 @@ +package com.tune_fun.v1.vote.application.port.input.usecase; + +import com.tune_fun.v1.vote.domain.value.FullVotePaper; + +@FunctionalInterface +public interface GetVotePaperUseCase { + + FullVotePaper getVotePaper(final Long votePaperId); + +} diff --git a/src/main/java/com/tune_fun/v1/vote/application/service/GetVotePaperService.java b/src/main/java/com/tune_fun/v1/vote/application/service/GetVotePaperService.java new file mode 100644 index 00000000..a3162139 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/application/service/GetVotePaperService.java @@ -0,0 +1,37 @@ +package com.tune_fun.v1.vote.application.service; + +import com.tune_fun.v1.common.exception.CommonApplicationException; +import com.tune_fun.v1.common.hexagon.UseCase; +import com.tune_fun.v1.vote.application.port.input.usecase.GetVotePaperUseCase; +import com.tune_fun.v1.vote.application.port.output.LoadVoteChoicePort; +import com.tune_fun.v1.vote.application.port.output.LoadVotePaperPort; +import com.tune_fun.v1.vote.domain.value.FullVotePaper; +import com.tune_fun.v1.vote.domain.value.RegisteredVoteChoice; +import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.util.List; + +import static com.tune_fun.v1.common.response.MessageCode.VOTE_PAPER_NOT_FOUND; + +@Service +@UseCase +@RequiredArgsConstructor +public class GetVotePaperService implements GetVotePaperUseCase { + + private final LoadVotePaperPort loadVotePaperPort; + private final LoadVoteChoicePort loadVoteChoicePort; + + private final VoteValueMapper voteValueMapper; + + + @Override + public FullVotePaper getVotePaper(final Long votePaperId) { + RegisteredVotePaper registeredVotePaper = loadVotePaperPort.loadRegisteredVotePaper(votePaperId) + .orElseThrow(() -> new CommonApplicationException(VOTE_PAPER_NOT_FOUND)); + List registeredVoteChoices = loadVoteChoicePort.loadRegisteredVoteChoice(votePaperId); + + return voteValueMapper.fullVotePaper(registeredVotePaper, registeredVoteChoices); + } +} diff --git a/src/main/java/com/tune_fun/v1/vote/application/service/VoteValueMapper.java b/src/main/java/com/tune_fun/v1/vote/application/service/VoteValueMapper.java new file mode 100644 index 00000000..a83691d8 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/application/service/VoteValueMapper.java @@ -0,0 +1,31 @@ +package com.tune_fun.v1.vote.application.service; + +import com.tune_fun.v1.common.config.BaseMapperConfig; +import com.tune_fun.v1.vote.domain.value.FullVotePaper; +import com.tune_fun.v1.vote.domain.value.RegisteredVoteChoice; +import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; + +import java.util.List; + +@Mapper(config = BaseMapperConfig.class) +public abstract class VoteValueMapper { + + @Mapping(target = "id", source = "votePaper.id") + @Mapping(target = "uuid", source = "votePaper.uuid") + @Mapping(target = "author", source = "votePaper.author") + @Mapping(target = "authorUsername", source = "votePaper.authorUsername") + @Mapping(target = "title", source = "votePaper.title") + @Mapping(target = "content", source = "votePaper.content") + @Mapping(target = "option", source = "votePaper.option") + @Mapping(target = "videoUrl", source = "votePaper.videoUrl") + @Mapping(target = "voteStartAt", source = "votePaper.voteStartAt") + @Mapping(target = "voteEndAt", source = "votePaper.voteEndAt") + @Mapping(target = "deliveryAt", source = "votePaper.deliveryAt") + @Mapping(target = "createdAt", source = "votePaper.createdAt") + @Mapping(target = "updatedAt", source = "votePaper.updatedAt") + @Mapping(target = "choices", source = "voteChoices") + public abstract FullVotePaper fullVotePaper(final RegisteredVotePaper votePaper, final List voteChoices); + +} diff --git a/src/main/java/com/tune_fun/v1/vote/domain/value/Choice.java b/src/main/java/com/tune_fun/v1/vote/domain/value/Choice.java new file mode 100644 index 00000000..18c2f751 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/domain/value/Choice.java @@ -0,0 +1,8 @@ +package com.tune_fun.v1.vote.domain.value; + +public record Choice( + Long id, + String music, + String artistName +) { +} diff --git a/src/main/java/com/tune_fun/v1/vote/domain/value/FullVotePaper.java b/src/main/java/com/tune_fun/v1/vote/domain/value/FullVotePaper.java new file mode 100644 index 00000000..9b6fdaf5 --- /dev/null +++ b/src/main/java/com/tune_fun/v1/vote/domain/value/FullVotePaper.java @@ -0,0 +1,37 @@ +package com.tune_fun.v1.vote.domain.value; + +import com.fasterxml.jackson.annotation.JsonFormat; +import com.tune_fun.v1.common.response.BasePayload; + +import java.time.LocalDateTime; +import java.util.Set; + +public record FullVotePaper( + Long id, + String uuid, + String author, + String authorUsername, + String title, + String content, + VotePaperOption option, + String videoUrl, + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime voteStartAt, + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime voteEndAt, + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime deliveryAt, + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime createdAt, + + @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss") + LocalDateTime updatedAt, + + Set choices +) implements BasePayload { + +} diff --git a/src/main/java/com/tune_fun/v1/vote/domain/value/RegisteredVoteChoice.java b/src/main/java/com/tune_fun/v1/vote/domain/value/RegisteredVoteChoice.java index d182d91d..3bdd3e11 100644 --- a/src/main/java/com/tune_fun/v1/vote/domain/value/RegisteredVoteChoice.java +++ b/src/main/java/com/tune_fun/v1/vote/domain/value/RegisteredVoteChoice.java @@ -1,5 +1,7 @@ package com.tune_fun.v1.vote.domain.value; +import com.tune_fun.v1.common.response.BasePayload; + import java.util.Set; public record RegisteredVoteChoice( @@ -10,5 +12,5 @@ public record RegisteredVoteChoice( Set genres, String releaseDate, Integer durationMs -) { +) implements BasePayload { } diff --git a/src/test/java/com/tune_fun/v1/dummy/DummyService.java b/src/test/java/com/tune_fun/v1/dummy/DummyService.java index d3ef2d3f..bd757eb2 100644 --- a/src/test/java/com/tune_fun/v1/dummy/DummyService.java +++ b/src/test/java/com/tune_fun/v1/dummy/DummyService.java @@ -31,6 +31,8 @@ import com.tune_fun.v1.vote.domain.behavior.SaveVotePaper; import com.tune_fun.v1.vote.domain.value.RegisteredVotePaper; import com.tune_fun.v1.vote.domain.value.VotePaperOption; +import io.hypersistence.tsid.TSID; +import io.hypersistence.utils.hibernate.id.TsidGenerator; import lombok.Getter; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; diff --git a/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperControllerIT.java b/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperControllerIT.java index 1aa24793..ea272a8b 100644 --- a/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperControllerIT.java +++ b/src/test/java/com/tune_fun/v1/vote/adapter/input/rest/VotePaperControllerIT.java @@ -164,6 +164,89 @@ void getVotePapersSuccess() throws Exception { @Transactional @Test @Order(2) + @DisplayName("투표 게시물 상세 조회, 성공") + void getVotePaperSuccess() throws Exception { + dummyService.initAndLogin(); + dummyService.initArtistAndLogin(); + + dummyService.initVotePaper(); + dummyService.registerVote(); + + Long votePaperId = dummyService.getDefaultVotePaper().getId(); + String accessToken = dummyService.getDefaultAccessToken(); + + ParameterDescriptor pathParameter = parameterWithName("votePaperId").description("투표 게시물 ID").attributes(constraint("NOT NULL")); + + FieldDescriptor[] responseDescriptors = ArrayUtils.addAll(baseResponseFields, + fieldWithPath("data.id").description("투표 게시물 ID").attributes(constraint("NOT NULL")), + fieldWithPath("data.uuid").description("투표 게시물 UUID").attributes(constraint("NOT NULL")), + fieldWithPath("data.author").description("투표 게시물 작성자 닉네임").attributes(constraint("NOT NULL")), + fieldWithPath("data.author_username").description("투표 게시물 작성자 아이디").attributes(constraint("NOT NULL")), + fieldWithPath("data.title").description("투표 게시물 제목").attributes(constraint("NOT NULL")), + fieldWithPath("data.content").description("투표 게시물 내용").attributes(constraint("NOT NULL")), + fieldWithPath("data.option").description("투표 종류").attributes(constraint("NOT NULL")), + fieldWithPath("data.video_url").description("투표 게시물 영상 URL").attributes(constraint("NULL or NOT NULL")), + fieldWithPath("data.vote_start_at").description("투표 시작 시간").attributes(constraint("NOT NULL")), + fieldWithPath("data.vote_end_at").description("투표 종료 시간").attributes(constraint("NOT NULL")), + fieldWithPath("data.delivery_at").description("투표 게시물 영상 제공일").attributes(constraint("NULL or NOT NULL")), + fieldWithPath("data.created_at").description("투표 게시물 생성 시간").attributes(constraint("NOT NULL")), + fieldWithPath("data.updated_at").description("투표 게시물 수정 시간").attributes(constraint("NOT NULL")), + fieldWithPath("data.choices").description("투표 선택지 목록").attributes(constraint("NOT EMPTY")), + fieldWithPath("data.choices[].id").description("아이디").attributes(constraint("NOT NULL")), + fieldWithPath("data.choices[].music").description("노래명").attributes(constraint("NOT BLANK")), + fieldWithPath("data.choices[].artist_name").description("아티스트명").attributes(constraint("NOT BLANK")), + fieldWithPath("data.choices[].genres[]").description("장르").attributes(constraint("NOT EMPTY")), + fieldWithPath("data.choices[].duration_ms").description("재생 시간(ms)").attributes(constraint("NOT NULL & POSITIVE")), + fieldWithPath("data.choices[].release_date").description("발매일").attributes(constraint("NOT BLANK")), + fieldWithPath("data.choices[].vote_paper_id").description("투표 게시물 ID").attributes(constraint("NOT NULL")) + ); + + mockMvc.perform( + get(Uris.VOTE_PAPER_ROOT + "/{votePaperId}", votePaperId) + .header(AUTHORIZATION, bearerToken(accessToken)) + ) + .andExpectAll(baseAssertion(MessageCode.SUCCESS)) + .andExpect(jsonPath("data.id", notNullValue())) + .andExpect(jsonPath("data.uuid", notNullValue())) + .andExpect(jsonPath("data.author", notNullValue())) + .andExpect(jsonPath("data.author_username", notNullValue())) + .andExpect(jsonPath("data.title", notNullValue())) + .andExpect(jsonPath("data.content", notNullValue())) + .andExpect(jsonPath("data.option", notNullValue())) + .andExpect(jsonPath("data.video_url", nullValue())) + .andExpect(jsonPath("data.vote_start_at", notNullValue())) + .andExpect(jsonPath("data.vote_end_at", notNullValue())) + .andExpect(jsonPath("data.delivery_at", nullValue())) + .andExpect(jsonPath("data.created_at", notNullValue())) + .andExpect(jsonPath("data.updated_at", notNullValue())) + .andExpect(jsonPath("data.choices", notNullValue())) + .andExpect(jsonPath("data.choices[*].id", notNullValue())) + .andExpect(jsonPath("data.choices[*].music", notNullValue())) + .andExpect(jsonPath("data.choices[*].artist_name", notNullValue())) + .andExpect(jsonPath("data.choices[*].genres[*]", notNullValue())) + .andExpect(jsonPath("data.choices[*].duration_ms", notNullValue())) + .andExpect(jsonPath("data.choices[*].release_date", notNullValue())) + .andExpect(jsonPath("data.choices[*].vote_paper_id", notNullValue())) + .andDo( + restDocs.document( + requestHeaders(authorizationHeader), + pathParameters(pathParameter), + responseFields(responseDescriptors), + resource( + builder(). + description("투표 게시물 상세 조회"). + pathParameters(pathParameter). + responseFields(responseDescriptors) + .build() + ) + ) + ); + + } + + @Transactional + @Test + @Order(3) @DisplayName("투표 게시물 등록, 성공") void registerVotePaperSuccess() throws Exception { dummyService.initAndLogin(); @@ -249,7 +332,7 @@ void registerVotePaperSuccess() throws Exception { log.info("readMusic: {}", readMusic); assertThat(readMusic, hasItems("KNOCK (With 박문치)", "Orange, You're Not a Joke to Me!")); - List readArtistName = parsedChoices.read("$[*].artistName"); + List readArtistName = parsedChoices.read("$[*].artist_name"); log.info("readArtistName: {}", readArtistName); assertThat(readArtistName, hasItems("권진아", "스텔라장 (Stella Jang)")); @@ -260,7 +343,7 @@ void registerVotePaperSuccess() throws Exception { @Transactional @Test - @Order(3) + @Order(4) @DisplayName("투표 선택지 등록, 성공") void registerVotePaperChoiceSuccess() throws Exception { dummyService.initAccount(); @@ -314,26 +397,26 @@ void registerVotePaperChoiceSuccess() throws Exception { assertEquals(registeredVoteChoices.size(), 3); DocumentContext parsedVoteChoices = JsonPath.parse(toJson(registeredVoteChoices)); - + List readMusic = parsedVoteChoices.read("$[*].music"); assertThat(readMusic, hasItem("이별이란 어느 별에 (Feat. 조광일)")); - List readArtistName = parsedVoteChoices.read("$[*].artistName"); + List readArtistName = parsedVoteChoices.read("$[*].artist_name"); assertThat(readArtistName, hasItem("HYNN (박혜원)")); List> readGenres = parsedVoteChoices.read("$[*].genres"); assertThat(readGenres, hasItem(singletonList("Ballad"))); - List readDurationMs = parsedVoteChoices.read("$[*].durationMs"); + List readDurationMs = parsedVoteChoices.read("$[*].duration_ms"); assertThat(readDurationMs, hasItem(231_000)); - List readReleaseDate = parsedVoteChoices.read("$[*].releaseDate"); + List readReleaseDate = parsedVoteChoices.read("$[*].release_date"); assertThat(readReleaseDate, hasItem("2022-02-11")); } @Transactional @Test - @Order(4) + @Order(5) @DisplayName("투표 게시물 영상 제공일 등록, 성공") void updateDeliveryDateSuccess() throws Exception { dummyService.initAndLogin(); @@ -389,7 +472,7 @@ void updateDeliveryDateSuccess() throws Exception { @Transactional @Test - @Order(5) + @Order(6) @DisplayName("투표 게시물 영상 업로드, 성공") void updateVideoUrlSuccess() throws Exception { dummyService.initAndLogin(); @@ -446,7 +529,7 @@ void updateVideoUrlSuccess() throws Exception { @Transactional @Test - @Order(6) + @Order(7) @DisplayName("투표 게시물 삭제, 성공") void deleteVotePaperSuccess() throws Exception { dummyService.initAndLogin();